Design a Custom Phone Authentication with Firebase for Android
May 16, 2021 by Atul Sharma
Many apps require their users to be authenticated. So for the purpose of authenticating the apps uses phone number authentication inside their apps.
When you use phone number authentication, you have many benefits like
- Preventing Fake Users: When you use phone authentication, the user can’t be able to register for multiple accounts, as for each account a unique phone number is needed.
- Increase User Value: When you have all the users verified by a phone number the value of your user base increases.
- Increased Security and User Experience: Nowadays more people using apps and remembering passwords are a headache for many users, so they end up using
weak passwords
. Using phone authentication increases security and user experience, as the user does not need to create and remember passwords, they will enter their number and then they can receive a temporary authentication code by SMS.
Why Firebase Phone Authentication?
For implementing phone authentication you need to pay for SMS service, but with firebase, you can do it for FREE, isn’t it awesome? The free plan of firebase has Ten Thousand Verification per month. That’s enough for the starter apps I guess, but yes if you exceed this limit, you need to pay.
Before we begin, we need to setup firebase for our android project
-
Add Firebase to your Android Project. If not familiar visit here.
-
Using the
Firebase Android BoM
, declare the dependency for the Firebase Authentication Android library in your module (app-level) Gradle file (usuallyapp/build.gradle
).dependencies { // **Import the BoM for the Firebase platform** implementation platform('com.google.firebase:firebase-bom:28.0.1') // **Declare the dependency for the Firebase Authentication library** implementation 'com.google.firebase:firebase-auth' }
When using the BoM, you don’t specify versions in Firebase library dependencies
-
Add your
SHA-1
andSHA-256
to your firebase project.
Enabling Phone Authentication for Firebase Project
-
To sign in users by SMS, you must first enable the Phone Number sign-in method for your Firebase project:
- In the Firebase console, open the Authentication section.
- On the Sign-in Method page, enable the Phone Number sign-in method.
-
Enable App Verification
Firebase must be able to verify that phone number sign-in requests are coming from your app, and to verify that enable
SafetyNet
which automatically verifies the device legitimate if google play services is installed. To enable it visit this link.
Send Verification code to User’s Phone
To initiate phone number sign-in, present the user an interface that prompts them to type their phone number.
Step 1
After you have user’s phone number, pass their phone number to the PhoneAuthProvider.verifyPhoneNumber
method to request that Firebase verify the user’s phone number.
private void sendOtp(String mobile_no) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(mobile_no)// Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
Step 2
When you call PhoneAuthProvider.verifyPhoneNumber
, you must also provide an instance of OnVerificationStateChangedCallbacks
, which contains implementations of the callback functions that handle the results of the request.
String mVerificationId;
PhoneAuthProvider.ForceResendingToken mResendToken;
private final PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
//Getting the code sent by SMS
String code = credential.getSmsCode();
//sometime the code is not detected automatically
//in this case the code will be null
//so user has to manually enter the code
if (code != null) {
verifyVerificationCode(code); // we shall se this implementation next
}
}
@Override
public void onVerificationFailed(FirebaseException e) {
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
if (e instanceof FirebaseAuthInvalidCredentialsException) {
// Invalid request
// ...
} else if (e instanceof FirebaseTooManyRequestsException) {
// The SMS quota for the project has been exceeded
// ...
}
// Show a message and update the UI
// ...
}
@Override
public void onCodeSent(@NonNull String verificationId,@NonNull PhoneAuthProvider.ForceResendingToken token) {
super.onCodeSent(verificationId, token);
// Save verification ID and resending token so we can use them later
mVerificationId = verificationId;
mResendToken = token;
}
};
Step 3
To check that OTP entered by user matches the one sent by Firebase, we are creating a PhoneAuthCredential
object.
private void verifyVerificationCode(String code) {
try {
//creating the credential
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(mVerificationId, code);
//signing the user
signInWithPhoneAuthCredential(credential);
} catch (Exception e) {
myApp.showSnackBarForErrors(EmployerOTP.this,getString(R.string.if_no_auth));
}
}
Step 4
Sign in the user to your App.
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
FirebaseUser user = task.getResult().getUser();
} else {
String message = getString(R.string.error_message);
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
message = getString(R.string.invalid_code);
}
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.dismiss, new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
snackbar.show();
}
}
});
}
To get User’s instance and Log out User
// get current user signed in
FirebaseAuth.getInstance().getCurrentUser();
// log out current user
FirebaseAuth.getInstance().signOut();
There is a Tip!
Yes, you read it right. During your development process to do verification of user you might see frequent crashes during the verification process of your app.
This crash is something like this which says
This error or crash (apparently 😅), created problems for users during authentication and and after researching a while, I got to know that this happens due to either low memory on phone or low network connection. When you maintain log with verification id i.e. Log.d(TAG, verificationId)
it will show null in verificationId
and as per the error it says cannot create PhoneAuthCredential
without verification proof and verificationId
is our verification proof.
Solution:
-
Clear phone cache and try signing again.
-
Go to app info and clear cache and Restore Instance State like the code programmatically.
private String verificationId; private static final String KEY_VERIFICATION_ID = "key_verification_id"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_otp); // Restore instance state // put this code after starting phone number verification if (verificationId == null && savedInstanceState != null) { onRestoreInstanceState(savedInstanceState); } }//end of onCreate // over-ride these 2 methods @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_VERIFICATION_ID,verificationId); } @Override protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); verificationId = savedInstanceState.getString(KEY_VERIFICATION_ID); }
What happens is the
verificationId
is stored locally and it is lost sometimes due to less memory. So to tackle this situation we have to restore Instance State which won’t let theverificationId
to be lost.
Some Helpful points to implement effective authentication
-
Use fictional numbers instead of your real ones for testing as Firebase to save your project from abuse blocks that number for a time period from signing in again.
-
Use
PinText
view for better User Experience to autofill the OTP.
Hope it helped. Happy Coding 😀