Skip to content

Two-Factor Authentication (Security Keys) : tpm-fido : Could not read your security key : NotAllowedError #35362

@bobemoe

Description

@bobemoe

Description

Registration of a roaming security key fails because the WebAuthn request requires resident/discoverable credentials, which such authenticators don’t support.

Environment

  • Client OS: Artix Linux, KDE, Wayland
  • Browser: Firefox 141.0.3 (64-bit)
  • Authenticator: tpm-fido (Linux TPM → FIDO2 via UHID)
    • fido2-token -L shows:
      /dev/hidraw3: vendor=0x15d9, product=0x0a37 ( tpm-fido) 

Steps to reproduce

  1. Start the authenticator bridge:
    tpm-fido
  2. On the Gitea demo site, open Settings → Security → Security Keys → Add Security Key.
  3. At this moment, the authenticator log prints:
    got VersionCmd 
    No user-presence prompt appears.
  4. The browser shows:
    Could not read your security key. NotAllowedError: The request is not allowed by the user agent or the platform in the current context 

Captured navigator.credentials.create() options (from DevTools on the failing page)

{ "publicKey": { "rp": { "name": "xx", "id": "xx.co.uk" }, "user": { "name": "xx", "displayName": "xx", "id": { "__type": "ArrayBuffer", "base64": "AgAAAAAAAAA=" } }, "challenge": { "__type": "ArrayBuffer", "base64": "xxxx=" }, "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 } ], "timeout": 300000, "authenticatorSelection": { "residentKey": "required" }, "attestation": "direct" } }

Workarounds verified (in-page, DevTools console)

1) Change only resident key requirement → registration succeeds

(() => { const orig = navigator.credentials.create.bind(navigator.credentials); navigator.credentials.create = (opts) => { const sel = opts?.publicKey?.authenticatorSelection; if (sel && (sel.residentKey === 'required' || sel.requireResidentKey === true)) { opts.publicKey.authenticatorSelection = { ...sel, residentKey: 'preferred', requireResidentKey: false }; } return orig(opts); }; })();

Result: user-presence prompt appears; firefox warns of collecting extra information; authenticator proceeds beyond got VersionCmd; key registers successfully.

2) Additionally set attestation to "none" → removes Firefox “extra information” consent; registration still succeeds

(() => { const orig = navigator.credentials.create.bind(navigator.credentials); navigator.credentials.create = (opts) => { if (opts?.publicKey) { if (opts.publicKey.authenticatorSelection?.residentKey === 'required' || opts.publicKey.authenticatorSelection?.requireResidentKey === true) { opts.publicKey.authenticatorSelection = { ...opts.publicKey.authenticatorSelection, residentKey: 'preferred', requireResidentKey: false }; } opts.publicKey.attestation = 'none'; } return orig(opts); }; })();

Control test

Using the same client + authenticator, registration works on webauthn.io when configured for a security key with non-resident credentials and userVerification discouraged.

Fix / Request

Proposed fix

For the Security Keys (roaming key) MFA flow:

  • Use options compatible with external roaming authenticators:
    • authenticatorSelection: { residentKey: "preferred", authenticatorAttachment: "cross-platform", userVerification: "preferred", requireResidentKey: false }
    • attestation: "none" (removes extra-info prompts; still works)
  • Alternatively, expose the above as per-instance settings so admins can choose.

Optional UX improvement:

  • Provide two explicit buttons/flows with appropriate defaults:
    1. Add Security Key (USB/NFC) → for 2FA
      authenticatorSelection: { residentKey: "preferred", authenticatorAttachment: "cross-platform", userVerification: "preferred", requireResidentKey: false } attestation: "none"
    2. Add Passkey (this device) → for passwordless
      authenticatorSelection: { residentKey: "required", authenticatorAttachment: "platform", userVerification: "required" } attestation: "none" // or "indirect"

Gitea Version

1.24.5

Can you reproduce the bug on the Gitea demo site?

Yes

How are you running Gitea?

https://demo.gitea.com/

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions