-
Notifications
You must be signed in to change notification settings - Fork 180
Explainer: WebAuthn challengeURL
Ken Buchanan <[email protected]>
Last updated: 08-Oct-2024
This explainer describes a new parameter for PublicKeyCredentialRequestOptions and PublicKeyCredentialCreationOptions that provides a URL through which the user agent can obtain a challenge, in lieu of embedding the challenge directly into the request.
When a relying party (RP) issues a Web Authentication request for an assertion or to create a new credential, it must provide a challenge – an array of random bytes – that will be signed by the credential’s private key as a defense against replay attacks.
Ideally this challenge is generated by the server in order to provide a strong assurance that the response was produced by the authenticator after the request was issued. In some scenarios this might require the client-side script to perform a fetch in order to obtain the challenge, before initiating the actual request. The fetch adds latency to the displaying of UI on the client.
This proposal provides a way for the user agent to fetch a challenge byte array after the WebAuthn operation has been initiated, allowing WebAuthn UI and the fetch to happen concurrently, improving the user experience.
Where currently challenge
is a required attribute in the dictionary for both get and create, this change would require exactly one of challenge
or challengeURL
to be present.
The new parameter would take an absolute URL from which the challenge may be obtained.
Example:
const cred = await navigator.credentials.get({
publicKey: {
challengeURL: 'https://example.com/challenge_endpoint',
rpId: 'example.com',
allowCredentials: {...}
}
});
For credential creation, the fetch is initiated immediately upon the API being called.
For assertion requests, the fetch is initiated when the user performs an authorization gesture, such as selecting an available credential, or providing user presence or user verification.
For both kinds of requests, the clientData
cannot be passed to the authenticator until the fetch response is received and processed by the user agent. The ceremony proceeds as normal once the challenge byte array is available.
The request is a credentialed HTTP GET request sent to the provided URL.
The response must have Content-type: application/octet-stream, with contents being at least 16 bytes in length, and contain no other data than the challenge.
The fetch request is credentialed (cookie- or token-bearing), as if it had been initiated directly as a Fetch API call by the RP’s script, and is similarly restricted by same-origin policy.
If the RP wishes to use a different origin in the challengeURL
from what its sign-in page was served from, it can configure cross-origin resource sharing to prevent the request’s rejection with a security error.
A challengeCallback
was previously proposed to solve a similar issue. This proposal has the RP specify a URL instead because a callback function doesn’t work for WebAuthn-based mobile APIs. Since many uses of WebAuthn are via mobile APIs, presenting a common interface is very valuable for developers and, because of this, we discarded the callback idea.