Skip to content

WebAuthn security key (2FA) login fails — "key not recognized" / NotAllowedError — while registration succeeds #38139

@morestreetspeed-jpg

Description

@morestreetspeed-jpg

Gitea Version

1.26.0

What happened?

Summary. Adding a roaming FIDO2 security key (Settings → Security → "Two-Factor Authentication (Security Keys)") succeeds, but login with that key fails at the 2FA step (/user/webauthn):

  • Windows dialog: "This security key isn't recognized."
  • Page: NotAllowedError: The operation either timed out or was not allowed.

This looks like the login-side counterpart of #35362. There, registration fails for authenticators that can't create resident credentials, because Gitea requests authenticatorSelection.residentKey: "required". My authenticator (Rutoken MFA) does support resident keys, so registration succeeds — but the subsequent assertion (login) is rejected.

Steps to reproduce

  1. Settings → Security → add a security key (Windows: choose "Security key", touch + PIN). Registration succeeds; the key is listed.
  2. Sign out; sign in with username + password.
  3. At the WebAuthn 2FA prompt, touch the same key → NotAllowedError; login is impossible. (TOTP, when also enrolled, works.)

Expected: login completes with the registered key.
Actual: the platform rejects the key as not matching any credential.

Relevant config: ROOT_URL=https://git.example.com/, HTTP_ADDR=127.0.0.1, no [webauthn]/RP_ID override (rpId derived from ROOT_URL = git.example.com, correct). HTTPS via nginx, valid TLS.

Isolation — the authenticator works against a reference RP. The same Rutoken MFA authenticates on webauthn.io in every configuration I tested:

Credential userVerification webauthn.io
discoverable (resident) preferred works
non-discoverable preferred works
non-discoverable discouraged works

Honest gap: the one combination matching Gitea's actual flow — a resident credential (Gitea registers Security Keys with residentKey: "required", see #35362) asserted with userVerification: "discouraged" — is the single combination I did not independently reproduce on webauthn.io. The three above all pass.

Assertion options Gitea returns at login (captured from the assertion fetch on /user/webauthn; credential id + challenge redacted):

{
  "challenge": "<redacted>",
  "timeout": 120000,
  "rpId": "git.example.com",
  "allowCredentials": [{ "type": "public-key", "id": "dZBDl…FarGn" }],
  "userVerification": "discouraged"
}

rpId is correct; allowCredentials carries the registered credential id (no transports field).

Analysis (evidence, not a verdict). The authenticator and each individually-tested parameter work against a reference RP, yet Gitea's assertion is rejected — so the problem is on Gitea's side, in the Security-Keys WebAuthn flow that #35362 already shows mishandles authenticatorSelection. Candidate mechanisms (not pinned): (1) the credential id Gitea stored/sent does not match the authenticator's actual credential (e.g. a base64 vs base64url round-trip); (2) the resident-credential + userVerification: "discouraged" interaction (the untested combination above).

Version note. 1.26.1 / 1.26.2 changelogs contain no WebAuthn fix; 1.26.3 was in the release pipeline when this was filed (changelog not yet published — worth confirming during triage).

Additional data on request: registration (attestation) credential id vs the assertion allowCredentials.id (to confirm/exclude mechanism 1); full un-redacted assertion JSON; docker logs around the failed attempt.

Related: #35362 (same Security-Keys flow, registration-side).

How are you running Gitea?

Docker ( network_mode: host ), PostgreSQL backend, behind an nginx reverse proxy (public HTTPS, HTTP_ADDR=127.0.0.1 ). Client: Windows 11, Chromium (Chrome/Edge), WebAuthn via the Windows platform dialog. Authenticator: Rutoken MFA (FIDO2/CTAP2.1, ES256), AAGUID all-zeros, transports nfc+usb.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions