biometrics
27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.
What it does
Biometric Authentication
Rule 1: Check availability before showing biometric UI
// ✅ Check what's available before prompting
fun BiometricManager.canAuthenticateWithBiometrics(): Boolean {
val authenticators = BIOMETRIC_STRONG or DEVICE_CREDENTIAL
return canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS
}
// ✅ Detailed availability check
sealed interface BiometricAvailability {
data object Available : BiometricAvailability
data object NoHardware : BiometricAvailability
data object NotEnrolled : BiometricAvailability
data object Unavailable : BiometricAvailability
}
fun checkBiometricAvailability(context: Context): BiometricAvailability {
val manager = BiometricManager.from(context)
return when (manager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
BiometricManager.BIOMETRIC_SUCCESS -> BiometricAvailability.Available
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> BiometricAvailability.NoHardware
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> BiometricAvailability.NotEnrolled
else -> BiometricAvailability.Unavailable
}
}
Rule 2: BiometricPrompt — correct setup in ComponentActivity
// ✅ Must be created in Activity, not in Composable
class MainActivity : ComponentActivity() {
private lateinit var biometricPrompt: BiometricPrompt
private lateinit var promptInfo: BiometricPrompt.PromptInfo
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupBiometrics()
setContent { MyApp(onAuthenticateClick = ::showBiometricPrompt) }
}
private fun setupBiometrics() {
val executor = ContextCompat.getMainExecutor(this)
biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
// Handle success — result.cryptoObject available if using CryptoObject
viewModel.onBiometricSuccess()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode != BiometricPrompt.ERROR_USER_CANCELED &&
errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
viewModel.onBiometricError(errString.toString())
}
}
override fun onAuthenticationFailed() {
// Biometric recognized but not matched — DO NOT lock out here
// BiometricPrompt handles lockout automatically
}
})
promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Authenticate")
.setSubtitle("Use your biometric to unlock the app")
.setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
// Don't set setNegativeButtonText when DEVICE_CREDENTIAL is allowed
.build()
}
fun showBiometricPrompt() {
biometricPrompt.authenticate(promptInfo)
}
}
Rule 3: Crypto-backed biometrics for secure key operations
// ✅ Biometric-protected Keystore key
object BiometricCryptoHelper {
private const val KEY_NAME = "biometric_key"
fun generateKey() {
val keyGen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGen.init(
KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG)
.setInvalidatedByBiometricEnrollment(true)
.build()
)
keyGen.generateKey()
}
fun getCipher(): Cipher {
val key = KeyStore.getInstance("AndroidKeyStore").run {
load(null)
getKey(KEY_NAME, null) as SecretKey
}
return Cipher.getInstance("${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_CBC}/${KeyProperties.ENCRYPTION_PADDING_PKCS7}").apply {
init(Cipher.ENCRYPT_MODE, key)
}
}
}
// Authenticate with CryptoObject for maximum security
fun showSecureBiometricPrompt() {
val cipher = BiometricCryptoHelper.getCipher()
biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
}
Common Mistakes
❌ Creating BiometricPrompt in Composable — create in Activity, pass lambda to Compose
❌ Setting setNegativeButtonText when DEVICE_CREDENTIAL is allowed — crash
❌ Not checking availability before showing prompt — shows broken UI on unsupported devices
❌ Locking user out on onAuthenticationFailed — BiometricPrompt handles lockout
❌ Using BIOMETRIC_WEAK for sensitive operations — use BIOMETRIC_STRONG with CryptoObject
Capabilities
Install
Quality
deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (5,243 chars)