Phone Authentication in Android with Firebase: Complete Tutorial (2026)
Last Updated: | Author: Deep Singh
Phone number authentication is one of the most convenient login methods for mobile apps. Users type their phone number, receive an SMS with a one-time password (OTP), enter it, and they're logged in — no password to remember, no email verification loop.
In this tutorial, you'll implement Firebase Phone Authentication in Android from scratch. We'll walk through creating a Firebase project, enabling phone sign-in, wiring up the Gradle dependencies, and building a working login screen with OTP verification.
Output Screens
The final app has a login screen with a mobile number input and an OTP input field, plus a home page that shows the signed-in user and a logout button.
Creating a Firebase Project
Open the Firebase Console and sign in with your Google account. Click Add Project, give it a name (e.g., Phone Authentication), and follow the wizard.
Once the project is created, register your Android app by entering the package name from your AndroidManifest.xml. Download the generated google-services.json file — you'll need to place it in your project shortly.
Enabling Phone Authentication
In the Firebase Console, navigate to Authentication → Sign-in method. Enable the Phone sign-in provider. If you want, you can also enable Email/Password or other providers at the same time.
Important: Firebase requires your app's SHA-1 fingerprint to enable phone authentication. You can get it by running ./gradlew signingReport in Android Studio's Terminal, then copy the SHA-1 value into your Firebase project settings.
Configuring Android Studio
In your app-level build.gradle, add the Firebase Auth dependency:
dependencies {
// Use the Firebase BoM so all Firebase libraries align
implementation platform('com.google.firebase:firebase-bom:33.5.1')
implementation 'com.google.firebase:firebase-auth'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
And apply the Google Services plugin at the bottom of the file:
apply plugin: 'com.google.gms.google-services'
Also make sure your project-level build.gradle includes the Google Services classpath:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.2'
}
}
Place google-services.json inside app/ in your project, and sync Gradle.
Note on versions: The original tutorial used firebase-auth:16.0.5. That version is several years old and doesn't work with modern Flutter or Firebase BoM setups. Use the BoM approach shown above.
Layouts
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/back_document" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/ll"
android:layout_centerHorizontal="true"
android:layout_marginBottom="70dp"
android:text="Phone Authentication"
android:textColor="#fff"
android:textSize="30sp"
android:textStyle="bold"
android:typeface="serif" />
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<EditText
android:id="@+id/mymobile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/button"
android:hint="Enter Mobile Number"
android:inputType="number"
android:maxLength="10"
android:padding="20dp"
android:textColor="#fff"
android:textColorHint="#fff" />
<EditText
android:id="@+id/myotp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/button"
android:hint="OTP Number"
android:inputType="number"
android:maxLength="6"
android:padding="20dp"
android:textColor="#fff"
android:textColorHint="#fff" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="50dp"
android:layout_marginRight="20dp"
android:background="@drawable/button"
android:onClick="generate"
android:text="Generate"
android:textAllCaps="false"
android:textColor="#fff"
android:textStyle="bold"
android:typeface="serif" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/button"
android:onClick="verify"
android:text="Verify & Login"
android:textAllCaps="false"
android:textColor="#fff"
android:textStyle="bold"
android:typeface="serif" />
</LinearLayout>
</RelativeLayout>
activity_home_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/back_document" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:padding="20dp"
android:text="Welcome To Home Page"
android:textColor="#fff"
android:textSize="20sp"
android:textStyle="italic"
android:typeface="serif" />
<TextView
android:id="@+id/mytext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerInParent="true"
android:padding="20dp"
android:textColor="#fff"
android:textSize="20sp"
android:typeface="serif" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp"
android:background="@drawable/button"
android:onClick="logout"
android:text="Logout"
android:textColor="#fff"
android:textStyle="bold" />
</RelativeLayout>
drawable/button.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="#fff" />
<corners
android:bottomRightRadius="10dp"
android:topLeftRadius="10dp" />
</shape>
MainActivity.java
package com.example.phoneauthentication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
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.FirebaseUser;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private EditText tmobile, totp;
private PhoneAuthProvider.OnVerificationStateChangedCallbacks myCallbacks;
private FirebaseAuth mAuth;
private String verificationCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tmobile = findViewById(R.id.mymobile);
totp = findViewById(R.id.myotp);
firebaseLogin();
}
/**
* Called by the "Generate" button. Sends an OTP to the entered
* phone number.
*/
public void generate(View view) {
String phone = tmobile.getText().toString().trim();
if (phone.isEmpty() || phone.length() < 10) {
Toast.makeText(this, "Enter a valid 10-digit mobile number", Toast.LENGTH_SHORT).show();
return;
}
PhoneAuthProvider.getInstance().verifyPhoneNumber(
"+91" + phone, // Phone number with country code
60, // Timeout duration
TimeUnit.SECONDS, // Timeout unit
this, // Activity for callback
myCallbacks // Callback handler
);
}
/**
* Called by the "Verify & Login" button. Verifies the code entered
* by the user against the code Firebase sent via SMS.
*/
public void verify(View view) {
String otp = totp.getText().toString().trim();
if (otp.isEmpty()) {
Toast.makeText(this, "Enter the OTP", Toast.LENGTH_SHORT).show();
return;
}
PhoneAuthCredential credential =
PhoneAuthProvider.getCredential(verificationCode, otp);
signInWithPhone(credential);
}
private void firebaseLogin() {
mAuth = FirebaseAuth.getInstance();
myCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@Override
public void onVerificationCompleted(@NonNull PhoneAuthCredential credential) {
// Auto-retrieval succeeded — sign in directly
Log.d("PhoneAuth", "Auto-verification succeeded");
totp.setText(credential.getSmsCode());
signInWithPhone(credential);
}
@Override
public void onVerificationFailed(@NonNull FirebaseException e) {
Log.e("PhoneAuth", "Verification failed", e);
Toast.makeText(MainActivity.this,
"Verification failed: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
@Override
public void onCodeSent(@NonNull String s,
@NonNull PhoneAuthProvider.ForceResendingToken token) {
super.onCodeSent(s, token);
verificationCode = s;
Log.d("PhoneAuth", "OTP sent. verificationId = " + s);
Toast.makeText(MainActivity.this,
"OTP sent to your phone",
Toast.LENGTH_SHORT).show();
}
};
}
private void signInWithPhone(PhoneAuthCredential credential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this,
"Signed in successfully",
Toast.LENGTH_SHORT).show();
goToHomePage();
} else {
Toast.makeText(MainActivity.this,
"Sign-in failed: " + task.getException(),
Toast.LENGTH_LONG).show();
}
}
});
}
@Override
protected void onStart() {
super.onStart();
FirebaseUser user = mAuth.getCurrentUser();
if (user != null) {
goToHomePage();
}
}
private void goToHomePage() {
startActivity(new Intent(MainActivity.this, HomePage.class));
finish();
}
}
HomePage.java
package com.example.phoneauthentication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
public class HomePage extends AppCompatActivity {
private FirebaseAuth mAuth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_page);
mAuth = FirebaseAuth.getInstance();
TextView ttext = findViewById(R.id.mytext);
FirebaseUser user = mAuth.getCurrentUser();
if (user != null) {
String displayName = user.getDisplayName() != null
? user.getDisplayName()
: "User";
String email = user.getEmail() != null
? user.getEmail()
: "No email";
String phone = user.getPhoneNumber() != null
? user.getPhoneNumber()
: "No phone";
ttext.setText(displayName + "\n" + email + "\n" + phone);
}
}
public void logout(View view) {
mAuth.signOut();
startActivity(new Intent(this, MainActivity.class));
finish();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.phoneauthentication">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".HomePage" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Common Pitfalls
- Missing SHA-1 fingerprint. Without it, Firebase will refuse to send an OTP. Register both your debug and release SHA-1 in the Firebase Console.
- Test numbers during development. Firebase lets you add test phone numbers and codes in the Console → Authentication → Sign-in method → Phone → Phone numbers for testing. Use them to avoid hitting SMS quotas.
- Wrong phone number format. Firebase needs the country code (e.g.,
+91for India). If you forget it, verification will fail silently. - Auto-retrieval can fire on emulators. The
onVerificationCompletedcallback may fire before you've entered anything, so it's safe to also fill the OTP field automatically (as shown). - Expired verification IDs. The
verificationIdreturned inonCodeSentis only valid for a limited time (typically the timeout you set). If you wait too long, verification fails. - Using old Firebase SDKs. The
firebase-auth:16.0.5version from 2018 tutorials is not compatible with current Android Gradle Plugin versions. Use the Firebase BoM approach. - Forgetting to call
Firebase.initializeApp(). The plugin auto-initialises fromgoogle-services.json, but if you've moved to a manual setup, you must call it explicitly in your Application class. - Not handling the
onStartauto-login check. If a user is already signed in, they should skip the login screen. The example does this inonStart.
Wrapping Up
You now have a working Firebase Phone Authentication flow in Android. Users enter their phone number, Firebase sends an OTP, they verify it, and they're logged in. The HomePage shows the signed-in user and includes a logout button.
From here, you can extend the app with:
- Resend OTP. Use the
ForceResendingTokenfromonCodeSentto allow resending without a full restart. - Multiple auth providers. Combine phone auth with Google Sign-In or email/password for a complete authentication system.
- Firestore integration. Store user profile data linked to the Firebase UID after sign-in.
- Modern UI. Replace the plain EditText/Button layout with Material 3 components or Jetpack Compose for a cleaner look.
- Test phone numbers. Add fake numbers in the Firebase Console so you can test freely without burning SMS quota.
1 comment:
Wow sir , it's very important project
Post a Comment