#2863·hanko

Android passkey registration fails with GMS [50152] RP ID cannot be validated; residentKey/requireResidentKey not configurable

Author: vdevcCreated Aug 20, 2026Updated Aug 20, 2026

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.json served correctly on the RP domain — verified well-formed, correct package_name/sha256_cert_fingerprints/relation, and independently confirmed via Google's own Digital Asset Links API ("linked": true).
  • APK signing fingerprint / applicationId match the served assetlinks.json — verified with apksigner/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) and attestation: none to a config confirmed working for another user against a different (non-Hanko) relying party — no change in behavior, same error, same timing (fails ~1s after ValidateRpIdOperation starts).
  • 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:

https://github.com/teamhanko/hanko/blob/124f19aa972d9af0a4a48dd22f8f3ef434763fc3/backend/flow_api/services/webauthn.go#L326-L340

go
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

  1. Self-hosted Hanko (Docker Compose, ghcr.io/teamhanko/hanko:latest pinned at the version below) as relying party, passkey.attestation_preference: none, passkey.user_verification: preferred.
  2. Client: Flutter app on Android using corbado/flutter-passkeys (passkeys: 2.22.3, passkeys_android: 2.13.1) calling Hanko's webauthn_generate_creation_options / registration flow.
  3. Pass the returned creationOptions.publicKey straight through to authenticator.register(...).
  4. 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:

json
{
  "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

yaml
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, discouraged

relying_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: required default: #278

Happy to provide any additional logs, a minimal repro Hanko config, or test further on request.