Skip to content

feat(webauthn): support appid and appidExclude extensions#204

Open
AlfioEmanueleFresta wants to merge 3 commits into
masterfrom
feat/webauthn-appid-extensions
Open

feat(webauthn): support appid and appidExclude extensions#204
AlfioEmanueleFresta wants to merge 3 commits into
masterfrom
feat/webauthn-appid-extensions

Conversation

@AlfioEmanueleFresta
Copy link
Copy Markdown
Member

@AlfioEmanueleFresta AlfioEmanueleFresta commented May 10, 2026

Adds the WebAuthn appid (assertion) and appidExclude (registration) extensions, which let a Relying Party reuse its legacy FIDO U2F-keyed credentials inside the WebAuthn API.

Background

FIDO U2F credentials were registered with an "AppID", a string (typically an https:// URL) that the authenticator hashed with SHA-256 to derive the per-application key. When an RP migrates from U2F to WebAuthn, its existing user accounts have credentials keyed under that legacy AppID, not under the new rp.id. To keep those credentials usable, WebAuthn defines two extensions:

  • appid (assertion only): the RP supplies its legacy AppID and the platform tries SHA-256(appid) as the application parameter alongside SHA-256(rpId).
  • appidExclude (registration only): preflight tests the excludeList against SHA-256(appidExclude) in addition to SHA-256(rpId), so the RP can prevent duplicate registration when a legacy U2F-keyed credential already exists.

Without these extensions, libwebauthn always derives the U2F application parameter from rp.id, making any legacy-AppID credential unreachable through the WebAuthn API.

Changes

  • IDL: appid and appidExclude accepted on the request side; appid: bool emitted on the response side (clientExtensionResults.appid).
  • U2F downgrade path emits a SignRequest keyed under SHA-256(appid) per allow-list entry, paired with the SHA-256(rpId) variant. clientExtensionResults.appid reflects which one matched.
  • FIDO2 preflight tests excludeList entries against both SHA-256(rpId) and SHA-256(appidExclude), so duplicate-prevention works against legacy-keyed credentials.
  • Basic well-formedness check on the appid string (must be a non-empty https:// URL).
  • Tests: IDL round-trip for input + output JSON, U2F downgrade hash derivation with and without appid, validation rejection of non-https AppIDs, preflight detection of a legacy-keyed credential when appid_exclude is supplied.

Deferred (worth a follow-up)

  • Full same-site validation of appid against rpId per WebAuthn L3 sec. 10.1.1.
  • U2F preflight efficiency improvements (CTAP1 0x07 check-only) that would also reduce the per-attempt cost of appid / appidExclude on U2F-only authenticators.

References

@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-appid-extensions branch from 7360190 to c838f4b Compare May 10, 2026 20:31
Per WebAuthn L3 sections 10.1 and 10.2, RPs can opt into using legacy
U2F AppID strings for assertion and registration ceremonies. This adds
the wire-level IDL fields so the JSON parser accepts them and the
response serializer can surface the appid extension output.
…grade

Adds appid to GetAssertionRequestExtensions and routes it through
try_downgrade(): when set, the U2F sign path emits a paired SignRequest
keyed under SHA-256(appid) for each allow-list entry, in addition to
the rpId-derived request. This makes legacy U2F-keyed credentials
reachable through the WebAuthn API.

Also adds the appid output field to GetAssertionResponseUnsignedExtensions
and a basic same-site sanity check on the input URL.
… appid in U2F output

Adds a ctap2_preflight_with_appid helper that tests each excludeList
entry under both the rpId-derived and the appidExclude-derived
application parameter (WebAuthn L3 sec 10.2), preventing duplicate
registration of legacy U2F-keyed credentials.

On the U2F downgrade get_assertion path, surface the FIDO AppID
extension output (clientExtensionResults.appid) based on which of the
paired SignRequests matched.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/webauthn-appid-extensions branch from c838f4b to f4aa890 Compare May 12, 2026 18:47
@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review May 12, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant