Skip to content

Explainer: WebAuthn challengeURL

Ken Buchanan edited this page Nov 25, 2024 · 5 revisions

Explainer: WebAuthn challengeURL

Author:

Ken Buchanan <kenrb@chromium.org>

Last updated: 25-Nov-2024

Summary

This explainer describes a new parameter for PublicKeyCredentialRequestOptions that provides a URL through which the user agent can obtain a challenge, in lieu of embedding the challenge directly into the request.

Background

When a relying party (RP) issues a Web Authentication request for an assertion 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. It can also reduce the number of challenges that the RP has to generate, since no challenge will be fetched for a conditional UI call in which the user does not select a credential.

API

Where currently challenge is a required attribute in the dictionary, this change would require exactly one of challenge or challengeURL to be present.

The new parameter would take a URL from which the challenge may be obtained. The URL may be absolute or relative.

Example:

const cred = await navigator.credentials.get({
  publicKey: {
    challengeURL: 'https://example.com/challenge_endpoint',
    rpId: 'example.com',
    allowCredentials: {...}
  }
});

Challenge fetch behavior

Integration into ceremonies

The fetch is initiated when the user performs an authorization gesture during an assertion request, such as selecting an available credential, or providing user presence or user verification.

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.

Challenge request

The request is an uncredentialed HTTP POST request sent to the provided URL. The URL must use https and be same-site with the RP's page. It can include query parameters with session information.

Challenge response

The response must have Content-type: application/x-webauthn-challenge as a header. Its contents must be at least 16 bytes in length, and contain no data other than the challenge.

Security

If this is replacing a flow in which the RP performs a fetch for the challenge and then calls the WebAuthn API, an ideal form of this feature would be to add an instance of Fetch's resourceRequest into the credential options, and have the browser treat it like a normal fetch request. However, this is problematic for requests that are passed to passkey providers that are not browsers, including calls to platform APIs. These will typically not have access to the user's session cookies, and lack important context to perform a fetch securely.

The primary concern is that this could be a vector to access or modify cross-origin resources that the page's script itself cannot access. The software component that performs the fetch (fetching application) could easily provide a bypass to browser-implemented protections, such as Private Network Access restrictions. The risk is particularly acute since data obtained from the fetch will be returned to the RP as part of the response's clientData.

The following constraints can be applied to mitigate that risk:

  • The user agent must reject any URL that does not use the https scheme.
  • The user agent must reject any URL that is not same-site with the RP (i.e. under the same registrable domain).
  • The user agent must ensure that the request conforms to page's Content Security Policy, in particular the default-src directive.
  • The fetching application must send the challengeURL request uncredentialed.
  • The fetching application must not follow redirects.
  • The fetching application must reject a response if there is any error in TLS certificate validation.
  • The fetching application must reject a response that does not have the specified Content-type header.

Alternatives

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.