Conversation
🦋 Changeset detectedLatest commit: 9ab9910 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
mikepitre
force-pushed
the
mike/expo-biometrics-android
branch
from
September 28, 2026 15:03
ce8ac2f to
92c610f
Compare
mikepitre
force-pushed
the
mike/expo-biometrics-android
branch
from
September 28, 2026 19:06
92c610f to
22d69b5
Compare
mikepitre
force-pushed
the
mike/expo-biometrics-ios
branch
from
September 28, 2026 19:35
19adb60 to
4455c35
Compare
mikepitre
force-pushed
the
mike/expo-biometrics-android
branch
from
September 28, 2026 19:35
22d69b5 to
31d5f90
Compare
9 tasks
Implement the Android side of the module against clerk-android's biometric credential storage contract v2, and add hashIdentifierHint() plus identifierHintSha256 on listed records on both platforms. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Report secureKeyStorageAvailable from getAvailability() on Android (false below API 28) and reject createKey() there with secure_key_storage_unavailable instead of biometry_not_available. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
mikepitre
force-pushed
the
mike/expo-biometrics-ios
branch
from
September 28, 2026 20:23
4455c35 to
ca1e44b
Compare
mikepitre
force-pushed
the
mike/expo-biometrics-android
branch
from
September 28, 2026 20:23
31d5f90 to
9ab9910
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implements the Android side of
@clerk/expo-biometrics, which was anot_implementedstub in #9959. It follows clerk-android's biometric credential storage contract v2 (clerk/clerk-android#966), so credentials enrolled by this module and by clerk-android in the same app are interchangeable. The module does not depend on clerk-android.Keys: Android Keystore EC
secp256r1signing keys with the aliascom.clerk.trusted_device.<localKeyId>, wherelocalKeyIdistdlk_+ 32 hex characters. Every use requires user authentication. On API 30+ the authenticators are set per policy, withinvalidatedByBiometricEnrollmentonly forbiometry_current_set. API 28–29 use validity-1, and on API < 28getAvailability()reportssecureKeyStorageAvailable: false,createKey()rejects withsecure_key_storage_unavailable, andsign()withbiometry_not_available.Signing: androidx
BiometricPromptwith aCryptoObject(Signature)on the currentFragmentActivity. The DER signature is converted to rawr || sand encoded as unpadded base64url.KeyPermanentlyInvalidatedExceptionmaps tokey_invalidated. The prompt allows a device credential only when the key itself accepts one (fromKeyInfoon API 30+). For keys created on API 30+ this matches the contract's per-policy rule. It also keepsbiometry_or_device_passcodekeys created on API 28–29 biometric-only after an OS upgrade, since a PIN could not unlock them.Records: stored in the contract's plaintext
noBackupFilesDir/clerk/biometric_credentials.v2.json(version 2, snake_case fields, onlyidentifier_hint_sha256). Every read-modify-write goes through the contract's write protocol:FileChannel.tryLockonbiometric_credentials.lock, retryingOverlappingFileLockException/nullwith a 2 → 50 ms backoff for up to 5 sclerk_preferencesare never read or writtenThe bridge converts between the snake_case storage fields and the existing camelCase record shape.
Other functions:
getAppIdentifier()returns the package name.getAvailability()usesBiometricManager.canAuthenticateand reportsbiometryType: 'biometric'(new union member), since Android does not say which sensor is a strong biometric.ensureInstallationMarker()always resolves{ wiped: false }, because uninstalling wipes bothnoBackupFilesDirand the app's Keystore keys.saveRecord(..., { removeOtherRecordsForApp: true })deletes only the same user's other records, as contract section 5.3 requires.Android stores only hashed identifier hints, so this also adds a cross-platform API:
hashIdentifierHint(hint)(sync) returns the lowercase hex SHA-256 of the trimmed, lowercased hint, ornullwhen it is empty.listRecords()records now includeidentifierHintSha256on both platforms. iOS computes it from its stored raw hint and still returnsidentifierHint. Android returnsidentifierHint: null.saveRecordstill takes the rawidentifierHint. iOS stores it raw (clerk-ios contract v1), and Android stores only its hash.Robolectric unit tests are wired into the module's Gradle test task. They pin the contract with the v2 fixture copied from clerk/clerk-android#966.
Stacked on #9959.
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
🤖 Generated with Claude Code