Skip to main content

Keeping the Authenticator and your Server in Sync

Your server and the user’s credential manager hold separate passkey records and they are not synchronized automatically. If they differ, the account chooser may offer a passkey that the server no longer accepts. The user can complete local device authentication before the login fails. The stale entry may also remain available on later attempts until the credential manager updates it. Where the platform and credential provider support it, signalling can reduce that gap. It is a progressive, best-effort lifecycle capability rather than a UI flow or authoritative source of truth; unsupported integrations must continue to work without it. In a native app the stale credential surfaces through Conditional UI and the app-start overlay, both of which offer whatever the credential manager holds regardless of what your server still accepts.
Support (August 2026): Android 15+ via androidx.credentials 1.6.0 (stable since 8 April 2026). Apple platforms support signalling from 26.0: use legacy ASCredentialUpdater on 26.0–26.1 and current ASCredentialDataManager on iOS, iPadOS, macOS and visionOS 26.2+. For the browser equivalent see the Web Signal API.
Apple renamed this API one point release after shipping it. ASCredentialUpdater was introduced in 26.0 and deprecated in 26.2 in favour of ASCredentialDataManager. Signalling is therefore possible from 26.0; use 26.2 as the recommended new-build target for the current API, not as the feature’s availability floor. Sample code from WWDC25 and 2025-era articles uses the deprecated class.

The three signals

On Android all three are sent through CredentialManager.signalCredentialState() and carry a JSON string rather than typed fields.
1

When the server rejects an unknown credential

  • Send this signal only when the credential ID is conclusively absent from or no longer accepted by server state. Do not send it for invalid signatures, expired challenges, RP or origin mismatch, cancellation, network failure or other transport and verification errors.
  • A provider may hide or remove the credential; some providers may later unhide it after a complete accepted-list signal, but do not rely on recoverability or claim that provider action occurred.
2

At an authenticated reconciliation point

  • According to a documented schedule, send the complete set of credential IDs your server accepts for that user. Typical points include selected authenticated refreshes and server credential-set changes, subject to platform rate limits.
3

After a successful credential-set mutation

  • After creation or server-side revocation, reconcile the complete server-side list while the user is authenticated, subject to availability, coalescing and rate limits. Revocation never waits for the signal.
4

After a profile change

  • Report only the fields supported by the current platform so enabled credential managers can choose to update them. Do not block the profile change or claim that an update occurred.
Never send a partial or accidentally empty credential list. The reconcile signal may hide or remove every passkey it does not find in the accepted list. An empty list is valid only when the authoritative server state confirms that the authenticated user has no accepted credentials, for example after deleting their final passkey. Treat the code that assembles this list as high-risk, and test it against accounts with zero, one and several credentials.

Why server-side Revocation still needs Client Cleanup

Revoking the credential in authoritative server state makes it unusable immediately: every later assertion from it must be rejected. The server may retain an inactive record for audit. What revocation cannot do is remove the stale entry from the user’s locally installed credential manager, because your backend has no direct channel to the user’s password manager. Client-side cleanup can happen only when the user’s client runs again. Signal an unknown credential only after the server confirms that the submitted credential is unknown. Reconcile the complete accepted list on a documented authenticated schedule, including after authoritative credential-set changes and selected refresh points. The schedule should account for capability, coalescing and rate limits; revocation itself must never wait for signalling.
Signals resolve with no information about what the provider did. You never learn whether a credential existed or whether the provider acted, which is deliberate and privacy-preserving. It also means signalling can never serve as an audit trail or a source of truth. Treat it as fire-and-forget hygiene, and never block a UI flow on the result.

Platform differences worth planning for

reportPublicKeyCredentialUpdate accepts only newName. The web and Android equivalents carry both name and displayName, so a display name change cannot be signalled from an iOS or macOS app.
Android permits a maximum of 10 calls in any 120-second window; exceeding it results in throttling or rejection. SignalCredentialRateLimitExceededException carries a retryMillis value, so back off by that amount rather than retrying blind. That exception only exists from 1.6.0, which is one reason to require the stable release rather than an earlier beta.
reportUnusedPasswordCredential(domain:userName:) tells credential managers that a password is no longer in use. This is directly relevant when retiring passwords on accounts that have moved to passkeys.
Android explicitly supports sending signals from a background context, within the rate limit. No user presence or user verification is required on any platform.

Acceptance criteria

Native signalling does not create or promote passkeys. It reduces stale credential choices after server credential state changes. A signal request is best effort and never proves that a credential provider changed anything.

References

Further reading