Secure SMS Verification in Android Applications with Firebase

Emre Karataş
6 min readJun 24, 2024

--

In this article, I will explain step-by-step how you can use Firebase’s SMS verification feature for user authentication in your Android application. SMS verification is an effective method for verifying users’ phone numbers and ensuring secure login. In this example, we will create a project and go through all the necessary steps.

1. Creating a Firebase Project

Firebase Console Settings

Log in to Firebase Console:

Create a New Project:

  • Click on the “Add Project” button in the Firebase Console.
  • Enter a name for your project and click “Continue.”
  • You might be asked to enable Google Analytics. Check the option according to your preference and click “Continue.”

Complete the Project Setup:

  • Once the project creation process is complete, click on the “Manage Project” button to go to your project page.
  • Enable the App Check option and register your app in this section. Using the Play Integrity API to enable Firebase App Check will enhance your app’s security and prevent unauthorized access to Firebase services.

2. Adding Your Android App to Firebase

Add Your App to Firebase:

  • In your project page, click on the Android icon in the “Project Overview” section at the top.
  • Enter the package name (e.g., com.ek.firebasesmssample), app nickname, and SHA-1 certificate fingerprint. To get the SHA-1 certificate fingerprint, follow these steps:
  • Open the terminal in Android Studio and enter the following command:
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
  • The SHA1 value in the output is your certificate fingerprint.
  • Click the “Register App” button.

Download the Google Services JSON File:

  • Download the google-services.json file and add it to the app directory of your Android project.

3. Adding the Firebase SDK to Your Project

Configure Your Gradle Files:

  • Add the following line to your project-level build.gradle file:
classpath 'com.google.gms:google-services:4.3.3'

Add the following lines to your app-level build.gradle file:

apply plugin: 'com.google.gms.google-services'

dependencies {
implementation platform('com.google.firebase:firebase-bom:26.1.0')
implementation 'com.google.firebase:firebase-auth'
implementation 'com.google.firebase:firebase-analytics'
}

4. Enabling Firebase Authentication

Set Up Authentication:

  • Go to the “Authentication” section in the left menu of the Firebase Console.
  • Click on the “Sign-in Method” tab.
  • Find the phone number sign-in method and click “Enable.”
  • Optionally, you can customize the message templates.
  • Click the “Save” button.

5. Coding the Android Application

Project Structure and UI Design

First, create your Android project and set up the necessary files.

activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your phone number for SMS verification"
android:textSize="12sp" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/phone_number_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="128dp">

<EditText
android:id="@+id/phone_number_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone number"
android:inputType="phone" />
</com.google.android.material.textfield.TextInputLayout>

<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:paddingVertical="12dp"
android:textSize="16sp"
android:textColor="@color/white"
android:backgroundTint="@color/red"
android:text="Login →" />

</LinearLayout>

LoginActivity.kt

Now, create the LoginActivity class and add the following code:

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.doAfterTextChanged
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseException
import com.google.firebase.appcheck.FirebaseAppCheck
import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.PhoneAuthCredential
import com.google.firebase.auth.PhoneAuthProvider
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern

class LoginActivity : AppCompatActivity() {

private lateinit var phoneNumberInput: EditText
private lateinit var loginButton: Button
private lateinit var phoneNumberLayout: TextInputLayout

private lateinit var auth: FirebaseAuth

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)

phoneNumberInput = findViewById(R.id.phone_number_input)
loginButton = findViewById(R.id.login_button)
phoneNumberLayout = findViewById(R.id.phone_number_layout)

auth = FirebaseAuth.getInstance()
FirebaseApp.initializeApp(this)
val firebaseAppCheck = FirebaseAppCheck.getInstance()
firebaseAppCheck.installAppCheckProviderFactory(
PlayIntegrityAppCheckProviderFactory.getInstance()
)
phoneNumberInput.addTextChangedListener(object : TextWatcher {
private var current = ""
override fun afterTextChanged(s: Editable?) {
if (s.toString() != current) {
phoneNumberInput.removeTextChangedListener(this)

val cleanString = s.toString().replace(" ", "").replace("+90", "")
val formatted = cleanString.chunked(3).joinToString(" ")

if (cleanString.length > 6) {
val part1 = cleanString.substring(0, 3)
val part2 = cleanString.substring(3, 6)
val part3 = cleanString.substring(6)
current = "+90 $part1 $part2$part3"
} else {
current = "+90 $formatted"
}

phoneNumberInput.setText(current)
phoneNumberInput.setSelection(current.length)

phoneNumberInput.addTextChangedListener(this)
}
}

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})

loginButton.setOnClickListener {
val phoneNumber = phoneNumberInput.text.toString()
if (isValidPhoneNumber(phoneNumber)) {
startPhoneNumberVerification(phoneNumber)
} else {
Toast.makeText(this, "Please enter a valid phone number", Toast.LENGTH_SHORT).show()
}
}
}

private fun isValidPhoneNumber(phoneNumber: String): Boolean {
val pattern = Pattern.compile("^\\+?[1-9]\\d{1,14}\$")
return pattern.matcher(phoneNumber.replace(" ", "")).matches()
}

private fun startPhoneNumberVerification(phoneNumber: String) {
val callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

override fun onVerificationCompleted(credential: PhoneAuthCredential) {
// This callback is triggered when verification is successful.
// You can perform the sign-in operation here.
signInWithPhoneAuthCredential(credential)
}

override fun onVerificationFailed(e: FirebaseException) {
// This callback is triggered when verification fails.
// Display an error message.
Log.e("onVerificationFailed", e.message ?: "An error occurred")
}

override fun onCodeSent(
verificationId: String,
token: PhoneAuthProvider.ForceResendingToken
) {
Log.e("onCodeSent", "verificationId: $verificationId")
// This callback is triggered when the verification code is sent.
// You can create a credential using the received code.
val credential = PhoneAuthProvider.getCredential(verificationId, "123456")
signInWithPhoneAuthCredential(credential)
}
}

// Start the verification process with the phone number and callbacks.
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number
60, // Timeout duration
TimeUnit.SECONDS, // Timeout unit
this, // Activity (or Fragment)
callbacks
)
}

private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success
val user = task.result?.user
val uid = user?.uid // User ID
val phoneNumber = user?.phoneNumber // Phone number
val providerId = user?.providerId // Provider ID

sendUserToBackend(uid)
} else {
Log.e("signInWithCredential", "signInWithCredential:failure", task.exception)
// Sign in failed
}
}
}

private fun sendUserToBackend(uid: String?) {
Log.e("sendUserToBackend", "UID: $uid")
// This function can be used to send the user's uid to the backend.
}
}

In the startPhoneNumberVerification method, the events that will be triggered during the phone number verification process are defined using callbacks such as onVerificationCompleted, onVerificationFailed, and onCodeSent.

The signInWithPhoneAuthCredential method performs the sign-in operation using Firebase Authentication. If the verification is successful, user information can be retrieved and sent to the backend.

After completing all these steps, you will receive an SMS from Firebase. Once you enter the code received via SMS, the signInWithPhoneAuthCredential method will verify it, and the application flow will continue.

Conclusion

By following the steps outlined in this article, you should now be able to implement secure SMS verification in your Android applications using Firebase. This method not only enhances the security of user authentication but also provides a seamless login experience for your users.

Implementing SMS verification can significantly reduce unauthorized access and ensure that only legitimate users can log in to your app. Additionally, Firebase’s robust features and ease of integration make it a powerful tool for managing authentication and security.

Note: During the development phase, the reCaptcha service might be automatically enabled by Google, and you may see a reCaptcha screen while performing SMS verification. I didn’t want to elaborate on this to avoid deviating from the topic. You can find more detailed information about it here.

Note-2: It is currently not possible to customize the template of the messages sent via Firebase. If you want to use customized message types, you should use a different provider.

I hope this article helps you implement SMS verification in your Android application using Firebase. If you have any questions or need further assistance, feel free to leave a comment below.

“Don’t watch the clock; do what it does. Keep going.” — Sam Levenson

--

--