Android passkey registration fails with GMS [50152] RP ID cannot be validated; residentKey/requireResidentKey not configurable
Checklist
- I could not find a solution in the existing issues or docs.
- I agree to follow this project's Code of Conduct.
Describe the bug
Passkey registration on Android fails with a Google Play Services (Credential Manager / FIDO2) error, [50152] RP ID cannot be validated, thrown from ValidateRpIdOperation inside Auth.Api.Credentials. This surfaces client-side (via corbado/flutter-passkeys) as a generic androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION/androidx.credentials.TYPE_DATA_ERROR.
I've spent a long debugging session (full writeup in corbado/flutter-passkeys#295) eliminating every variable I could test directly, and everything checks out except the relying party itself:
assetlinks.jsonserved correctly on the RP domain — verified well-formed, correctpackage_name/sha256_cert_fingerprints/relation, and independently confirmed via Google's own Digital Asset Links API ("linked": true).- APK signing fingerprint /
applicationIdmatch the servedassetlinks.json— verified withapksigner/keytool. - Reproduced identically on two independent environments (Android emulator, GMS 26.29.32; physical device, GMS 26.30.32) with a byte-identical APK.
- Matched
authenticatorSelection(residentKey: preferred,requireResidentKey: false) andattestation: noneto a config confirmed working for another user against a different (non-Hanko) relying party — no change in behavior, same error, same timing (fails ~1s afterValidateRpIdOperationstarts). - The RP ID's domain is a CNAME to its apex; found and fixed an unrelated TLS/vhost gap on the apex just to rule it out — no change in behavior.
Since I've now matched a working third-party client/RP configuration field-for-field on the parts I can control, and the failure persists, I'm not able to narrow this down further from the client side. Filing here in case this is a known interaction with how Hanko generates creationOptions, or in case someone on the team has visibility into what GMS's RP ID validation actually checks that I don't.
Separately, and possibly related: while investigating, I found that residentKey/requireResidentKey are hardcoded (not configurable) in the primary passkey registration flow:
func (s *webauthnService) GenerateCreationOptionsPasskey(p GenerateCreationOptionsParams) (*models.WebauthnSessionData, *protocol.CredentialCreation, error) {
requireResidentKey := true
authenticatorSelection := protocol.AuthenticatorSelection{
RequireResidentKey: &requireResidentKey,
ResidentKey: protocol.ResidentKeyRequirementRequired,
UserVerification: protocol.UserVerificationRequirement(p.Cfg.Passkey.UserVerification),
}
...
}UserVerification and AttestationPreference are both exposed via Passkey config (backend/config/config_passkey.go), but ResidentKey/RequireResidentKey are not — unlike the sibling GenerateCreationOptionsSecurityKey flow, which hardcodes the opposite (discouraged/false). I understand the rationale for defaulting to required for discoverable-credential UX (discussed in #278), but not having any way to override it made it impossible to fully isolate this variable without patching the Go source locally to test. Exposing it (even just as an advanced/opt-in config key) would help future debugging like this.
Reproducing the bug
- Self-hosted Hanko (Docker Compose,
ghcr.io/teamhanko/hanko:latestpinned at the version below) as relying party,passkey.attestation_preference: none,passkey.user_verification: preferred. - Client: Flutter app on Android using corbado/flutter-passkeys (
passkeys: 2.22.3,passkeys_android: 2.13.1) calling Hanko'swebauthn_generate_creation_options/ registration flow. - Pass the returned
creationOptions.publicKeystraight through toauthenticator.register(...). - Registration fails immediately (~1s) with the log below, before any biometric/PIN prompt.
Logs
[ValidateRpIdOperation] Operation started. [CONTEXT service_id=363 ]
[Registration_flowRunner] Flow failed. [CONTEXT service_id=363 ]
colz: [50152] RP ID cannot be validated.
at coly.a(:com.google.android.gms@[email protected] (260400-956293556):11)
at coma.f(:com.google.android.gms@[email protected] (260400-956293556):10)
at albu.a(:com.google.android.gms@[email protected] (260400-956293556):25)
...
[RegistrationChimeraActivity] Activity finished with error. [CONTEXT service_id=363 ]
colz: [50152] RP ID cannot be validated.Native-layer JSON actually sent to CreatePublicKeyCredentialRequest (captured by instrumenting the plugin), domain/user anonymized:
{
"rp": {"name": "Example Authentication Service", "id": "auth.example.com"},
"user": {"displayName": "[email protected]", "name": "[email protected]", "id": "QGDaCe_DSPauybArM4SpFQ"},
"challenge": "vpCgjQWHzGChxD7X7tDG2HlBwL7ffQsi8alC999Zv0Y",
"attestation": "none",
"timeout": 600000,
"pubKeyCredParams": [
{"type":"public-key","alg":-7},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},
{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},
{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39},
{"type":"public-key","alg":-8}
],
"authenticatorSelection": {"requireResidentKey": false, "residentKey": "preferred", "userVerification": "preferred"},
"excludeCredentials": []
}(This particular capture has residentKey/requireResidentKey overridden client-side for testing — Hanko's actual default response sets residentKey: required, requireResidentKey: true, per the hardcoded values above. Both were tested and the error is identical either way.)
Configuration
passkey:
acquire_on_registration: "always" # default | always, conditional, never
acquire_on_login: "never" # always, conditional, never
attestation_preference: "none" # default | direct, indirect, none
enabled: true # default
limit: 10 # default
optional: true # default
user_verification: "preferred" # default | required, preferred, discouragedrelying_party.id is auth.example.com (anonymized) — a subdomain CNAME'd to its apex, see the CNAME/TLS note above.
Hanko Version
v3.0.4 (commit 124f19aa972d9af0a4a48dd22f8f3ef434763fc3)
OS Hanko Backend
Linux
OS
Other (Android — client-side, not the Hanko backend host)
OS Version
Android 17 (API 37, emulator, GMS 26.29.32) and Android 10 (API 29, physical Huawei device, GMS 26.30.32)
Environment
Docker Compose
Additional Context
- Full client-side investigation, including all ruled-out variables and raw logs: corbado/flutter-passkeys#295
- Related historical context on the
residentKey: requireddefault: #278
Happy to provide any additional logs, a minimal repro Hanko config, or test further on request.
Source: teamhanko/hanko