Android Biometric Auth & ProGuard R8 — Security Best Practices
Building secure Android apps requires understanding both authentication mechanisms and code obfuscation. This guide covers BiometricPrompt for fingerprint/face authentication and ProGuard/R8 configuration for production builds.
Part 1: BiometricPrompt Setup
1. Add Dependencies
dependencies {
implementation "androidx.biometric:biometric:1.2.0-alpha05"
}
2. Check BiometricManager Availability
Always verify device capabilities before showing the prompt:
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators
val biometricManager = BiometricManager.from(context)
val canAuthenticate = biometricManager.canAuthenticate(
Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL
)
when (canAuthenticate) {
BiometricManager.BIOMETRIC_SUCCESS -> {
// Device supports biometric authentication
showBiometricPrompt()
}
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
// No biometric hardware available
showPasswordAuth()
}
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
// Hardware temporarily unavailable
showRetryButton()
}
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
// User hasn't enrolled biometric data
showEnrollmentPrompt()
}
}
3. Display BiometricPrompt
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import java.util.concurrent.Executor
val executor: Executor = ContextCompat.getMainExecutor(context)
val biometricPrompt = BiometricPrompt(
activity,
executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
// Authentication successful
handleAuthSuccess(result)
}
override fun onAuthenticationError(
errorCode: Int,
errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
// Authentication error (user canceled, etc.)
handleAuthError(errorCode, errString)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
// Biometric rejected (wrong finger, face mismatch)
handleAuthFailed()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Authenticate")
.setSubtitle("Use your fingerprint or face to continue")
.setNegativeButtonText("Cancel")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG or
BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
.build()
biometricPrompt.authenticate(promptInfo)
4. DEVICE_CREDENTIAL Fallback
The DEVICE_CREDENTIAL authenticator allows PIN/pattern/password as fallback:
setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG or
BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
When used, the system shows biometric first; if failed, user can switch to PIN/password without leaving the prompt.
5. CryptoObject for Encryption
For sensitive operations (payments, access tokens), tie authentication to encryption:
import javax.crypto.Cipher
import java.security.KeyStore
fun createCryptoObject(): BiometricPrompt.CryptoObject? {
return try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" +
KeyProperties.BLOCK_MODE_CBC + "/" +
KeyProperties.ENCRYPTION_PADDING_PKCS7
)
cipher.init(
Cipher.ENCRYPT_MODE,
keyStore.getKey("biometric_key", null)
)
BiometricPrompt.CryptoObject(cipher)
} catch (e: Exception) {
null
}
}
// Use in authentication
val cryptoObject = createCryptoObject()
biometricPrompt.authenticate(promptInfo, cryptoObject)
// After success, get cipher from result
val cipher = result.cryptoObject?.cipher
val encryptedData = cipher?.doFinal(sensitiveData)
Part 2: ProGuard/R8 Configuration
1. Enable Code Obfuscation
In your app's build.gradle:
android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
-
minifyEnabled true: Enables R8/ProGuard obfuscation -
shrinkResources true: Removes unused resources -
proguardFiles: Specifies keep rules
2. Keep Rules for Popular Libraries
Create/update proguard-rules.pro:
# Gson
-keep class com.google.gson.** { *; }
-keepclassmembers class ** {
@com.google.gson.annotations.SerializedName <fields>;
}
# Retrofit
-keep interface retrofit2.** { *; }
-keep class retrofit2.** { *; }
-keepattributes Exceptions
# Room Database
-keep class androidx.room.** { *; }
-keep @androidx.room.Entity class * { *; }
-keep @androidx.room.Dao interface *
# Kotlin Serialization
-keep class kotlinx.serialization.** { *; }
# Your app's data classes (if using reflection)
-keep class com.example.myapp.models.** { *; }
# BiometricPrompt callback
-keepclassmembers class ** {
*** onAuthenticationSucceeded(...);
*** onAuthenticationError(...);
*** onAuthenticationFailed(...);
}
3. Understanding isMinifyEnabled & isShrinkResources
| Setting | Effect |
|---|---|
minifyEnabled: true |
Obfuscates class/method/field names, removes dead code |
shrinkResources: true |
Removes unused resources (layouts, strings, drawables) |
Both false
|
Debug builds with full debugging info |
Both true
|
Smallest APK, but requires proper keep rules |
4. Using mapping.txt for Debugging
After a release build, R8 generates mapping.txt:
Path: app/build/outputs/mapping/release/mapping.txt
Example content:
com.example.myapp.login.LoginActivity → com.example.myapp.login.a:
void onLoginClick() → a()
When analyzing crash logs, use retrace:
./gradlew retrace -mapping app/build/outputs/mapping/release/mapping.txt crash_stacktrace.txt
This unmasks the obfuscated names back to original class/method names.
5. ProGuard Full Mode (Advanced)
For maximum obfuscation, add to build.gradle:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
// Or in proguard-rules.pro:
-optimizationpasses 5
-dontusemixedcaseclassnames
-verbose
-renamesourcefileattribute SourceFile
Full mode performs aggressive optimizations:
- Method inlining
- Class merging
- Field removal
- Unused branch elimination
6. Testing ProGuard Locally
Enable obfuscation in debug builds to catch issues early:
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Build and test thoroughly before releasing.
Security Checklist
✓ BiometricPrompt with BIOMETRIC_STRONG or DEVICE_CREDENTIAL
✓ CryptoObject for sensitive operations
✓ canAuthenticate() check before showing prompt
✓ Proper error handling for all callbacks
✓ ProGuard/R8 enabled in release builds
✓ Keep rules for serialization libraries (Gson, Retrofit, Room)
✓ mapping.txt stored safely for crash analysis
✓ Tested locally with minifyEnabled=true
Get Started
Ready to build secure Android apps? Explore 8 Android App Templates with biometric auth, ProGuard configs, and more:
8 Android App Templates → https://myougatheax.gumroad.com
Templates include:
- BiometricPrompt integration
- Room + Retrofit setup with keep rules
- Proper ProGuard configuration
- Example crash log analysis with mapping.txt
Top comments (0)