How an Android passkey provider works: Credential Manager, step by step
SHORT ANSWER
How does a third-party passkey provider work on Android?
Android's Credential Manager receives the request from the browser or app and asks every enabled provider what it can offer. When the user picks HostSpica, Android launches one of our screens. We check the user with a biometric, create or use a Keystore key, build the WebAuthn data, sign it, and hand the result back to Credential Manager.
Key takeaways
- On Android 14 and newer a provider is a service, `CredentialProviderService`, plus screens that Android launches on the user's behalf. It does not talk to the website itself.
- The Keystore key must exist before the biometric prompt, because the attestation challenge has to be fixed when the key is created.
- The biometric check must be bound to the exact signing operation, or Android refuses to sign.
- We have tested register and sign-in on a small set of sites. We list what is untested and what is not supported yet.
Who talks to whom
When a site in Chrome calls navigator.credentials.create(), the browser does not contact HostSpica. It asks Android's Credential Manager, which asks each enabled provider what it can offer and shows the user a single sheet. Choosing our entry makes Android start one of our activities. Whatever that activity returns goes back through Credential Manager to the browser, and from there to the site. That is why a provider needs no internet permission.
Creating a passkey
- Begin. Credential Manager calls
onBeginCreateCredentialRequest. We answer with a single entry, "HostSpica Identity", that carries aPendingIntentto our create screen. - Parse. Our
CreatePasskeyActivityreceives the request JSON: the site ID, the user's ID and name, and the challenge. - Client data. A browser on Google's list of privileged apps supplies the hash of the client data itself, because it owns the real origin. For other callers we build the client data ourselves and hash it.
- Key first. We generate an EC P-256 key in Keystore, asking for StrongBox first. The key needs a fresh user authentication for every signature, and we pass the client-data hash as its attestation challenge.
- Biometric. We show a fingerprint or face prompt bound to a
CryptoObjectthat wraps the signing operation for that key. - Build and sign. The authenticator data is the SHA-256 of the site ID, a flags byte (user present, user verified, attested data included), a signature counter of 0, our authenticator ID (AAGUID), the credential ID and the public key in COSE format. We sign that data followed by the client-data hash.
- Attest and return. The attestation object uses format
android-key: our signature plus the certificate chain Android produced for the key, ending at Google's hardware attestation root. If a device cannot attest, we fall back to formatnone. The response goes back to Credential Manager.
Signing in
onBeginGetCredentialRequest lists the credentials we hold for the site and returns one entry per credential. When one is picked, GetPasskeyActivity increments and saves the signature counter before signing, asks for the biometric bound to that key's signing operation, signs the authenticator data plus the client-data hash, and returns it with the user handle.
Who may claim a website's origin
A browser knows which site it is showing, so it tells Credential Manager. A provider must not trust that claim from just any app, so Android lets a provider check the caller against an allowlist of privileged apps with their signing certificates. We ship Google's published list, which covers the main browsers (Chrome, Firefox, Edge, Brave, Samsung Internet and others).
Bugs we hit, so you do not have to
- Biometric not bound to the key. Calling the prompt first and then signing with a fresh
Signaturefails every time, because the authentication has to belong to that exact operation. Create theSignature, wrap it in aCryptoObject, and sign through the object the prompt returns. - StrongBox failure shows up late. On some phones the error arrives from
generateKeyPair(), not frominitialize(). We catch any failure and retry without StrongBox. - Different exceptions on different Android versions. A key that needs authentication throws
UserNotAuthenticatedExceptionon some builds and a plainSignatureExceptionwith a "Key user not authenticated" message on newer ones. We check both. - Chrome rejects responses without `clientExtensionResults`. Even though we support no extensions, the response JSON must contain an empty
clientExtensionResultsobject. Without it Chrome failed with an unknown error while our screen logged success. - Two providers with the same name. With the standalone HostSpica Passkey app and HostSpica Identity both enabled, the picker showed two identical "HostSpica Passkey" entries. We renamed the Identity entry.
What we have tested
In October 2026 we registered and signed in on Google, GitHub, Microsoft and webauthn.io, using Chrome and Brave, on one Android 16 phone. We have not tested other phones or Android versions, Firefox or Edge, or sign-in from another device.
What we do not support yet
Frequently asked questions
Does HostSpica Passkey need internet access?
No. Android's Credential Manager carries the request between the browser and our screens on the phone. The app declares no internet permission.
Which Android versions does it need?
Android 14 or newer, the first version with the Credential Manager provider API. The Passkey tab is hidden on older versions.
Why does the key get created before the fingerprint prompt?
Android key attestation binds a challenge into the key when the key is created, and the WebAuthn format needs that challenge to be the client-data hash. The key therefore has to exist first, and we delete it again if you cancel.
References
Review status
Last technical self-review by the author on 3 October 2026. No independent reviewer yet. If you spot an error, write to [email protected] and we will correct it and note the change.
Rohan builds HostSpica's Android apps — Authenticator, Passkey and Identity — and writes up how they work, including the mistakes along the way.
ABOUT THE PRODUCTS
RELATED