Web Authentication extensions
TheWeb Authentication API has a system of extensions — extra functionality that can be requested during credential creation (navigator.credentials.create()
) or authentication (navigator.credentials.get()
) operations. This article explains how to request WebAuthn extensions, retrieve information about the responses from those requests, and the available extensions — including browser support and expected inputs and outputs.
How to use WebAuthn extensions
When invokingnavigator.credentials.create()
ornavigator.credentials.get()
, thepublicKey
object parameter required to initiate a WebAuthn flow can include anextensions
property. The value ofextensions
is itself an object, the properties of which are the input values for any extensions the relying party wishes to request the use of in the method you call.
Behind the scenes, the inputs are processed by the user agent and/or the authenticator.
For example, in apublicKey
object for acreate()
call, we might want to request the use of two extensions:
- The
credProps
extension. Relying parties setcredProps
to request that the browser tells the relying party whether the credential is resident/discoverable after registration. This is useful when callingcreate()
withpublicKey.authenticatorSelection.residentKey = "preferred"
. To request it, you also need to setpublicKey.extensions.credProps = true
when the browser makes a credential and, depending on the type of authenticator used, it will be discoverable (for example, the FIDO2 authenticator would typically make it discoverable; FIDO1/U2F security key would be non-discoverable).credProps
is processed by the user agent only. - The
minPinLength
extension allows relying parties to request the authenticator's minimum PIN length. This requiresextensions.minPinLength
to be set totrue
.minPinLength
is processed by the authenticator, with the user agent only serving to pass the input data along to it.
const publicKey = { challenge: new Uint8Array([117, 61, 252, 231, 191, 241 /* … */]), rp: { id: "acme.com", name: "ACME Corporation" }, user: { id: new Uint8Array([79, 252, 83, 72, 214, 7, 89, 26]), name: "jamiedoe", displayName: "Jamie Doe", }, pubKeyCredParams: [{ type: "public-key", alg: -7 }], authenticatorSelection: { residentKey: "preferred", }, extensions: { credProps: true, minPinLength: true, },};
We can then pass thepublicKey
object into acreate()
call to initiate the credential creation flow:
navigator.credentials.create({ publicKey });
Retrieving extension request results
If successful, thecreate()
call will return aPromise
that resolves with aPublicKeyCredential
object. Once extension processing has completed, the results of the processing are communicated in the response (although not in all cases — it is possible for extensions to have no output).
navigator.credentials .create({ publicKey }) .then((publicKeyCred) => { const myClientExtResults = publicKeyCred.getClientExtensionResults(); // myClientExtResults will contain the output of processing // the "credProps" extension const authData = publicKeyCred.response.getAuthenticatorData(); // authData will contain authenticator data, which will include // authenticator extension processing results, i.e., minPinLength }) .catch((err) => { console.error(err); });
As demonstrated by the above code snippet, there are two different places to find your output extension results:
You can find the results of client (user agent) extension processing by calling the
PublicKeyCredential.getClientExtensionResults()
method. This returns amap
, with each entry being an extensions' identifier string as the key, and the output from the processing of the extension by the client as the value. In the example above, if the browser supported thecredProps
extension and it was processed correctly, themyClientExtResults
map object would contain one entry,"credProps"
, with a value of{ rk: true }
. This would verify that the created credential is indeed discoverable.You can find the results of authenticator extension processing in the authenticator data for the operation:
- In the case of
PublicKeyCredential
s returned from successfulcreate()
calls, this can be returned via a call topublicKeyCredential.response.getAuthenticatorData()
. - In the case of
PublicKeyCredential
s returned from successfulget()
calls, this can be found in thepublicKeyCredential.response.authenticatorData
property.
Authenticator data takes the form of an
ArrayBuffer
with a consistent structure — seeauthenticator data. The authenticator extension results data is always found in a section at the end, as aCBOR map representing the results. SeeAuthenticatorAssertionResponse.authenticatorData
for a detailed description of the complete authenticator data structure.Back to our example, if the relying party is authorized to receive the
minPinLength
value, the authenticator data would contain a representation of it in the following form:"minPinLength": uint
.- In the case of
Available extensions
The extensions below do not represent an exhaustive list of all the extensions available. We have elected to document extensions that we know to be standardized and supported by at least one rendering engine.
appid
- Usable in: Authentication (
get()
) - Processed by: User agent
- Specification:FIDO AppID Extension (appid)
Allows a relying party to request an assertion for a credential previously registered using the legacy FIDO U2F JavaScript API, avoiding the hassle of re-registering the credential. Theappid
is that API's equivalent to therpId
in WebAuthn (although bear in mind thatappid
s are in the form of URLs, whereasrpId
s are in the form of domains).
Input
ThepublicKey
'sextensions
property must contain anappid
property, the value of which is the application identifier used in the legacy API. For example:
({ extensions: { appid: "https://accounts.example.com", },});
You must also list the FIDO U2F credential IDs in thepublicKey
'sallowCredentials
property, for example:
({ allowCredentials: [ { id: arrayBuffer, // needs to contain decoded binary form of id transports: ["nfc", "usb"], type: "public-key", }, ],});
Output
Outputsappid: true
if theappid
was successfully used for the assertion, orappid: false
otherwise.
appidExclude
- Usable in: Registration (
create()
) - Processed by: User agent
- Specification:FIDO AppID Exclusion Extension (appidExclude)
Allows a relying party to exclude authenticators containing specified credentials previously registered using the legacy FIDO U2F JavaScript API during registration. This is required because by default the contents of theexcludeCredentials
field are assumed to be WebAuthn credentials. When using this extension, you can include legacy FIDO U2F credentials insideexcludeCredentials
, and they will be recognized as such.
Input
ThepublicKey
'sextensions
property must contain anappidExclude
property, the value of which is the identifier of the relying party requesting to exclude authenticators by legacy FIDO U2F credentials. For example:
({ extensions: { appidExclude: "https://accounts.example.com", },});
You can then list FIDO U2F credentials in thepublicKey
'sexcludeCredentials
property, for example:
({ excludeCredentials: [ { id: arrayBuffer, // needs to contain decoded binary form of id transports: ["nfc", "usb"], type: "public-key", }, ],});
Output
OutputsappidExclude: true
if the extension was acted upon, orappidExclude: false
otherwise.
credProps
- Usable in: Registration (
create()
) - Processed by: User agent
- Specification:Credential Properties Extension (credProps)
Allows a relying party to request additional information/properties about the credential that was created. This is currently only useful when callingcreate()
withpublicKey.authenticatorSelection.residentKey = "preferred"
; it requests information on whether the created credential is discoverable.
Input
ThepublicKey
'sextensions
property must contain acredProps
property with a value oftrue
:
({ extensions: { credProps: true, },});
You must also setauthenticatorSelection.requireResidentKey
totrue
, which indicates that a resident key is required.
({ authenticatorSelection: { requireResidentKey: true, },});
Output
Outputs the following if the registeredPublicKeyCredential
is a client-side discoverable credential:
({ credProps: { rk: true, },});
Ifrk
is set tofalse
in the output, the credential is a server-side credential. Ifrk
is not present in the output, then it is not known whether the credential is client-side discoverable or server-side.
credProtect
- Usable in: Registration (
create()
) - Processed by: Authenticator
- Specification:Credential Protection (credProtect)
Allows a relying party to specify a minimum credential protection policy when creating a credential.
Input
ThepublicKey
'sextensions
property must contain acredentialProtectionPolicy
property specifying the protection level of the credential to be created, and a booleanenforceCredentialProtectionPolicy
property specifying whether thecreate()
call should fail rather than creating a credential that does not conform to the specified policy:
({ extensions: { credentialProtectionPolicy: "userVerificationOptional", enforceCredentialProtectionPolicy: true, },});
The availablecredentialProtectionPolicy
values are as follows:
"userVerificationOptional"
ExperimentalUser verification is optional. The equivalent
credProtect
value sent to the authenticator for processing is0x01
."userVerificationOptionalWithCredentialIDList"
User verification is optional only if the credential is discoverable (i.e., it is client-side discoverable). The equivalent
credProtect
value sent to the authenticator for processing is0x02
."userVerificationRequired"
User verification is always required. The equivalent
credProtect
value sent to the authenticator for processing is0x03
.
Note:Chromium will default touserVerificationOptionalWithCredentialIDList
oruserVerificationRequired
, depending on the type of request:
- Chromium will request a protection level of
userVerificationOptionalWithCredentialIDList
when creating a credential ifresidentKey
is set topreferred
orrequired
. (SettingrequireResidentKey
is treated the same as required.) This ensures that simple physical possession of a security key does not allow the presence of a discoverable credential for a givenrpId
to be queried. - Additionally, if
residentKey
isrequired
anduserVerification
is preferred, the protection level will be increased touserVerificationRequired
. This ensures that physical possession of a security key does not allow sign-in to a site that doesn't require user verification. (This is not complete protection; sites should still carefully consider the security of their users.) - If the site requests an explicit
credProtect
level, that will override these defaults. These defaults never cause the protection level to be lower than the security key's default if that is higher.
Suppose theenforceCredentialProtectionPolicy
value istrue
. In that case, thecreate()
call will fail if the policy cannot be adhered to (for example, it requires user verification, but the authenticator does not support user verification). If it isfalse
, the system will make the best attempt to create a credential that conforms to the policy, but it will still create one that conforms as closely as it can if this is not possible.
Output
If thecreate()
call is successful, the authenticator data will contain a representation of thecredProtect
value representing the set policy in the following form:
({ credProtect: 0x01 });
largeBlob
- Usable in: Registration (
create()
) and authentication (get()
) - Processed by: User agent
- Specification:Large blob storage extension (largeBlob)
Allows a relying party to store blobs associated with a credential on the authenticator — for example, it may wish to directly store certificates rather than run a centralized authentication service.
Input
During acreate()
call, thepublicKey
'sextensions
property must contain alargeBlob
property with the following object structure:
({ extensions: { largeBlob: { support: "required", }, },});
Thesupport
property's value is a string, which can be one of the following:
"preferred"
: The credential will be created with an authenticator that can store blobs if possible, but it will still create one if not. The output' supported' property reports the authenticator's ability to store blobs."required"
: The credential will be created with an authenticator to store blobs. Thecreate()
call will fail if this is impossible.
During aget()
call, thepublicKey
'sextensions
property must contain alargeBlob
property with one of two sub-properties —read
orwrite
(get()
fails if both are present):
Theread
property is a boolean. A value oftrue
indicates that the relying party would like to fetch a previously-written blob associated with the asserted credential:
({ extensions: { largeBlob: { read: true, }, },});
Thewrite
property takes as a value anArrayBuffer
,TypedArray
, orDataView
representing a blob that the relying party wants to store alongside an existing credential:
({ extensions: { largeBlob: { write: arrayBuffer, }, },});
Note:For a write authentication operation to be successful,publicKey.allowCredentials
must contain only a single element representing the credential you want the blob stored alongside.
Output
A successfulcreate()
call provides the following extension output if the registered credential is capable of storing blobs:
({ largeBlob: { supported: true, // false if it cannot store blobs },});
Aget()
read call makes the blob available as anArrayBuffer
in the extension output if successful:
({ largeBlob: { blob: arrayBuffer, },});
Note:If unsuccessful, thelargeBlob
object will be returned, butblob
will not be present.
Aget()
write call indicates whether the write operation was successful with awritten
boolean value in the extension output. Atrue
value means it was written successfully to the associated authenticator, andfalse
means it was unsuccessful.
({ largeBlob: { written: true, },});
minPinLength
- Usable in: Registration (
create()
) - Processed by: Authenticator
- Specification:Minimum PIN Length Extension (minPinLength)
Allows relying parties to request the authenticator's minimum PIN length.
Input
ThepublicKey
'sextensions
property must contain aminPinLength
property with a value oftrue
:
({ extensions: { minPinLength: true, },});
Output
If the relying party is authorized to receive theminPinLength
value (if itsrpId
is present on the authenticator's authorized relying party list), the authenticator data will contain a representation of it in the following form:
({ minPinLength: uint });
If the relying party is not authorized, the extension is ignored, and no"minPinLength"
output value is provided.
payment
- Usable in: Registration (
create()
) - Processed by: User agent
- Specification:Secure Payment Confirmation
Allows a relying party to request the creation of a WebAuthn credential that may be used – by both the Relying Party and other parties – with Secure Payment Confirmation; seeUsing Secure Payment Confirmation.
Input
The inputs for thepayment
extension are defined in theAuthenticationExtensionsPaymentInputs dictionary
isPayment
A boolean that indicates that the extension is active.
rpID
TheRelying Party id of the credential(s) being used. Only used at authentication time; not registration.
topOrigin
The origin of the top-level frame. Only used at authentication time; not registration.
payeeName
The payee name, if present, that was displayed to the user. Only used at authentication time; not registration.
payeeOrigin
The payee origin, if present, that was displayed to the user. Only used at authentication time; not registration.
total
The transaction amount that was displayed to the user. Only used at authentication time; not registration. The total is of typePaymentCurrencyAmount.
instrument
The instrument details that were displayed to the user. Only used at authentication time; not registration. The instrument is of typePaymentCredentialInstrument.
Output
None
prf
- Usable in: Registration (
create()
) and authentication (get()
) - Processed by: User agent
- Specification:Pseudo-random function extension (prf)
Allows a relying party to get outputs for either one or two inputs from a pseudo-random function (PRF) associated with a credential.A PRF is effectively arandom oracle — a function that returns a random value for any given input, but will always return the same value for the same input.
The ability to generate a random number associated with a user's credential is useful in a number of cryptographic applications.For example, it can be used to generate a symmetric key for encrypting sensitive data, and that can only be decrypted by a user who has the seed and the associated authenticator.It could similarly be used to create a symmetric key for end-to-end encryption, seeded with a value from the server and unique for that credential and session.
The extension allows you to pass buffer values of typeArrayBuffer
orTypedArray
to the authenticator, which will return the result of evaluating the value with the PRF of the associated credential.This can be done in an assertion, as part of the authentication workflow — specifying the credential or credentials for which the result is to be evaluated.It can also be done when creating a credential; but fewer authenticators support the generation of outputs when creating credentials.
Input
During acreate()
call, thepublicKey
'sextensions
property may contain aprf
property which haseval
object with the propertyfirst
and optional propertysecond
.These properties are eitherArrayBuffer
orTypedArray
instances that the contain values to pass to the PRF for the credential.
For example, the definition below might be used when creating a new credential in order to create a new symmetric key from a server-provided secret.
({ extensions: { prf: { eval: { first: new TextEncoder().encode("Salt for new symmetric key") }, }, },});
The optionalsecond
property can be used if two random values need to be created for a credential, such as in a workflow where the encryption key is rotated on each session.As an example of such a workflow, in each session you pass two salts: thefirst
salt returns a value that can be used to decrypt the previous session data, while thesecond
salt returns a value that can be used to encrypt this session data.In subsequent sessions thesecond
salt is moved to the position of thefirst
salt, so the lifetime where a particular salt can be usefully compromised is bounded.
({ extensions: { prf: { eval: { first: currentSessionKey, // salt for current session second: nextSessionKey, // salt for next session }, }, },});
Thecreate()
call may reject with the following exceptions:
NotSupportedError
DomException
- The
evalByCredential
key is present in theeval
object.
- The
Note that evaluating a PRF when creating a credential may not be supported, and this would be reported in the output.You could still try evaluating the PRF in an assertion as shown below.
During aget()
call, thepublicKey
'sextensions
property may contain aprf
property with theevalByCredential
sub-property.This is an object that mapsBase64 URL-encoded credential IDs to evaluation objects with the same form as shown above.In other words, this allows you to specify values to evaluate for different credentials.
({ extensions: { prf: { evalByCredential: { "<credentialId>": { first: bufferOne, second: bufferTwo }, // … "<credentialId2>": { first: anotherBufferOne, second: anotherBufferTwo, }, }, }, },});
Theget()
call may reject with the following exceptions:
NotSupportedError
DomException
If
eval
is theprf
object, or ifallowCredentials
is empty whenevalByCredential
is not empty.SyntaxError
DomException
Any key in
evalByCredential
is the empty string or is not a valid Base64 URL encoding, or does not match the id of some element withpublicKey.allowCredentials
.
Output
A successfulcreate()
call provides the following extension output if the registered credential supports using the PRF when creating credentials.
({ prf: { enabled: true, // PRF can be used when creating credentials. results: { first: outputBuffer1, second: outputBuffer2 }, },});
Theenabled
property indicates whether the PRF can be used when creating credentials.Thefirst
andsecond
properties contain the result of evaluatingfirst
andsecond
on the input, andsecond
will be omitted if the corresponding input was not specified.
If the authenticator doesn't support using the PRF on creation, the output oncreate()
will look like this:
({ prf: { enabled: false, // PRF cannot be used when creating credentials. },});
Aget()
returns a sameprf
object with the same structure ascreate()
, except that it omits theenabled
key.The object contains PRF values that correspond to the inputs for the credential that was selected by the user.
({ prf: { results: { first: outputBuffer1, second: outputBuffer2 }, },});
Note thatenabled
is only present as an output forcreate()
, and indicates if PRF is supported by the authenticator when a credential is created.If the authenticator doesn't support PRF at all, the result for theget()
call will be:
({ prf: {},});
Specifications
Specification |
---|
Web Authentication: An API for accessing Public Key Credentials - Level 3 # sctn-defined-extensions |
Unknown specification # sctn-defined-extensions |
There are a number of places that WebAuthn extensions are specified. IANA'sWebAuthn Extension Identifiers provides a registry of all extensions, but bear in mind that some may be deprecated.