问题
This is a follow up to this question. I have a firebase function which is supposed to take an OTP, validate it and then change a user's password based on whether it is correct or not (for some reason I'm not able to use firebase's inbuild password reset functionality). Following is my function:
exports.resetPassword = functions.https.onCall((data, context) => {
return new Promise((resolve, reject) => {
if(data.sesId && data.otp){
admin.firestore().collection('verification').doc(data.sesId).get().then(verSnp => {
if(verSnp.data().attempt != 'verified'){
var now = new Date().getTime()
if(verSnp.data().expiring > now){
if(data.email == verSnp.data().email){
if(verSnp.data().attempt > 0){
if(data.otp == verSnp.data().otp){
admin.auth().getUserByEmail(data.email).then(user => {
admin.auth().updateUser(user.uid,{
password: data.password
}).then(() => {
admin.firestore().collection('verification').doc(data.sesId).update({
attempt: 'verified'
}).then(() => {
Promise.resolve()
}).catch(() => {
throw new Error('Error updating the database.')
})
}).catch(() => {
throw new Error('Error updating the password. Please try again.')
})
}).catch(() => {
throw new Error('Incorrect email. How did you get here?')
})
} else {
var redAttempt = verSnp.data().attempt - 1
admin.firestore().collection('verification').doc(data.sesId).update({
attempt: redAttempt
}).then(() => {
throw new Error(`Incorrect OTP. You have ${redAttempt} attempts remaining.`)
}).catch(() => {
throw new Error('Wrong OTP, try again.')
})
}
} else {
throw new Error('Incorrect OTP. You have exhausted your attempts. Please request a new OTP.')
}
} else {
throw new Error('Incorrect email. How did you get here?')
}
} else {
throw new Error('OTP is expired. Please request a new OTP.')
}
} else {
throw new Error('OTP is invalid. Please request a new OTP.')
}
}).catch(() => {
throw new Error('Invalid session id. Please request the OTP through Forgot Password.')
})
} else {
throw new Error('Enter OTP')
}
})
})
When I run the function, it gets executed, because I can see it in the console statements, but I'm getting following error on my client side.
Access to fetch at 'https://us-central1-project-name.cloudfunctions.net/functionName' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
and when I log the response received from the function, it shows {"code":"internal"}
.
What is the cors package? How do I solve this problem?
Part 2 (not related)
Also, on lines 11 and 12 of my function, I'm using
admin.auth().getUserByEmail(data.email).then(user => {
admin.auth().updateUser(user.uid, {password: data.password})
})
Is this correct?
For part 1 I referred to this question, but it has no answers.
回答1:
Have a look at the documentation for Callable Cloud Functions:
- You don't need to encapsulate it in
return new Promise((resolve, reject) => {})
; - You need to return data that can be JSON encoded;
- You need to manage the errors correctly, by by throwing (or returning a Promise rejected with) an instance of
functions.https.HttpsError
; - You need to correctly chain all the promises returned by the asynchronous methods.
I've tried below to re-organized your code in the lights of the points above, but since your business logic is complex I cannot test it and there might be other approaches to manage all the cases... Up to you to "polish" this first attempt! Hoping it will help.
exports.resetPassword = functions.https.onCall((data, context) => {
if(data.sesId && data.otp){
let dataOptCorresponds = true;
return admin.firestore().collection('verification').doc(data.sesId).get()
.then(verSnp => {
if(verSnp.data().attempt != 'verified'){
var now = new Date().getTime()
if(verSnp.data().expiring > now){
if(data.email == verSnp.data().email){
if(verSnp.data().attempt > 0){
if(data.otp == verSnp.data().otp){
return admin.auth().getUserByEmail(data.email);
} else {
dataOptCorresponds = false;
var redAttempt = verSnp.data().attempt - 1
return admin.firestore().collection('verification').doc(data.sesId).update({
attempt: redAttempt
})
}
} else {
throw new Error('Incorrect OTP. You have exhausted your attempts. Please request a new OTP.')
}
} else {
throw new Error('Incorrect email. How did you get here?')
}
} else {
throw new Error('OTP is expired. Please request a new OTP.')
}
} else {
throw new Error('OTP is invalid. Please request a new OTP.')
}
})
.then(user => {
if(dataOptCorresponds) {
return admin.auth().updateUser(user.uid,{
password: data.password
})
} else {
throw new Error(`Incorrect OTP. You have xxxx attempts remaining.`)
}
})
.then(() => {
return admin.firestore().collection('verification').doc(data.sesId).update({
attempt: 'verified'
})
.then(() => {
return {result: "success"}
})
.catch(error => {
throw new functions.https.HttpsError('internal', error.message);
})
} else {
throw new functions.https.HttpsError('invalid-argument', 'Enter OTP');
}
})
UPDATE following Bergi's comment below:
If you want to be able to differentiate the kind of errors returned to the front-end (in particular sending back an invalid-argument
HttpsError
if the OTP is incorrect, invalid or expired or if the email is incorrect) you may use a second argument in the then()
method.
exports.resetPassword = functions.https.onCall((data, context) => {
if(data.sesId && data.otp){
let dataOptCorresponds = true;
return admin.firestore().collection('verification').doc(data.sesId).get()
.then(
verSnp => {
if(verSnp.data().attempt != 'verified'){
var now = new Date().getTime()
if(verSnp.data().expiring > now){
if(data.email == verSnp.data().email){
if(verSnp.data().attempt > 0){
if(data.otp == verSnp.data().otp){
return admin.auth().getUserByEmail(data.email);
} else {
dataOptCorresponds = false;
var redAttempt = verSnp.data().attempt - 1
return admin.firestore().collection('verification').doc(data.sesId).update({
attempt: redAttempt
})
}
} else {
throw new Error('Incorrect OTP. You have exhausted your attempts. Please request a new OTP.')
}
} else {
throw new Error('Incorrect email. How did you get here?')
}
} else {
throw new Error('OTP is expired. Please request a new OTP.')
}
} else {
throw new Error('OTP is invalid. Please request a new OTP.')
}
},
error => {
throw new functions.https.HttpsError('invalid-argument', error.message);
}
)
.then(user => {
if(dataOptCorresponds) {
return admin.auth().updateUser(user.uid,{
password: data.password
})
} else {
throw new Error(`Incorrect OTP. You have xxxx attempts remaining.`)
}
})
.then(() => {
return admin.firestore().collection('verification').doc(data.sesId).update({
attempt: 'verified'
})
.then(() => {
return {result: "success"}
})
.catch(error => {
throw new functions.https.HttpsError('internal', error.message);
})
} else {
throw new functions.https.HttpsError('invalid-argument', 'Enter OTP');
}
})
来源:https://stackoverflow.com/questions/59392765/what-will-happen-if-i-set-the-requests-mode-to-no-cors-in-my-firebase-cloud-f