3.1 KiB
3.1 KiB
PRF extension support
(no submodules)
Purpose
Implements the W3C WebAuthn L3 §10.1.4 PRF (Pseudo-Random Function) extension passthrough plus result parsing. PRF is the cornerstone of passkey-as-master-key-unlock flows: the authenticator's secure element materialises a ~32-byte symmetric secret deterministic in the (credential id, caller-supplied salt) pair. Consumers (notably @suaveplan/vault) use the secret to wrap an Account Unlock Key so a vault unlocks without the user typing a master password.
Features
- Typed input shapes for both ceremonies:
PrfRegisterInput({ eval: { first, second? } }) andPrfAuthenticateInput({ eval | evalByCredential }). readPrfResult(extensions)extracts theprffield from the rawClientExtensionResultsobject, narrowing types so callers consumeUint8Arrays rather thanunknown.buildPrfExtension(input)produces the wire-shape forwarded tonavigator.credentials.create/getso callers do not duplicate the structural construction.- Three-state result:
enabled === false(not supported),enabled === truewith noresults(supported but caller did not request an eval),enabled === truewithresults.first: Uint8Array(the materialised secret).
Usage: Basic Example
import { readPrfResult } from "@suaveplan/webauthn/extensions/prf";
const prf = readPrfResult(authenticationResult.extensions);
if (prf?.enabled && prf.results?.first) {
const wrapKey = prf.results.first;
}
Usage: Advanced Example
import {
buildPrfExtension,
readPrfResult,
} from "@suaveplan/webauthn/extensions/prf";
const ext = buildPrfExtension({
eval: { first: new Uint8Array(32) }
});
const cred = await navigator.credentials.get({
publicKey: { challenge, allowCredentials, extensions: ext },
});
const prf = readPrfResult(
(cred as PublicKeyCredential).getClientExtensionResults()
);
if (!prf?.enabled) {
throw new Error("authenticator does not support PRF");
}
API Reference
PrfRegisterInput—{ eval?: { first: Uint8Array, second?: Uint8Array } }.PrfAuthenticateInput—{ eval?, evalByCredential? }.PrfRegisterResult/PrfAuthenticateResult—{ enabled: boolean, results?: { first: Uint8Array, second?: Uint8Array } }.readPrfResult(extensions): PrfRegisterResult | undefined— narrow + extract.buildPrfExtension(input): { prf?: ... }— wire-shape builder.
Implementation Notes
- The package does NOT request or surface a
prfsecond-input by default. Consumers that need two-input PRF (key rotation flows) opt in by settingeval.second. - The
enabledfield is set totrueby some Chromium platform authenticators even when the caller did not request an eval; consumers SHOULD treatenabled === truewithoutresultsas "PRF is available, request it next time" rather than "secret is materialised". readPrfResultis intentionally tolerant of missing fields — it returnsundefinedwhen no PRF extension is present at all, distinguishing that from{ enabled: false }(PRF requested but not supported by the authenticator).