FireBase Phone Authentication – Example

Hi folks, in the previous post we have seen the FireBase authentication using email/password. In continuing to our firebase authentication series today we are going to explore user authentication with a phone number.

I am maintaining a Github Repository for all firebase Tutorials. if you want to dive directly into code just check out here. You can get all firebase authentication code in FireBaseAuth git branch.

User Authentication using phone number firebase.

So if you have not configured your project for firebase phone authentication let’s do it.

Configure Project for Phone Authentication

Add Firebase to your project if you have not done yet.

Add FireBase dependencies to your project.

implementation "com.google.firebase:firebase-auth:16.1.0"
implementation "com.google.firebase:firebase-database:16.0.6"

You need to add your SHA-1 key to Firebase project .

Go to Android Studio – > Gradle (at right sidebar ) -> app -> Tasks – > Double click on SigningReport. after that you will find your SHA1 in the terminal of the android studio.

Copy the SHA1 and paste it in firebase project console.

Go to Firebase Project – >Project Setting – > General – > Apps -> Add fingerprint and paste that SHA1 over there.

Now we need to enable phone authentication in firebase console.

Go to FirebaseProject->Authentication->Sign-In methods-> Phone and enable it.

Now we have successfully configured your project for Phone Authentication.

Structure of FireBase database to store User’s detail.

In this tutorial, we are going to take phone, name, city from the user and after successful authentication, we are going to save his detail in the database. Here is the structure of that database.

When we create a user with the help of Firebase Phone Authentication it generates a unique id of each user like below. which helps us to save and identify the Logged User. so with the help of unique id, I will be saving their details in firebase database.

Now just take look into activities . I have created 4 activities for this tutorail.

FireBase Authentication Methods

FireBase provides a method to send an OTP to the phone number (country code must be there with a number for example +919109248747)

private void sendOtpToNumber(String phoneNumber) {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                "+91" + phoneNumber // Phone number to verify
                , 30                // Timeout duration
                , TimeUnit.SECONDS     // Unit of timeout
                , this         // Activity (for callback binding)
                , mPhoneCallBack);     // OnVerificationStateChangedCallbacks
    }

this method is taking a parameter mPhoneCallBack which is an instance of PhoneAuthProvider.OnVerificationStateChangedCallback . we have to override some methods to get the verification state of Phone.

PhoneAuthProvider.OnVerificationStateChangedCallbacks mPhoneCallBack;
    private void setCallBackFromFireBase() {
        mPhoneCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
              
            }

            @Override
            public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(verificationId, forceResendingToken);
               
            }
        };

    }

OnVerificationComplete() get called

  • Instant verification: in some cases, the phone number can be instantly verified without needing to send or enter a verification code
  • Auto-retrieval: on some devices, Google Play services can automatically detect the incoming verification SMS and perform verification without user action. (This capability might be unavailable with some carriers.)

onVerificationFailed() get Called

  • This method is called in response to an invalid verification request, such as a request that specifies an invalid phone number or verification code.

onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken) get Called

  • Optional. This method is called after the verification code has been sent by SMS to the provided phone number.

After getting OTP

from all above code, we will get 6 digit verification code on the provided phone number. now, what to do next:-D.

FirebaseAuth having a method signInWithCredential(phoneAuthCredential) for SignIn with taking PhoneAuthCredential object as a parameter. this object contains the login information. we will get this object in two ways

  • if a phone auto detects user sign in details after sending OTP this object will trigger in onVerificationCompleted() overridden method OR
  • if the phone, not auto detects then we will create an object with the help of verificationId (we got in onCodeSent()) and OTP.
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(verificationId, enteredOtp);

After getting this PhoneAuthCredential the object we will simply pass this object to signInWithCredential(phoneAuthCredential) to check OTP or SignIn details are valid or not.

Now let’s do it step by step to better understanding

Take phonenumber from EditText and send a OTP on it.

btnPhoneNumber.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isPhoneNumberValid()) {
                    sendOtpToNumber(edtPhoneNumber.getText().toString());
                } else {
                    Toast.makeText(PhoneLoginActivity.this, "Please enter valid phone number", Toast.LENGTH_SHORT).show();
                }
            }
        });

private void sendOtpToNumber(String phoneNumber) {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                "+91" + phoneNumber // Phone number to verify
                , 30                // Timeout duration
                , TimeUnit.SECONDS     // Unit of timeout
                , this         // Activity (for callback binding)
                , mPhoneCallBack);     // OnVerificationStateChangedCallbacks
    }

Initialize mPhoneCallBack object to get the Verification States. we will create a method (loginWithGivenCredential(phoneAuthCredential)) which will do sign in with the help of PhoneAuthCredential object.

PhoneAuthProvider.OnVerificationStateChangedCallbacks mPhoneCallBack;
    
    private void setCallBackFromFireBase() {
        mPhoneCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                mProgressDialog.hide();
                loginWithGivenCredential(phoneAuthCredential);
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
                mProgressDialog.hide();
                Toast.makeText(PhoneLoginActivity.this, "Error Login :", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(verificationId, forceResendingToken);
                mProgressDialog.hide();
                Intent intent = new Intent(PhoneLoginActivity.this, OtpVerificationActivity.class);
                intent.putExtra("verification_id", verificationId);
                intent.putExtra("resendToken", forceResendingToken);
                intent.putExtra("phone_number", edtPhoneNumber.getText().toString());
                startActivity(intent);
                finish();
            }
        };

    }

if the phone is not able to do instant verifications we will open OTPVerificationActivity and send there resend token, verificationID, phone number to Authenticate and get PhoneAuthCredential an object from OTP.

here is loginWithGivenCredential(phoneAuthCredential) method

 private void loginWithGivenCredential(PhoneAuthCredential phoneAuthCredential) {
        mAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Toast.makeText(PhoneLoginActivity.this, "Login Successfully", Toast.LENGTH_SHORT).show();
                    checkUserIsAlreadyExistInDatabase(task);
                } else {
                    mProgressDialog.dismiss();
                    Toast.makeText(PhoneLoginActivity.this, "Error in Login :", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

after successful login, we have to check whether is logged in for the first time or he/ she is Old user. for this, we need to see the user’s unique id in the firebase database.

if it exists in the database we will show his details in UserDetailsActivity or if the user is new then we will open SubmitDetailActivity to save his details.

private void checkUserIsAlreadyExistInDatabase(Task<AuthResult> task) {
        final String loggedUserUniqueId = task.getResult().getUser().getUid();
        mDatabaseRef.child("Users").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if (dataSnapshot.child(loggedUserUniqueId).exists()) {
                    startActivity(new Intent(PhoneLoginActivity.this, UserDetailActivity.class));
                    finish();
                } else {
                    startActivity(new Intent(PhoneLoginActivity.this, SubmitUserDetailActivity.class));
                    finish();
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }

Full code of PhoneLoginActivity

public class PhoneLoginActivity extends AppCompatActivity {

    EditText edtPhoneNumber;
    Button btnPhoneNumber;
    ProgressDialog mProgressDialog;

    FirebaseAuth mAuth = FirebaseAuth.getInstance();
    DatabaseReference mDatabaseRef = FirebaseDatabase.getInstance().getReference();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_phone_login);
        init();
        onClicks();
        setCallBackFromFireBase();
    }

    private void init() {
        edtPhoneNumber = findViewById(R.id.edt_phone_number);
        btnPhoneNumber = findViewById(R.id.btn_next);
        mProgressDialog = new ProgressDialog(this);
    }

    private void onClicks() {
        btnPhoneNumber.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isPhoneNumberValid()) {
                    sendOtpToNumber(edtPhoneNumber.getText().toString());
                } else {
                    Toast.makeText(PhoneLoginActivity.this, "Please enter valid phone number", Toast.LENGTH_SHORT).show();
                }
            }
        });

        edtPhoneNumber.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_NEXT) {
                    if (isPhoneNumberValid()) {
                        sendOtpToNumber(edtPhoneNumber.getText().toString());
                    } else {
                        Toast.makeText(PhoneLoginActivity.this, "Please enter valid phone number", Toast.LENGTH_SHORT).show();
                    }
                    return true;
                }
                return false;
            }
        });
    }

    private void sendOtpToNumber(String phoneNumber) {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                "+91" + phoneNumber // Phone number to verify
                , 30                // Timeout duration
                , TimeUnit.SECONDS     // Unit of timeout
                , this         // Activity (for callback binding)
                , mPhoneCallBack);     // OnVerificationStateChangedCallbacks
    }


    PhoneAuthProvider.OnVerificationStateChangedCallbacks mPhoneCallBack;

    private void setCallBackFromFireBase() {
        mPhoneCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                mProgressDialog.hide();
                loginWithGivenCredential(phoneAuthCredential);
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
                mProgressDialog.hide();
                Toast.makeText(PhoneLoginActivity.this, "Error Login :", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(verificationId, forceResendingToken);
                mProgressDialog.hide();
                Intent intent = new Intent(PhoneLoginActivity.this, OtpVerificationActivity.class);
                intent.putExtra("verification_id", verificationId);
                intent.putExtra("resendToken", forceResendingToken);
                intent.putExtra("phone_number", edtPhoneNumber.getText().toString());
                startActivity(intent);
                finish();
            }
        };

    }

    private void loginWithGivenCredential(PhoneAuthCredential phoneAuthCredential) {
        mAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Toast.makeText(PhoneLoginActivity.this, "Login Successfully", Toast.LENGTH_SHORT).show();
                    checkUserIsAlreadyExistInDatabase(task);
                } else {
                    mProgressDialog.dismiss();
                    Toast.makeText(PhoneLoginActivity.this, "Error in Login :", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    private void checkUserIsAlreadyExistInDatabase(Task<AuthResult> task) {
        final String loggedUserUniqueId = task.getResult().getUser().getUid();
        mDatabaseRef.child("Users").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if (dataSnapshot.child(loggedUserUniqueId).exists()) {
                    startActivity(new Intent(PhoneLoginActivity.this, UserDetailActivity.class));
                    finish();
                } else {
                    startActivity(new Intent(PhoneLoginActivity.this, SubmitUserDetailActivity.class));
                    finish();
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }


    public boolean isPhoneNumberValid() {
        return edtPhoneNumber.getText().toString().length() == 10;
    }

}

Here is Code of OtpVerificationActivity.java

package com.androchef.androchef_firebaselearning.phoneauth;

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.CountDownTimer;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.androchef.androchef_firebaselearning.R;
import com.androchef.androchef_firebaselearning.UserDetailActivity;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseException;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.concurrent.TimeUnit;

public class OtpVerificationActivity extends AppCompatActivity {

    FirebaseAuth mAuth = FirebaseAuth.getInstance();
    DatabaseReference mDataBaseRef = FirebaseDatabase.getInstance().getReference();
    PhoneAuthProvider.OnVerificationStateChangedCallbacks mPhoneCallBack;

    EditText edtOTP;
    Button btnVerify;
    TextView tvOtpSentPhoneNumberText, resendOtpText;
    String verificationId, resendToken, phoneNumber;
    ProgressDialog mProgressDialog;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_otp_verification);
        init();
        getDataFromLoginActivity();
        onClicks();
        setOtpTimer();
        setResendFireBasePhoneAuthCallBack();
    }

    private void init() {
        edtOTP = findViewById(R.id.edt_otp);
        btnVerify = findViewById(R.id.btn_verify);
        tvOtpSentPhoneNumberText = findViewById(R.id.tv_otp_sent_phone_number);
        resendOtpText = findViewById(R.id.tv_resend_otp);
        mProgressDialog = new ProgressDialog(this);
    }

    private void getDataFromLoginActivity() {
        phoneNumber = getIntent().getStringExtra("phone_number");
        resendToken = getIntent().getStringExtra("resendToken");
        verificationId = getIntent().getStringExtra("verification_id");
        tvOtpSentPhoneNumberText.setText("OTP sent to  +91" + phoneNumber);
    }

    private void setOtpTimer(){
        new CountDownTimer(30000, 1000) {

            public void onTick(long millisUntilFinished) {
                resendOtpText.setTextColor(getResources().getColor(R.color.grey));
                resendOtpText.setText("Wait : "+millisUntilFinished / 1000);
                resendOtpText.setEnabled(false);
            }

            public void onFinish() {
                resendOtpText.setTextColor(getResources().getColor(R.color.blue));
                resendOtpText.setText("Resend Otp");
                resendOtpText.setEnabled(true);
            }
        }.start();
    }
    private void onClicks() {
        btnVerify.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isOtpEntered()) {
                    mProgressDialog.setTitle("Verifying..");
                    mProgressDialog.setMessage("Please Wait verify entered OTP..");
                    verifyOtp();
                } else {
                    Toast.makeText(OtpVerificationActivity.this, "Please enter 6 digit OTP", Toast.LENGTH_SHORT).show();
                }
            }
        });

        resendOtpText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                    resendOtpToNumber(phoneNumber);
            }
        });

        edtOTP.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_NEXT) {
                    if (isOtpEntered()) {
                        mProgressDialog.setTitle("Verifying..");
                        mProgressDialog.setMessage("Please Wait verify entered OTP..");
                        verifyOtp();
                    } else {
                        Toast.makeText(OtpVerificationActivity.this, "Please enter 6 digit OTP", Toast.LENGTH_SHORT).show();
                    }
                    return true;
                }
                return false;
            }
        });
    }

    private boolean isOtpEntered() {
        return edtOTP.getText().toString().length() == 6;
    }

    private void resendOtpToNumber(String phoneNumber) {
        mProgressDialog.setTitle("Sending..");
        mProgressDialog.setMessage("Please Wait while sending OTP..");
        PhoneAuthProvider.getInstance().verifyPhoneNumber("+91" + phoneNumber
                , 30
                , TimeUnit.SECONDS
                , this
                , mPhoneCallBack);
    }

    private void verifyOtp() {
        String enteredOtp = edtOTP.getText().toString();
        PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(verificationId, enteredOtp);
        loginWithGivenCredential(phoneAuthCredential);
    }

    private void loginWithGivenCredential(PhoneAuthCredential phoneAuthCredential) {
        mAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    Toast.makeText(OtpVerificationActivity.this, "Login Successfully", Toast.LENGTH_SHORT).show();
                    checkUserIsAlreadyExistInDatabase(task);
                } else {
                    Toast.makeText(OtpVerificationActivity.this, "Error in Login :", Toast.LENGTH_SHORT).show();
                    mProgressDialog.hide();
                }
            }
        });
    }

    private void checkUserIsAlreadyExistInDatabase(Task<AuthResult> task) {
        final String loggedUserUniqueId = task.getResult().getUser().getUid();
        mDataBaseRef.child("Users").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if (dataSnapshot.child(loggedUserUniqueId).exists()) {
                    startActivity(new Intent(OtpVerificationActivity.this, UserDetailActivity.class));
                    finish();
                } else {
                    startActivity(new Intent(OtpVerificationActivity.this, SubmitUserDetailActivity.class));
                    finish();
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }

    private void setResendFireBasePhoneAuthCallBack(){
        mPhoneCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                mProgressDialog.hide();
                loginWithGivenCredential(phoneAuthCredential);
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
                mProgressDialog.hide();
                Toast.makeText(OtpVerificationActivity.this, "Error Login :", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(verificationId, forceResendingToken);
                mProgressDialog.hide();
                Toast.makeText(OtpVerificationActivity.this, "OTP sent", Toast.LENGTH_SHORT).show();
                setOtpTimer();
            }
        };
    }
}

if you have problems please let me know in comment section. Thank you for reading this article :- ) .