Firebase Phone Number Authentication allows users to sign into an app by providing a phone number. Before the user can sign in, a one-time code is sent via SMS to the provided number which must be entered into the app to gain access. Firebase will validate the phone number, handle any normalization logic that needs to happen, if this is a new user, or a returning one, and go ahead and generate a one-time code associated with this request.
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.
To sign in users by SMS, you must first enable the Phone Number sign-in method for your Firebase project:
Second, connect your app to Firebase. If you're using the latest version of Android Studio (version 2.2 or later), it's recommend using the Firebase Assistant to connect your app to Firebase. The Firebase Assistant can connect your existing project or create a new one for you and automatically install any necessary gradle dependencies.
To open the Firebase Assistant in Android Studio:
There are two ways to do Firebase Phone Number Authentication
Phone Number Authentication via FirebaseUI library
Add the following dependency to your app level build.gradle file to use FirebaseUI.
compile 'com.google.android.gms:play-services:11.4.2' compile 'com.firebaseui:firebase-ui:3.1.0'
Make sure to add the Fabric repository to your project level build.gradle file. To do so, add the highlighted line.
allprojects { repositories { // ... maven { url 'https://maven.fabric.io/public' } } }
Create an activity named PhoneAuthenticationActivity
and add the below code.
public class PhoneAuthenticationActivity extends AppCompatActivity { private static final int RC_SIGN_IN = 123; private Activity activity = PhoneAuthenticationActivity.this; private String TAG = "TAG"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_phone_authentication); FirebaseAuth auth = FirebaseAuth.getInstance(); if (auth.getCurrentUser() != null) { startActivity(new Intent(activity, LoggedActivity.class)); finish(); } else { // not signed in startActivityForResult( AuthUI.getInstance() .createSignInIntentBuilder() .setAvailableProviders( Arrays.asList( new AuthUI.IdpConfig.Builder(AuthUI.PHONE_VERIFICATION_PROVIDER).build() )) .build(), RC_SIGN_IN); } } protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) { IdpResponse response = IdpResponse.fromResultIntent(data); if (resultCode == ResultCodes.OK) { startActivity(new Intent(activity, LoggedActivity.class)); finish(); return; } else { if (response == null) { // User pressed back button Log.d(TAG, "Login canceled by User"); return; } if (response.getErrorCode() == ErrorCodes.NO_NETWORK) { Log.d(TAG, "No Internet Connection"); return; } if (response.getErrorCode() == ErrorCodes.UNKNOWN_ERROR) { Log.d(TAG, "Unknown Error"); return; } } Log.d(TAG, "Unknown sign in response"); } } }
Create an activity named LoggedActivity
and add the below code.
public class LoggedActivity extends AppCompatActivity { private String TAG = "TAG"; private Activity activity = LoggedActivity.this; Button btnSignOut; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_logged); FirebaseAuth auth = FirebaseAuth.getInstance(); if (auth.getCurrentUser() != null) { Log.d(TAG, "ProviderId: " + auth.getCurrentUser().getProviderId()); Log.d(TAG, "PhoneNumber: " + auth.getCurrentUser().getPhoneNumber()); } else { Log.d(TAG, "onCreate: EMPTY"); } btnSignOut = (Button) findViewById(R.id.btn2); btnSignOut.setOnClickListener(view -> { auth.signOut(); startActivity(new Intent(activity, PhoneActivity.class)); finish(); }); } }
Now when you run the app, you can see the Phone Number Authentication in action.
You can create your own custom style in the styles.xml file and edit the UI as per your need. The custom style you create should have FirebaseUI as the parent and the three standard AppCompat color must be defined. For the demo, I am creating a style having the purple theme as shown below.
<style name="PurpleTheme" parent="FirebaseUI"> <!-- Required standard AppCompat colors --> <item name="colorPrimary">@color/material_purple_500</item> <item name="colorPrimaryDark">@color/material_purple_700</item> <item name="colorAccent">@color/material_yellow_a700</item> <item name="colorControlNormal">@color/material_purple_500</item> <item name="colorControlActivated">@color/material_majenta_a700</item> <item name="colorControlHighlight">@color/material_purple_a200</item> <item name="android:windowBackground">@color/material_purple_50</item> </style>
Once the custom style is created, set it in the sign-in intent as shown below.
startActivityForResult( AuthUI.getInstance(this).createSignInIntentBuilder() // ... .setTheme(R.style.PurpleTheme) .build());
If you also want to change the text that appears during the login flow, here’s the list.
Phone Number Authentication via Firebase SDK
Having covered Phone number authentication using FirebaseUI Auth in first part, this part will explain how to achieve similar results using the Firebase SDK.
Phone authentication using the Firebase SDK involves asking the user for a phone number, sending a 6-digit verification code via SMS message, and then verifying that the code is valid. If the code entered is valid, the app can take the usual steps to sign the user in. In the event that the code is not received by the user, the app should typically provide the user the option to resend the code.
All of the work involved in generating the code and sending the SMS message is handled by a call to the verifyPhoneNumber()
method of the PhoneAuthProvider
instance. This will trigger a call to one of the following callback methods:
onVerificationCompleted()
. Called only on certain device configurations (typically devices containing a SIM card) where the verification code can be verified automatically on the device without the user having to manually enter it.onVerificationFailed()
. Indicates that an error occurred when sending the activation code. This is usually the result of the user entering an invalid or incorrectly formatted phone number.onCodeSent()
. Called after the code has been sent. This method is passed a verification ID and a resend token that should be referenced when making a code resend request.onCodeAutoRetrievalTimeOut()
. When the verifyPhoneNumber()
method is called it is passed a timeout value. This method is called if the verification process is not completed within the given timescale.Once the verification code has been entered by the user, the code and the verification ID provided to the onCodeSent()
callback are passed through to the getCredential()
method of the PhoneAuthProvider
class. This returns a PhoneAuthCredential
object which can be used to sign the user into the app in the usual way via a call to the signInWithCredential()
method of the FirebaseAuth class.
So, connect your app to Firebase as described above.
Phone verification makes use of the latest version of the firebase-auth
library. Add the following dependency to your app level build.gradle file to use FirebaseUI.
compile 'com.google.firebase:firebase-auth:11.4.2'
The user interface for the main activity is going to consist of four Buttons, two EditText views and a TextView object.
Layout
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:paddingTop="30dp"> <TextView android:id="@+id/tvStatus" android:text="Signed Out" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText android:id="@+id/edPhoneNumber" android:hint="Phone number" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSendCode" android:onClick="sendPhoneNumber" android:text="Send code" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnResendCode" android:onClick="resendCode" android:text="Resend code" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText android:id="@+id/edCode" android:hint="Code" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnVerifyCode" android:onClick="verifyCode" android:text="Verify code" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSignOut" android:onClick="signOut" android:text="Sign out" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Create an activity named PhoneFbSdkActivity
and add the below code.
public class PhoneFbSdkActivity extends AppCompatActivity { private String TAG = "PhoneFbSdkActivity"; private int TIMEOUT = 60; private EditText edCode, edPhoneNumber; private Button btnSendCode, btnResendCode, btnVerifyCode, btnSignOut; private TextView tvStatus; private Activity activity = PhoneFbSdkActivity.this; private String phoneVerificationId; private PhoneAuthProvider.OnVerificationStateChangedCallbacks cbVerification; private PhoneAuthProvider.ForceResendingToken resendToken; private FirebaseAuth fbAuth; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_phone_fb_sdk); edPhoneNumber = (EditText) findViewById(R.id.edPhoneNumber); edCode = (EditText) findViewById(R.id.edCode); btnVerifyCode = (Button) findViewById(R.id.btnVerifyCode); btnSendCode = (Button) findViewById(R.id.btnSendCode); btnResendCode = (Button) findViewById(R.id.btnResendCode); btnSignOut = (Button) findViewById(R.id.btnSignOut); tvStatus = (TextView) findViewById(R.id.tvStatus); btnVerifyCode.setEnabled(false); btnResendCode.setEnabled(false); btnSignOut.setEnabled(false); tvStatus.setText("Signed Out"); fbAuth = FirebaseAuth.getInstance(); if (fbAuth.getCurrentUser() != null) { startActivity(new Intent(activity, LoggedActivity.class)); finish(); } else { Toast.makeText(activity, "Please login", Toast.LENGTH_SHORT).show(); } } public void sendPhoneNumber(View v) { String phoneNumber = edPhoneNumber.getText().toString(); setUpVerificatonCallbacks(); PhoneAuthProvider.getInstance().verifyPhoneNumber( phoneNumber, // Phone number to verify TIMEOUT, // Timeout duration TimeUnit.SECONDS, // Unit of timeout activity, // Activity (for callback binding) cbVerification); } public void verifyCode(View view) { String code = edCode.getText().toString(); PhoneAuthCredential credential = PhoneAuthProvider.getCredential(phoneVerificationId, code); signInWithPhoneAuthCredential(credential); } private void setUpVerificatonCallbacks() { cbVerification = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() { @Override public void onVerificationCompleted(PhoneAuthCredential credential) { btnSignOut.setEnabled(true); tvStatus.setText("Signed In"); btnResendCode.setEnabled(false); btnVerifyCode.setEnabled(false); edCode.setText(""); signInWithPhoneAuthCredential(credential); } @Override public void onVerificationFailed(FirebaseException e) { if (e instanceof FirebaseAuthInvalidCredentialsException) { // Invalid request Log.d(TAG, "Invalid credential: " + e.getLocalizedMessage()); } else if (e instanceof FirebaseTooManyRequestsException) { // SMS quota exceeded Log.d(TAG, "SMS Quota exceeded."); } } @Override public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) { phoneVerificationId = verificationId; resendToken = token; btnVerifyCode.setEnabled(true); btnSendCode.setEnabled(false); btnResendCode.setEnabled(true); } }; } private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) { fbAuth.signInWithCredential(credential) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { btnSignOut.setEnabled(true); edCode.setText(""); tvStatus.setText("Signed In"); btnResendCode.setEnabled(false); btnVerifyCode.setEnabled(false); FirebaseUser user = task.getResult().getUser(); Toast.makeText(activity, "Successfully logged", Toast.LENGTH_SHORT).show(); startActivity(new Intent(activity, LoggedActivity.class)); finish(); } else { if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) { Toast.makeText(activity, "The verification code entered was invalid", Toast.LENGTH_SHORT).show(); } } } }); } public void resendCode(View view) { String phoneNumber = edPhoneNumber.getText().toString(); setUpVerificatonCallbacks(); PhoneAuthProvider.getInstance().verifyPhoneNumber( phoneNumber, TIMEOUT, TimeUnit.SECONDS, activity, cbVerification, resendToken); } public void signOut(View view) { fbAuth.signOut(); tvStatus.setText("Signed Out"); btnSignOut.setEnabled(false); btnSendCode.setEnabled(true); } }
The sendPhoneNumber()
method calls the verifyPhoneNumber()
method of the PhoneAuthProvider
instance to send the code to the phone number entered into the edPhoneNumber
EditText
view. Before doing this, however, a call is made to set up the verification callbacks which are referenced in the final argument passed to the verifyPhoneNumber()
method call. The next step, therefore, is to implement this method.
After the code arrives via SMS message and has been entered it into the code text field, the user is expected to click on the Verify Code button. This button as configured to call a method named verifyCode()
. The method obtains the code from the EditText
view and passes it, along with the verification ID saved within the onCodeSent()
method to the getCredential()
method of the PhoneAuthProvider
instance. The credential returned by this method call is then passed to the signInWithPhoneCredential()
method which now also needs to be implemented.
Regardless of whether the user manually verified the code, or if verification was automatic, the app has been provided with a PhoneAuthCredential
object which can be used to sign into the app. The code for this showed in signInWithPhoneAuthCredential
method.
resendCode()
method is called when the Resend Code button is clicked and simply makes another call to the verifyPhoneNumber()
method of the PhoneAuthProvider
instance. This time, however, the method is also passed the resend token which was saved within the onCodeSent()
callback method.