Secure User Authentication in Android: A Comprehensive Guide [PART 2]

Romman SabbirRomman Sabbir
5 min read

User authentication is one of the most critical security aspects of any mobile application. Implementing secure authentication methods ensures that users’ data and accounts are protected from unauthorized access. In this article, we will dive deep into several techniques for implementing secure user authentication in Android, including Biometric Authentication (Fingerprint and Face), OAuth 2.0 & OpenID Connect, and Secure Password Storage. We’ll provide real-life examples and Kotlin code snippets for each method to help you integrate them into your Android apps.


Biometric API: Secure Authentication with Fingerprint and Face Recognition

Biometric authentication uses physical traits like fingerprints or facial recognition to authenticate users. This provides a more secure and convenient login experience compared to traditional password-based authentication, as biometrics are unique to each individual.

Use Case: Implementing Fingerprint or Face Authentication

You can integrate the BiometricPrompt API in your Android app to allow users to authenticate using their fingerprint or face. This is particularly useful for apps where convenience and security are essential, such as banking apps, medical records, or any app handling sensitive data.

Kotlin Example: Using Biometric Authentication

  1. Add the biometric dependency to your app’s build.gradle file:
implementation 'androidx.biometric:biometric:1.2.0-alpha05'
  1. Implement biometric authentication in your app:
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat

fun showBiometricPrompt() {
    val executor = ContextCompat.getMainExecutor(this)
    val biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            // Authentication succeeded, proceed with login
        }

        override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
            // Handle error
        }

        override fun onAuthenticationFailed() {
            // Handle failure
        }
    })

    val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle("Biometric Authentication")
        .setSubtitle("Use your fingerprint or face to login")
        .setNegativeButtonText("Use password")
        .build()

    biometricPrompt.authenticate(promptInfo)
}

In this example, we use BiometricPrompt to prompt the user to authenticate using a fingerprint or facial recognition. If the biometric data matches, the user is authenticated successfully.

Real-Life Example: Banking App

In a banking app, users can log in using their fingerprint or face for quicker access to their accounts, without needing to type a password each time. This not only enhances security but also improves user experience.


OAuth 2.0 & OpenID Connect: Secure Token-Based Authentication

OAuth 2.0 and OpenID Connect are widely used frameworks for authentication and authorization, especially when an app needs to interact with external services like Google, Facebook, or custom APIs. OAuth 2.0 allows users to grant access to their data on one service without exposing their credentials, while OpenID Connect adds an identity layer on top of OAuth 2.0 for authentication.

Use Case: Logging in with Google or Facebook

Many apps use OAuth 2.0 to allow users to log in using their Google or Facebook accounts, which not only simplifies the login process but also improves security by avoiding the need for password management.

Kotlin Example: Google Sign-In Using OAuth 2.0

  1. Add the Google Sign-In dependency to your build.gradle file:
implementation 'com.google.android.gms:play-services-auth:20.2.0'
  1. Set up Google Sign-In in your app:
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.tasks.Task
import android.content.Intent

private lateinit var googleSignInClient: GoogleSignInClient

fun setupGoogleSignIn() {
    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .requestIdToken(getString(R.string.server_client_id)) // For backend verification
        .build()

    googleSignInClient = GoogleSignIn.getClient(this, gso)
}

fun signInWithGoogle() {
    val signInIntent = googleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == RC_SIGN_IN) {
        val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(data)
        handleSignInResult(task)
    }
}

private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
    try {
        val account = completedTask.getResult(ApiException::class.java)
        // Signed in successfully, handle account data here
    } catch (e: ApiException) {
        // Sign-in failed, handle the error
    }
}

In this example, Google Sign-In is implemented using OAuth 2.0, where the user can log in using their Google account. The Google ID token can be passed to your server for authentication.

Real-Life Example: Social Media Login

In apps like Instagram, users can log in using their Facebook account via OAuth 2.0. This provides a seamless authentication process and enhances security by offloading password management to a trusted provider.


Secure Password Storage: Hashing and Salting Passwords

Storing passwords securely is essential to protect user credentials from being exposed in case of a data breach. Instead of storing plain-text passwords, they should be hashed and salted. Hashing transforms the password into a fixed-length string, while salting adds a random string to each password before hashing it, making it more resistant to attacks like rainbow table attacks.

Use Case: Storing User Passwords Securely

When a user creates an account in your app, their password should be securely hashed and salted before storing it in the database. This ensures that even if the database is compromised, attackers cannot easily recover the original passwords.

Kotlin Example: Password Hashing Using PBKDF2

  1. Hash and salt a password:
import java.security.SecureRandom
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import java.util.Base64

fun hashPassword(password: String, salt: ByteArray): String {
    val spec = PBEKeySpec(password.toCharArray(), salt, 10000, 256) // 10000 iterations
    val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
    val hash = factory.generateSecret(spec).encoded
    return Base64.getEncoder().encodeToString(hash)
}

fun generateSalt(): ByteArray {
    val random = SecureRandom()
    val salt = ByteArray(16)
    random.nextBytes(salt)
    return salt
}

In this example, PBKDF2 (Password-Based Key Derivation Function 2) is used to securely hash and salt the password. You can also use alternatives like bcrypt or Argon2 for even better security.

  1. Verify the password:
fun verifyPassword(inputPassword: String, storedHash: String, salt: ByteArray): Boolean {
    val inputHash = hashPassword(inputPassword, salt)
    return inputHash == storedHash
}

Here, the input password is hashed again with the same salt, and the resulting hash is compared with the stored hash to verify if the password is correct.

Real-Life Example: User Account in E-commerce App

In an e-commerce app, when users create accounts, their passwords should be securely hashed and stored in the database. During login, the app verifies the password by hashing the input again and comparing it with the stored hash, ensuring that user credentials are protected even if the database is compromised.


Conclusion

Implementing secure user authentication is a crucial step in building safe Android applications. The Biometric API enables seamless and secure login experiences using fingerprint or facial recognition. OAuth 2.0 & OpenID Connect simplify user authentication with external services like Google and Facebook while enhancing security with token-based authentication. Finally, securely storing passwords using hashing and salting ensures that even if an attacker gains access to your database, user credentials remain protected.

By following these techniques, you can enhance the security of your Android app and ensure that users' data is kept safe from unauthorized access.


That’s it for today. Happy coding…

0
Subscribe to my newsletter

Read articles from Romman Sabbir directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Romman Sabbir
Romman Sabbir

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.