You can use Firebase Authentication to sign in a user by sending an SMS message to the user’s phone. The user signs in using a one-time code contained in the SMS message.
The easiest way to add phone number sign-in to your app is to use FirebaseUI, which includes a drop-in sign-in widget that implements sign-in flows for phone number sign-in, as well as password-based and federated sign-in. This document describes how to implement a phone number sign-in flow using the Firebase SDK.
Phone numbers that end users provide for authentication will be sent and stored by Google to improve our spam and abuse prevention across Google services, including but not limited to Firebase. Developers should ensure they have appropriate end-user consent prior to using the Firebase Authentication phone number sign-in service.
If you haven’t already, copy the initialization snippet from the Firebase console to your project as described in Add Firebase to your JavaScript project
Authentication using only a phone number, while convenient, is less secure than the other available methods, because possession of a phone number can be easily transferred between users. Also, on devices with multiple user profiles, any user that can receive SMS messages can sign in to an account using the device’s phone number.
If you use phone number based sign-in in your app, you should offer it alongside more secure sign-in methods, and inform users of the security tradeoffs of using phone number sign-in.
To sign in users by SMS, you must first enable the Phone Number sign-in method for your Firebase project:
Firebase’s phone number sign-in request quota is high enough that most apps won’t be affected. However, if you need to sign in a very high volume of users with phone authentication, you might need to upgrade your pricing plan. See the pricing page.
Before you can sign in users with their phone numbers, you must set up Firebase’s reCAPTCHA verifier. Firebase uses reCAPTCHA to prevent abuse, such as by ensuring that the phone number verification request comes from one of your app’s allowed domains.
You don’t need to manually set up a reCAPTCHA client; when you use the Firebase SDK’s RecaptchaVerifier
object, Firebase automatically creates and handles any necessary client keys and secrets.
The RecaptchaVerifier
object supports invisible reCAPTCHA, which can often verify the user without requiring any user action, as well as the reCAPTCHA widget, which always requires user interaction to complete successfully.
The underlying rendered reCAPTCHA can be localized to the user’s preference by updating the language code on the Auth instance before rendering the reCAPTCHA. The aforementioned localization will also apply to the SMS message sent to the user, containing the verification code.
firebase.auth().languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// firebase.auth().useDeviceLanguage();
To use an invisible reCAPTCHA, create a RecaptchaVerifier
object with the size
parameter set to invisible
, specifying the ID of the button that submits your sign-in form. For example:
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
onSignInSubmit();
}
});
To use the visible reCAPTCHA widget, create an element on your page to contain the widget, and then create a RecaptchaVerifier
object, specifying the ID of the container when you do so. For example:
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
You can optionally set callback functions on the RecaptchaVerifier
object that are called when the user solves the reCAPTCHA or the reCAPTCHA expires before the user submits the form:
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
'size': 'normal',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
// ...
},
'expired-callback': function() {
// Response expired. Ask user to solve reCAPTCHA again.
// ...
}
});
If you want to pre-render the reCAPTCHA before you submit a sign-in request, call render
:
recaptchaVerifier.render().then(function(widgetId) {
window.recaptchaWidgetId = widgetId;
});
After render
resolves, you get the reCAPTCHA’s widget ID, which you can use to make calls to the reCAPTCHA API:
var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);
To initiate phone number sign-in, present the user an interface that prompts them to provide their phone number, and then call signInWithPhoneNumber
to request that Firebase send an authentication code to the user’s phone by SMS:
Legal requirements vary, but as a best practice and to set expectations for your users, you should inform them that if they use phone sign-in, they might receive an SMS message for verification and standard rates apply.
signInWithPhoneNumber
, passing to it the user’s phone number and the RecaptchaVerifier
you created earlier.var phoneNumber = getPhoneNumberFromUserInput();
var appVerifier = window.recaptchaVerifier;
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
}).catch(function (error) {
// Error; SMS not sent
// ...
});
If signInWithPhoneNumber
results in an error, reset the reCAPTCHA so the user can try again:
grecaptcha.reset(window.recaptchaWidgetId);
// Or, if you haven't stored the widget ID:
window.recaptchaVerifier.render().then(function(widgetId) {
grecaptcha.reset(widgetId);
}
The signInWithPhoneNumber
method issues the reCAPTCHA challenge to the user, and if the user passes the challenge, requests that Firebase Authentication send an SMS message containing a verification code to the user’s phone.
To prevent abuse, Firebase enforces a limit on the number of SMS messages that can be sent to a single phone number within a period of time. If you exceed this limit, phone number verification requests might be throttled. If you encounter this issue during development, use a different phone number for testing, or try the request again later.
After the call to signInWithPhoneNumber
succeeds, prompt the user to type the verification code they received by SMS. Then, sign in the user by passing the code to the confirm
method of the ConfirmationResult
object that was passed to signInWithPhoneNumber
’s fulfillment handler (that is, its then
block). For example:
var code = getCodeFromUserInput();
confirmationResult.confirm(code).then(function (result) {
// User signed in successfully.
var user = result.user;
// ...
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
If the call to confirm
succeeded, the user is successfully signed in.
If you need to get an AuthCredential
object for the user’s account, pass the verification code from the confirmation result and the verification code to PhoneAuthProvider.credential
instead of calling confirm
:
var credential = firebase.auth.PhoneAuthProvider.credential(confirmationResult.verificationId, code);
Then, you can sign in the user with the credential:
firebase.auth().signInWithCredential(credential);
You can whitelist phone numbers for development via the Firebase console. Whitelisting phone numbers provides these benefits:
Phone numbers to whitelist must meet these requirements:
You can directly start using a whitelisted phone number in your application. This allows you to perform manual testing during development stages without running into quota issues or throttling. You can also test directly from an iOS simulator or Android emulator without Google Play Services installed.
When you provide the whitelisted phone number and send the verification code, no actual SMS is sent. Instead, you need to provide the previously configured verification code to complete the sign in.
On sign-in completion, a Firebase user is created with that phone number. The user has the same behavior and properties as a real phone number user, and can access Realtime Database/Cloud Firestore and other services the same way. The ID token minted during this process has the same signature as a real phone number user.
Because the ID token for the whitelisted phone number has the same signature as a real phone number user, it is important to store these numbers securely and to continuously recycle them.
Another option is to set a test role via custom claims on these users to differentiate them as fake users if you want to further restrict access.
In addition to manual testing, Firebase Authentication provides APIs to help write integration tests for phone auth testing. These APIs disable app verification by disabling the reCAPTCHA requirement in web and silent push notifications in iOS. This makes automation testing possible in these flows and easier to implement. In addition, they help provide the ability to test instant verification flows on Android.
Make sure app verification is not disabled for production apps and that no whitelisted phone numbers are hardcoded in your production app.
On web, set appVerificationDisabledForTesting
to true
before rendering the firebase.auth.RecaptchaVerifier
. This resolves the reCAPTCHA automatically, allowing you to pass the phone number without manually solving it. Note that even though reCAPTCHA is disabled, using a non-whitelisted phone number will still fail to complete sign in. Only whitelisted phone numbers can be used with this API.
// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;
var phoneNumber = "+16505554567";
var testVerificationCode = "123456";
// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// confirmationResult can resolve with the whitelisted testVerificationCode above.
return confirmationResult.confirm(testVerificationCode)
}).catch(function (error) {
// Error; SMS not sent
// ...
});
Visible and invisible mock reCAPTCHA app verifiers behave differently when app verification is disabled:
appVerifier.render()
, it automatically resolves itself after a fraction of a second delay. This is equivalent to a user clicking the reCAPTCHA immediately upon rendering. The reCAPTCHA response will expire after some time and then auto-resolve again.appVerifier.verify()
call or when the button anchor of the reCAPTCHA is clicked after a fraction of a second delay. Similarly, the response will expire after some time and will only auto-resolve either after the appVerifier.verify()
call or when the button anchor of the reCAPTCHA is clicked again.Whenever a mock reCAPTCHA is resolved, the corresponding callback function is triggered as expected with the fake response. If an expiration callback is also specified, it will trigger on expiration.
After a user signs in for the first time, a new user account is created and linked to the credentials—that is, the user name and password, phone number, or auth provider information—the user signed in with. This new account is stored as part of your Firebase project, and can be used to identify a user across every app in your project, regardless of how the user signs in.
In your apps, the recommended way to know the auth status of your user is to set an observer on the Auth
object. You can then get the user’s basic profile information from the User
object.
In your Firebase Realtime Database and Cloud Storage Security Rules, you can get the signed-in user’s unique user ID from the auth
variable, and use it to control what data a user can access.
You can allow users to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account.
To sign out a user, call signOut
:
firebase.auth().signOut().then(function() {
// Sign-out successful.
}).catch(function(error) {
// An error happened.
});
#firebase #flutter #mobile-apps #javascript #security