PhoneCredential must be valid phone credential - firebase

Hi i'm trying to set the phone number of a User with my following method and I get the following error "updatePhoneNumber failed: First argument "phoneCredential" must be a valid phone credential." I have tried different ways any suggestions?
_addPhone = () =>{
firebase.auth().currentUser.updatePhoneNumber({
phoneNumber: '+441234567890' // Fake for question
}).then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
});
}

The updatePhoneNumber method can only accept valid credentials that firebase will create once a phone number will be verified.
Similar question on the subject

Related

firebase Sign in with email and password not working with await function vuejs

async signIn() {
try {
let userCredential= await firebase.auth().signInWithEmailAndPassword(this.email, this.password)
console.log("user value is",userCredential)
this.$router.replace({name:'Sidebar'})
} catch (err) {
console.log("Invalid user", err);
}
},
},
};
When I use login without the await function it logs in, even on wrong credentials, and if I use the await function nothing happens and also it shows no errors.
When using the await function even on wrong credentials it's not showing an error, and also on correct credentials, it's not showing console.log value. What am I doing wrong here? why is it not checking DB for email and password!
You should dont use try catch here, make it simple like this. Beside you should not post information about firebase. It is sensitive infor.
firebase.auth().signInWithEmailAndPassword(this.email, this.password)
.then(function(firebaseUser) {
// Success
console.log("user value is", firebaseUser)
this.$router.replace({name:'Sidebar'})
})
.catch(function(error) {
// Error Handling
console.log("Invalid user", error);
});
I found the error. The form was not submitting the correct value at submission. Had to render the value outside the form and pass the values normally without form.

How to use Firebase's 'verifyPhoneNumber()' to confirm phone # ownership without using # to sign-in?

Im using react-native-firebase v5.6 in a project.
Goal: In the registration flow, I have the user input their phone number, I then send a OTP to said phone number. I want to be able to compare the code entered by the user with the code sent from Firebase, to be able to grant entry to the next steps in registration.
Problem: the user gets the SMS OTP and everything , but the phoneAuthSnapshot object returned by firebase.auth().verifyPhoneNumber(number).on('state_changed', (phoneAuthSnapshot => {}), it doesn't give a value for the code that firebase sent, so there's nothing to compare the users entered code with. However, there's a value for the verificationId property. Here's the object return from the above method:
'Verification code sent', {
verificationId: 'AM5PThBmFvPRB6x_tySDSCBG-6tezCCm0Niwm2ohmtmYktNJALCkj11vpwyou3QGTg_lT4lkKme8UvMGhtDO5rfMM7U9SNq7duQ41T8TeJupuEkxWOelgUiKf_iGSjnodFv9Jee8gvHc50XeAJ3z7wj0_BRSg_gwlN6sumL1rXJQ6AdZwzvGetebXhZMb2gGVQ9J7_JZykCwREEPB-vC0lQcUVdSMBjtig',
code: null,
error: null,
state: 'sent'
}
Here is my on-screen implementation:
firebase
.firestore()
.collection('users')
.where('phoneNumber', '==', this.state.phoneNumber)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty === true) {
// change status
this.setState({ status: 'Sending confirmation code...' });
// send confirmation OTP
firebase.auth().verifyPhoneNumber(this.state.phoneNumber).on(
'state_changed',
(phoneAuthSnapshot) => {
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT:
console.log('Verification code sent', phoneAuthSnapshot);
this.setState({ status: 'Confirmation code sent.', confirmationCode: phoneAuthSnapshot.code });
break;
case firebase.auth.PhoneAuthState.ERROR:
console.log('Verification error: ' + JSON.stringify(phoneAuthSnapshot));
this.setState({ status: 'Error sending code.', processing: false });
break;
}
},
(error) => {
console.log('Error verifying phone number: ' + error);
}
);
}
})
.catch((error) => {
// there was an error
console.log('Error during firebase operation: ' + JSON.stringify(error));
});
How do I get the code sent from Firebase to be able to compare?
As #christos-lytras had in their answer, the verification code is not exposed to your application.
This is done for security reasons as providing the code used for the out of band authentication to the device itself would allow a knowledgeable user to just take the code out of memory and authenticate as if they had access to that phone number.
The general flow of operations is:
Get the phone number to be verified
Use that number with verifyPhoneNumber() and cache the verification ID it returns
Prompt the user to input the code (or automatically retrieve it)
Bundle the ID and the user's input together as a credential using firebase.auth.PhoneAuthProvider.credential(id, code)
Attempt to sign in with that credential using
firebase.auth().signInWithCredential(credential)
In your source code, you also use the on(event, observer, errorCb, successCb) listener of the verifyPhoneNumber(phoneNumber) method. However this method also supports listening to results using Promises, which allows you to chain to your Firebase query. This is shown below.
Sending the verification code:
firebase
.firestore()
.collection('users')
.where('phoneNumber', '==', this.state.phoneNumber)
.get()
.then((querySnapshot) => {
if (!querySnapshot.empty) {
// User found with this phone number.
throw new Error('already-exists');
}
// change status
this.setState({ status: 'Sending confirmation code...' });
// send confirmation OTP
return firebase.auth().verifyPhoneNumber(this.state.phoneNumber)
})
.then((phoneAuthSnapshot) => {
// verification sent
this.setState({
status: 'Confirmation code sent.',
verificationId: phoneAuthSnapshot.verificationId,
showCodeInput: true // shows input field such as react-native-confirmation-code-field
});
})
.catch((error) => {
// there was an error
let newStatus;
if (error.message === 'already-exists') {
newStatus = 'Sorry, this phone number is already in use.';
} else {
// Other internal error
// see https://firebase.google.com/docs/reference/js/firebase.firestore.html#firestore-error-code
// see https://firebase.google.com/docs/reference/js/firebase.auth.PhoneAuthProvider#verify-phone-number
// probably 'unavailable' or 'deadline-exceeded' for loss of connection while querying users
newStatus = 'Failed to send verification code.';
console.log('Unexpected error during firebase operation: ' + JSON.stringify(error));
}
this.setState({
status: newStatus,
processing: false
});
});
Handling a user-sourced verification code:
codeInputSubmitted(code) {
const { verificationId } = this.state;
const credential = firebase.auth.PhoneAuthProvider.credential(
verificationId,
code
);
// To verify phone number without interfering with the existing user
// who is signed in, we offload the verification to a worker app.
let fbWorkerApp = firebase.apps.find(app => app.name === 'auth-worker')
|| firebase.initializeApp(firebase.app().options, 'auth-worker');
fbWorkerAuth = fbWorkerApp.auth();
fbWorkerAuth.setPersistence(firebase.auth.Auth.Persistence.NONE); // disables caching of account credentials
fbWorkerAuth.signInWithCredential(credential)
.then((userCredential) => {
// userCredential.additionalUserInfo.isNewUser may be present
// userCredential.credential can be used to link to an existing user account
// successful
this.setState({
status: 'Phone number verified!',
verificationId: null,
showCodeInput: false,
user: userCredential.user;
});
return fbWorkerAuth.signOut().catch(err => console.error('Ignored sign out error: ', err);
})
.catch((err) => {
// failed
let userErrorMessage;
if (error.code === 'auth/invalid-verification-code') {
userErrorMessage = 'Sorry, that code was incorrect.'
} else if (error.code === 'auth/user-disabled') {
userErrorMessage = 'Sorry, this phone number has been blocked.';
} else {
// other internal error
// see https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#sign-inwith-credential
userErrorMessage = 'Sorry, we couldn\'t verify that phone number at the moment. '
+ 'Please try again later. '
+ '\n\nIf the issue persists, please contact support.'
}
this.setState({
codeInputErrorMessage: userErrorMessage
});
})
}
API References:
verifyPhoneNumber() - React Native or Firebase
PhoneAuthProvider.credential(id, code) - Firebase
signInWithCredential() - React Native or Firebase
Suggested code input component:
react-native-confirmation-code-field
Firebase firebase.auth.PhoneAuthProvider won't give you the code for to compare, you'll have to use verificationId to verify the verificationCode that the user enters. There is a basic example in firebase documentation than uses firebase.auth.PhoneAuthProvider.credential and then tries to sign in using these credentials with firebase.auth().signInWithCredential(phoneCredential):
firebase
.firestore()
.collection('users')
.where('phoneNumber', '==', this.state.phoneNumber)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty === true) {
// change status
this.setState({ status: 'Sending confirmation code...' });
// send confirmation OTP
firebase.auth().verifyPhoneNumber(this.state.phoneNumber).on(
'state_changed',
(phoneAuthSnapshot) => {
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT:
console.log('Verification code sent', phoneAuthSnapshot);
// this.setState({ status: 'Confirmation code sent.', confirmationCode: phoneAuthSnapshot.code });
// Prompt the user the enter the verification code they get and save it to state
const userVerificationCodeInput = this.state.userVerificationCode;
const phoneCredentials = firebase.auth.PhoneAuthProvider.credential(
phoneAuthSnapshot.verificationId,
userVerificationCodeInput
);
// Try to sign in with the phone credentials
firebase.auth().signInWithCredential(phoneCredentials)
.then(userCredentials => {
// Sign in successfull
// Use userCredentials.user and userCredentials.additionalUserInfo
})
.catch(error => {
// Check error code to see the reason
// Expect something like:
// auth/invalid-verification-code
// auth/invalid-verification-id
});
break;
case firebase.auth.PhoneAuthState.ERROR:
console.log('Verification error: ' + JSON.stringify(phoneAuthSnapshot));
this.setState({ status: 'Error sending code.', processing: false });
break;
}
},
(error) => {
console.log('Error verifying phone number: ' + error);
}
);
}
})
.catch((error) => {
// there was an error
console.log('Error during firebase operation: ' + JSON.stringify(error));
});
In order to use Multi-factor Authentication, you must create Phone Sign-in provider in the background alongside primary (in your case) email sign-in provider either initially or later while user choose to update settings and enables MFA. And then link it while user is logged using email sign-in provider as follows;
const credential = auth.PhoneAuthProvider.credential(verificationId, code);
let userData = await auth().currentUser.linkWithCredential(credential);
This is not supported by firebase unfortunately. Logging in and out after signInWithCredential can work, but is very confusing
I was facing the same difficulty. My aim was only to verify users' phone numbersenter image description here and then register them using email and password. After a long intense trial and error methodology, I have arrived at the solution. But the point is I am using firebase in my android application. what it did was
I first tried matching the OTP with the user entered OTP, but the OTP that firebase provides us in the backend is encrypted with some logic and the logic is nowhere in the documentation so I could not decrypt it.
The second approach worked for me. What I did was, I signed in the user using the phone authorization and when the task was successful I deleted the newly created user there itself and then signed in the user using email id and password.

How to get the verified phone number in firebase cloud functions?

I have this function to add a custom claim when a user is verified his phone number
exports.phoneCustomClaim = functions.auth.user().onCreate((user) => {
const sellerNumber = user.phone; // here i am not getting the phone number
const uid = user.uid;
const customClaims = {
number: sellerNumber,
};
console.log(`Uid: ${uid}, number: ${sellerNumber}`);
console.log(`before Seller number: ${sellerNumber}`);
return admin.auth().setCustomUserClaims(uid, customClaims).then(() => {
console.log(`claim added!`);
})
.catch(error => {
console.log(error);
})
});
Log
Uid: 1mlxF1T8xcWbByAzQ7rfKjdshj, number: undefined
please how to get that phone number?
I suspect that the user account is created (but not authenticated) before the phone number is actually verified. This is similar to how the flow for email verification works.
If that is the case, you'll have to work around the problem. As there currently is no trigger for Cloud Functions when a user profile is updated, the best you can do is call a callable Cloud Function from your code when the user has verified their phone number.

signInWithEmailAndPassword: getting auth/user-token-expired [duplicate]

I am using Firebase authentication in my iOS app. Is there any way in Firebase when user login my app with Firebase then logout that user all other devices(sessions)? Can I do that with Firebase admin SDK?
When i had this issue i resolved it with cloud functions
Please visit this link for more details https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens
Do the following;
Set up web server with firebase cloud functions (if none exists)
use the admin sdk(thats the only way this method would work) - [Visit this link] (
(https://firebase.google.com/docs/admin/setup#initialize_the_sdk).
Create an api that receives the uid and revokes current sessions as specified in the first link above
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
//return valid response to ios app to continue the user's login process
});
Voila users logged out. I hope this gives insight into resolving the issue
Firebase doesn't provide such feature. You need to manage it yourself.
Here is the Firebase Doc and they haven't mentioned anything related to single user sign in.
Here is what you can do for this-
Take one token in User node (Where you save user's other data) in Firebase database and regenerate it every time you logged in into application, Match this token with already logged in user's token (Which is saved locally) in appDidBecomeActive and appDidFinishLaunching or possibly each time you perform any operation with Firebase or may be in some fixed time interval. If tokens are different logged out the user manually and take user to authenticate screen.
What i have done is:
Created collection in firestore called "activeSessions".User email as an id for object and "activeID" field for holding most recent session id.
in sign in page code:
Generating id for a user session every time user is logging in.
Add this id to localstorage(should be cleaned everytime before adding).
Replace "activeID" by generated id in collection "activeSessions" with current user email.
function addToActiveSession() {
var sesID = gen();
var db = firebase.firestore();
localStorage.setItem('userID', sesID);
db.collection("activeSessions").doc(firebase.auth().currentUser.email).set({
activeID: sesID
}).catch(function (error) {
console.error("Error writing document: ", error);
});
}
function gen() {
var buf = new Uint8Array(1);
window.crypto.getRandomValues(buf);
return buf[0];
}
function signin(){
firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
localStorage.clear();
addToActiveSession();
}
}), function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/wrong-password') {
alert('wrong pass');
} else {
alert(errorMessage);
}
console.log(error);
};
}
Then i am checking on each page if the id session in local storage is the same as "activeID" in firestore,if not then log out.
function checkSession(){
var db = firebase.firestore();
var docRef = db.collection("activeSessions").doc(firebase.auth().currentUser.email);
docRef.get().then(function (doc) {
alert(doc.data().activeID);
alert(localStorage.getItem('userID'));
if (doc.data().activeID != localStorage.getItem('userID')) {
alert("bie bie");
firebase.auth().signOut().then(() => {
window.location.href = "signin.html";
}).catch((error) => {
// An error happened.
});
window.location.href = "accountone.html";
} else{alert("vse ok");}
}).catch(function (error) {
console.log("Error getting document:", error);
});
}
PS: window has to be refreshed to log inactive session out.

Passing email address in custom token

I have simple backend that can register users and generate jwt's using the firebase-admin package.
When I login with this custom token in firebase console -> authentication the email address is not set. Is there a way to be able to see the email address from there?
You can use the updateUser() method to update the user's email address (detailed docs here):
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com"
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
});

Resources