October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk9 min

Building a Lightweight Biometric Authentication Library for React Native with Kotlin

How to wrap AndroidX BiometricPrompt in a small Kotlin React Native module, with a stable result contract, device-credential fallback decisions, and honest limits on security and "lightweight" claims.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Android half of a React Native biometric library is a thin native module. It exposes two or three methods to JavaScript, hands the real work to AndroidX BiometricPrompt in Kotlin, and translates every native callback into one stable result shape. “Lightweight” should come from what you leave out: no custom UI, no hidden state, no business logic. This guide covers that design, the Kotlin code, the fallback decisions, and the limits of what a successful prompt proves.

This is implementation guidance based on Android’s documentation and on two existing open-source libraries. It is not a report of hands-on device testing, and no size or speed figures are claimed.

Decide first: UI gate or cryptographic proof?

Everything else follows from this choice, so make it before writing code.

  • Prompt-only gate. The app asks the system to verify the user locally and receives success or failure. This is fine for revealing a screen, confirming a sensitive in-app action, or unlocking locally cached data. It does not prove anything to your server.
  • Key-backed operation. Android’s prompt has an authenticate overload that takes a CryptoObject, so a keystore key can be usable only after authentication. A server login needs a deliberate design: a native keystore-backed key pair, a server-issued challenge, a signature produced after authentication, and server-side verification.

The SelfLender react-native-biometrics library takes this split seriously. It describes public/private keys held in native keystores, protected by biometrics, with signatures created after authentication. It also offers a separate simplePrompt for gating in-app actions, and its documentation cautions that a prompt-only result should not be used as server login authentication. Adopt the same wording in your own docs: “the device accepted local verification” is not “the backend authenticated this account.”

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Optical Fingerprint Reader Sensor AS608 Green Light Fingerprint Recognition Module for Arduino 51 AVR STM32 ESP8266
  • Document link: https://tinyurl(DOT)com/Fringerprint-Sensor
  • Storage Capacity: 240 fingerprints
  • This module can be controlled through the serial port, or using the computer's serial port
  • The product consists of optical fingerprint sensor, high-speed DSP processor, high-performance fingerprint matching algorithm, ultra-large capacity FLASH chip and other hardware and software
  • This fingerprint module has stable performance, complete functions, and has multiple functions such as fingerprint collection, fingerprint registration, fingerprint matching, and fingerprint search

The rest of this article builds the prompt-only gate, which is the smallest useful library. Key-backed signing is a separate feature with its own key-management, invalidation, and enrollment-change questions, and you should not imply a simple prompt provides it.

What Android gives you, and which API to use

Android’s framework BiometricPrompt is documented as “a class that manages a system-provided biometric dialog” and requires API 28 (Android 9). The AndroidX BiometricPrompt wraps it: per Android Developers, it uses the system prompt on Android 9 and later and a custom fingerprint dialog on earlier supported versions. For a library that must run on a range of React Native app configurations, the AndroidX class is the practical choice because the compatibility handling is not yours to maintain.

Question Framework android.hardware.biometrics.BiometricPrompt AndroidX androidx.biometric.BiometricPrompt
Minimum Android API 28 System prompt on API 28+, custom fingerprint dialog on earlier supported versions (Android Developers)
Dialog System-provided System-provided on API 28+; library-provided before
Maintenance burden for you You handle version differences Library handles them; you pin and test a dependency version

Check the current AndroidX Biometric artifact version and its supported-OS notes when you implement; this article does not pin one.

One behavior matters for your contract: AndroidX states that “for security reasons, the prompt will be dismissed when the client application is no longer in the foreground.” Your module must treat that dismissal as a normal outcome, not an edge case.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Design the JavaScript contract

Keep the surface to three calls: check availability, authenticate, cancel. Return outcomes as data instead of throwing for expected situations such as a user pressing cancel. Reserve rejected promises for programmer errors (no foreground activity, bad options).

Rank #2
EC Buying ZW101 Fingerprint Recognition Module Fingerprint Scanner Low-Power Finger Detection Capacitive Semiconductor Fingerprint Sensor Fingerprint Reader
  • Advanced ZW101 Fingerprint Recognition Module with low-power finger detection technology for high accuracy in fingerprint scanning and identification
  • Features a capacitive semiconductor fingerprint sensor with a protective coating, RGB LED lights, and UART interface for reliable fingerprint reading
  • Securely store up to 50 fingerprint features with ESD protection exceeding 15KV, ensuring top-notch security for applications like fingerprint door locks and safes
  • Lightning-fast response time with feature extraction in under 0.06 seconds and a false acceptance rate (FAR) below 1/1000000 for seamless identity verification
  • Perfect for a wide range of industries including finance, security, and management, offering a versatile solution for access control systems, POS terminals, and time attendance machines
// index.ts
import { NativeModules } from 'react-native';

export type Availability =
  | 'available'
  | 'none_enrolled'
  | 'no_hardware'
  | 'hardware_unavailable'
  | 'security_update_required'
  | 'unsupported';

export type AuthResult =
  | { success: true }
  | { success: false; reason:
      | 'user_canceled' | 'negative_button' | 'lockout'
      | 'lockout_permanent' | 'not_available' | 'timeout'
      | 'interrupted' | 'superseded' | 'error'; message?: string };

export interface AuthOptions {
  title: string;
  subtitle?: string;
  cancelLabel?: string;          // required when device credential is NOT allowed
  allowDeviceCredential?: boolean;
}

const Native = NativeModules.LightBiometrics;

export const getAvailability = (allowDeviceCredential = false): Promise<Availability> =>
  Native.getAvailability(allowDeviceCredential);
export const authenticate = (o: AuthOptions): Promise<AuthResult> => Native.authenticate(o);
export const cancel = (): void => Native.cancel();

Write the Kotlin module

Dependencies

Add the AndroidX Biometric artifact to the library’s build.gradle (use the current stable version from Android Developers’ release notes):

dependencies {
    implementation "androidx.biometric:biometric:<current-stable-version>"
}

The prompt needs a FragmentActivity. React Native’s default MainActivity extends ReactActivity, which is a AppCompatActivity and therefore qualifies, but check this for apps with custom activities. The Android reference lists the USE_BIOMETRIC permission for the relevant operations; declare it in the library manifest and confirm against current documentation for the API levels you target.

Availability check

private fun authenticators(allowCredential: Boolean) =
    if (allowCredential) BIOMETRIC_STRONG or DEVICE_CREDENTIAL else BIOMETRIC_STRONG

@ReactMethod
fun getAvailability(allowCredential: Boolean, promise: Promise) {
    val status = BiometricManager.from(reactContext)
        .canAuthenticate(authenticators(allowCredential))
    promise.resolve(when (status) {
        BiometricManager.BIOMETRIC_SUCCESS -> "available"
        BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> "none_enrolled"
        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> "no_hardware"
        BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> "hardware_unavailable"
        BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> "security_update_required"
        else -> "unsupported"
    })
}

Treat availability as advisory. State can change between the check and the prompt (a user can remove their enrollment), so authenticate must still handle failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Authenticate with a single in-flight attempt

The most common bug in bridge code is a promise that never settles. Guard against it with one pending slot and a settle-once helper.

class LightBiometricsModule(private val ctx: ReactApplicationContext) :
    ReactContextBaseJavaModule(ctx), LifecycleEventListener {

    override fun getName() = "LightBiometrics"

    private class Pending(val promise: Promise, val prompt: BiometricPrompt) {
        private val done = AtomicBoolean(false)
        fun settle(map: WritableMap) { if (done.compareAndSet(false, true)) promise.resolve(map) }
    }
    private val pending = AtomicReference<Pending?>(null)

    init { ctx.addLifecycleEventListener(this) }

    @ReactMethod
    fun authenticate(opts: ReadableMap, promise: Promise) {
        val activity = ctx.currentActivity as? FragmentActivity
            ?: return promise.reject("E_NO_ACTIVITY", "No foreground FragmentActivity")
        val allowCred = opts.hasKey("allowDeviceCredential") && opts.getBoolean("allowDeviceCredential")

        activity.runOnUiThread {
            // Supersede any earlier attempt so its promise is settled, not leaked.
            pending.getAndSet(null)?.let { old ->
                old.settle(failure("superseded"))
                old.prompt.cancelAuthentication()
            }

            val executor = ContextCompat.getMainExecutor(activity)
            lateinit var current: Pending
            val callback = object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(r: BiometricPrompt.AuthenticationResult) {
                    pending.compareAndSet(current, null)
                    current.settle(Arguments.createMap().apply { putBoolean("success", true) })
                }
                override fun onAuthenticationError(code: Int, msg: CharSequence) {
                    pending.compareAndSet(current, null)
                    current.settle(failure(mapError(code), msg.toString()))
                }
                // onAuthenticationFailed = one bad attempt; the prompt stays open. Do not settle.
            }
            val prompt = BiometricPrompt(activity, executor, callback)
            current = Pending(promise, prompt)
            pending.set(current)

            val info = BiometricPrompt.PromptInfo.Builder()
                .setTitle(opts.getString("title") ?: "Authenticate")
                .apply { opts.getString("subtitle")?.let(::setSubtitle) }
                .setAllowedAuthenticators(authenticators(allowCred))
                .apply {
                    // A negative button is required without device credential, and not allowed with it.
                    if (!allowCred) setNegativeButtonText(opts.getString("cancelLabel") ?: "Cancel")
                }
                .build()
            prompt.authenticate(info)
        }
    }

    @ReactMethod
    fun cancel() {
        pending.get()?.let { p ->
            ctx.currentActivity?.runOnUiThread { p.prompt.cancelAuthentication() }
        }
    }

    override fun onHostPause() {
        // The prompt is dismissed when the app leaves the foreground; make sure JS hears about it.
        pending.getAndSet(null)?.settle(failure("interrupted"))
    }
    override fun onHostResume() {}
    override fun onHostDestroy() { pending.getAndSet(null)?.settle(failure("interrupted")) }
}

Two notes on this sketch. First, onAuthenticationFailed (a non-matching fingerprint or face) is not terminal in the prompt, so it deliberately does nothing; resolving there would end an attempt the user is still making. Second, depending on the activity and prompt behavior, onHostPause can fire for reasons other than the prompt (for example, the system dialog itself on some devices). Verify on real devices that your pause handling does not settle a prompt that is still legitimately showing; if it does, rely on the error callback and settle only in onHostDestroy.

Rank #3
Geekstory Optical Fingerprint Reader Sensor Module Door Lock Access Control Red Light for Arduino Mega2560 UNO R3
  • Optical fingerprint sensor secure your project with biometrics. This fingerprint module can be used for fingerprint collection, fingerprint registration, fingerprint comparison and fingerprint search, it's easy to use, so its perfect for any project
  • Fingerprint sensor module can work with any microcontroller which with serial port: such as compatible with arduino, 51, avr, stm32, pic, arm, msp430
  • Package Includes:1 X Optical Fingerprint Reader Sensor, 2 X Cable. You can enroll new fingers directly - up to 240 finger prints can be stored
  • Applications: Fingerprint door locks, safes, guns, financial and other security areas; Access control systems, industrial computers, POS machines, driving training, attendance and other areas of identity; fingerprint payment and other financial areas
  • The fingerprint moudle documentation link cannot be displayed. If you need technical documentation, please click “Geekstory” to em-ail us

Map native errors to your stable reasons

private fun mapError(code: Int) = when (code) {
    BiometricPrompt.ERROR_USER_CANCELED -> "user_canceled"
    BiometricPrompt.ERROR_NEGATIVE_BUTTON -> "negative_button"
    BiometricPrompt.ERROR_CANCELED -> "interrupted"
    BiometricPrompt.ERROR_LOCKOUT -> "lockout"
    BiometricPrompt.ERROR_LOCKOUT_PERMANENT -> "lockout_permanent"
    BiometricPrompt.ERROR_TIMEOUT -> "timeout"
    BiometricPrompt.ERROR_NO_BIOMETRICS,
    BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL,
    BiometricPrompt.ERROR_HW_NOT_PRESENT,
    BiometricPrompt.ERROR_HW_UNAVAILABLE -> "not_available"
    else -> "error"
}

private fun failure(reason: String, message: String? = null) =
    Arguments.createMap().apply {
        putBoolean("success", false)
        putString("reason", reason)
        message?.let { putString("message", it) }
    }

Keep the human-readable message as a diagnostic only. Application logic should branch on reason, which your library controls, and not on platform strings that vary by OEM and locale.

Fallback policy: biometrics only, or device credential too

The library should not decide this silently. Expose it as allowDeviceCredential and document what each setting means.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Biometric only: the prompt needs a cancel/negative button, and a user without enrolled biometrics gets none_enrolled from the availability check. Your app must offer its own alternative path.
  • Biometric plus device PIN/pattern/password: the same system prompt offers the credential. With this option the negative button text must not be set, as the sketch above does.

Be careful about generalizing API-level limits. SelfLender documents that its own allowDeviceCredentials option is not supported on Android before API 30; that is a statement about that package, not a universal Android restriction. AndroidX also documents restrictions on some authenticator combinations on older API levels, particularly alongside a CryptoObject, so test your chosen combination on the oldest API level you claim to support instead of assuming it works everywhere.

Also decide what a device-credential success means for your feature. If a key is bound to strong biometrics only, a PIN entry will not unlock it; the prompt-only gate has no such distinction, which is one more reason to document the guarantee precisely.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Outcomes your app should handle explicitly

Outcome Typical cause Suggested app behavior
success Local verification accepted Proceed with the local action only; do not treat as server login
user_canceled / negative_button User dismissed the prompt or chose the alternative Stay locked or show your alternative path; no error banner
lockout Too many failed attempts, temporary Offer device credential if allowed, or tell the user to wait
lockout_permanent Biometrics disabled until stronger authentication Route to device credential or account sign-in
not_available No enrollment, hardware missing or busy Hide the feature or direct the user to settings
interrupted App backgrounded or system cancelled Allow a retry on return; never auto-grant
superseded A newer call replaced this one Ignore; the newer call owns the UI
timeout, error Platform timeout or other failure Offer retry and a non-biometric path

The rule is simple: only success: true unlocks anything. Every other branch is a distinct, handled non-success.

Rank #4
Kensington Upgraded VeriMark Desktop 2.0 USB Fingerprint Reader Supports USB-C and USB-A - Windows Hello with ESS, Windows 11 Fingerprint Scanner for PC, FIDO U2F, FIDO2, TAA Compliant (K64741WW)
  • Certified to Microsoft’s highest fingerprint security standards (ESS & SDCP) for robust, hardware-isolated authentication. Supports next-gen Windows features, including Copilot Recall and Windows Hello with ESS support.
  • Windows Hello ready for fast, password free fingerprint login to Windows and Microsoft 365 accounts
  • On device fingerprint storage keeps biometric data securely within the key. Supports privacy regulations (GDPR, BIPA, CCPA) through on device biometric processing; TAA compliant.
  • Reliable wired USB fingerprint authentication with USB C and USB A compatibility for desktop PCs.
  • Consistent, all condition 360° fingerprint recognition.

Compatibility work that is separate from the Kotlin

Writing correct Kotlin does not make a library compatible with every React Native setup. Treat these as three separate acceptance tracks:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Old versus new architecture. A module written as above uses the legacy native-module style. Supporting the new architecture means defining a typed spec and codegen output and testing with it enabled. The @sbaiahmed1/react-native-biometrics repository claims support for both architectures, but that is a maintainer claim about that package, not evidence about yours.
  • Expo. Expo apps that use custom native code need a development build, and a config plugin if your library requires manifest or Gradle changes. The same repository documents Expo configuration; again, treat it as a checklist item for you to verify.
  • Activity assumptions. Apps with a non-FragmentActivity host, multiple activities, or an embedded React Native view will break the currentActivity as? FragmentActivity cast. Fail with a clear rejection.

Build or adopt?

Two existing libraries are worth reading before you commit, and both are maintainer documentation rather than audited products. @sbaiahmed1/react-native-biometrics documents Kotlin on Android, availability checks, prompts, device credential fallback, key functions, Expo configuration, and old/new architecture support. SelfLender/react-native-biometrics documents keystore-managed keypairs, signing, and simplePrompt. Repository pages change, and current release numbers and maintenance activity are not established here, so check both before deciding.

Compare candidates, including your own, on the same axes:

  • Prompt-only gate versus key-backed signing.
  • Framework API versus AndroidX compatibility API.
  • Biometric-only versus device-credential fallback.
  • Legacy bridge versus new-architecture support.
  • Dependency count and binary size, measured on the same build and device baseline.
  • Defined behavior for cancellation, lockout, missing sensors, and backgrounding.

Be careful with “lightweight”

The existing library that markets itself as lightweight with minimal dependencies publishes no reproducible measurement in its documentation, and no benchmark figure was found for either library. If you call yours lightweight, define it: the AAR size delta in a release build, transitive dependency count, and any latency number with the device, API level, and build type stated. Without those, describe the design (“one module, one AndroidX dependency, no custom UI”) and let readers judge.

Pre-release test matrix

  • Lowest and highest API levels you claim, plus API 28/29 and 30+ if you offer device-credential fallback.
  • Device with no enrollment, with fingerprint, and with face unlock.
  • Cancel via back, via the negative button, and via cancel() from JS.
  • Press Home while the prompt is open, then return.
  • Rapid double call to authenticate; confirm both promises settle.
  • Repeated failures until lockout.
  • Old and new architecture builds, and an Expo development build.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.