webuathn
webauthn logo

Question: Explain the algorithm for using an existing credential to generate an assertion via the [[DiscoverFromExternalSource]]() method.

Let’s break down the [[DiscoverFromExternalSource]]() method, which is the heart of WebAuthn authentication ceremonies using existing credentials. This is a lengthy algorithm, so we’ll focus on the key concepts and steps.

Purpose:

The primary function of [[DiscoverFromExternalSource]]() is to allow the user agent (browser) to discover and interact with a user’s available authenticators to generate an assertion for a website. This happens when a Relying Party (website) calls navigator.credentials.get({publicKey:...}) to initiate authentication.

High-Level Flow:

  1. Input Validation & Setup: The method begins by validating the inputs provided in the PublicKeyCredentialRequestOptions object. This includes checking timeouts, the Relying Party ID, and ensuring the request originates from a secure context.
  2. Conditional Mediation: If conditional mediation is enabled (options.mediation is set to conditional), the algorithm optimizes for discoverable credentials (passkeys). This means the user might not see a prominent prompt unless credentials are discovered.
  3. Extension Handling: The method processes client extensions (e.g., appid, uvm, largeBlob). These extensions can modify how the authentication works or provide additional information to the Relying Party.
  4. Discovery and User Interaction: The algorithm loops through the available authenticators. It filters out unsuitable ones based on user verification requirements, credential restrictions, and transport hints. This is where the user will typically see a prompt to choose a credential and authorize its use (e.g., by providing a biometric scan, PIN, or touch).
  5. Assertion Generation: Once the user selects and authorizes a credential, the authenticator generates the assertion signature (authenticatorGetAssertion). This signature cryptographically proves the user controls the private key associated with the credential.
  6. Response Construction: The algorithm constructs a PublicKeyCredential object containing the assertion data (AuthenticatorAssertionResponse). This object is returned to the Relying Party script.
  7. Error Handling: If the process encounters errors (e.g., timeout, user cancellation, no suitable credential), appropriate exceptions are thrown. Privacy-sensitive errors are masked to prevent information leakage to the Relying Party.

Important Details:

  • Credential Discovery: The allowCredentials member of PublicKeyCredentialRequestOptions plays a key role in credential discovery.
    • Non-Empty allowCredentials: The algorithm attempts to find authenticators matching the credential IDs provided. This is used when the user has been pre-identified (e.g., entered a username).
    • Empty allowCredentials: Only discoverable credentials are used. This is for passwordless flows where the user isn’t explicitly identified beforehand.
  • Authenticator Selection: The algorithm considers authenticator capabilities (userVerification, authenticatorAttachment) to filter eligible authenticators.
  • User Consent: An essential security aspect is user consent. The authenticator must obtain user authorization before generating the assertion. This is usually done through a platform UI that requests a biometric gesture, PIN, or button press.
  • Privacy: The method includes safeguards to prevent malicious Relying Parties from probing for credentials without user consent. Error handling masks sensitive errors to avoid leaking information about the user’s registered credentials.

In Summary:

The [[DiscoverFromExternalSource]]() method is a complex process with multiple steps for securely obtaining an assertion from an authenticator. It involves coordinating between the Relying Party, the user agent, and the authenticator, while protecting user privacy and ensuring the integrity of the authentication ceremony.

Question: Explain the procedure for issuing a credential request to a specific authenticator.

The procedure for issuing a credential request to a specific authenticator is a sub-algorithm of the [[DiscoverFromExternalSource]]() internal method. This method is invoked when a Relying Party calls navigator.credentials.get() to request an assertion from an existing credential.

Here’s a breakdown of the procedure, factoring in the various UI contexts and user mediation:

1. Context and Arguments

This sub-algorithm is called within different contexts of [[DiscoverFromExternalSource]](), depending on whether the authentication ceremony uses:

  • Conditional Mediation: In this case, the sub-algorithm is executed when the user interacts with a form control tagged for WebAuthn (e.g., by clicking an input field with a relevant autocomplete attribute).
  • Non-Conditional Mediation: If conditional mediation is not used, the sub-algorithm runs when an authenticator becomes available on the client device.

The algorithm receives the following arguments:

  • authenticator: A handle identifying a currently available authenticator.
  • savedCredentialIds: A map of authenticators to credential IDs (this is modified by the algorithm).
  • pkOptions: The PublicKeyCredentialRequestOptions object, defining the request’s attributes.
  • rpId: The Relying Party ID.
  • clientDataHash: The hash of the serialized client data.
  • authenticatorExtensions: A map containing data for authenticator extensions.

2. Preliminary Checks

  • User Verification: If pkOptions.userVerification is “required” but the authenticator lacks user verification capability, the algorithm returns false, signaling the authenticator is ineligible.
  • Effective User Verification: Determine the effective user verification requirement (true or false) based on pkOptions.userVerification and the authenticator’s capability.
  • Enterprise Attestation: If pkOptions.attestation is “enterprise”, determine if enterprise attestation is allowed for the RP ID.
  • Attestation Formats: Set attestationFormats based on pkOptions.attestation and pkOptions.attestationFormats.

3. Filtering Allowed Credentials

  • If pkOptions.allowCredentials is not empty (meaning the Relying Party provided specific credential IDs), the client filters the provided list to only those credentials believed to be bound to this authenticator, matching on rpId, id, and type.
  • If the filtered list is empty, return false (the authenticator is ineligible).
  • If the filtered list has one entry, store its id in savedCredentialIds. This is an optimization to avoid unnecessary communication with the authenticator if it only has one matching credential.

4. Transport Selection

  • Aggregate distinct transport mechanisms listed in the filtered credentials (if any).
  • Non-empty transports: The client selects one transport based on the aggregated list and its knowledge of the authenticator.
  • Empty transports: The client relies on its configuration to determine the appropriate transport for the authenticator.

5. Invoking authenticatorGetAssertion

  • Using the selected transport, the client invokes the authenticatorGetAssertion operation on the authenticator, passing the necessary parameters: rpId, clientDataHash, allowCredentialDescriptorList (if applicable), userVerification, enterpriseAttestationPossible, attestationFormats, and authenticatorExtensions.

6. Success

  • The algorithm returns true, indicating the credential request was successfully issued to the authenticator.

This procedure ensures that credential requests are only sent to authenticators that are capable of handling them and provides a streamlined approach to managing communication between the client and a specific authenticator.

Question: How does the mediation option impact the UI displayed during credential discovery?

The mediation option in PublicKeyCredentialRequestOptions significantly impacts the UI displayed during credential discovery in WebAuthn. It controls the level of user interaction required before an authenticator can be selected. Here’s a breakdown:

1. mediation: "optional" (Default)

  • UI Behavior: The user agent has full freedom to determine the UI. It could display a prominent modal dialog prompting the user to choose an authenticator or could silently attempt to use available platform authenticators.
  • User Interaction: No explicit user interaction is required.
  • Use Case: Suitable for most typical scenarios where explicit user interaction is not always necessary.

2. mediation: "required"

  • UI Behavior: The user agent must display a modal dialog prompting the user to select an authenticator. This ensures the user is actively involved in choosing the credential.
  • User Interaction: Explicit user interaction is mandatory.
  • Use Case: Ideal for high-security scenarios or when the user must consciously confirm their choice of authenticator.

3. mediation: "conditional"

This is where the UI gets interesting: the table below summarizes the UI requirements for get() and create() calls

UI neededUI not needed
get()1. Show the discovered credentials in a picker
2. If no credentials found, then show a prompt specific to the type of the credential. For example, attach an authenticator
UI is always needed
create()If user has not previously consented to the credential creation OR user agent has not recently mediated an assertion, then modal UI is neededIf user has previously consented to the credential creation and user agent has recently mediated an assertion, then no further prominent modal UI is needed
Conditional mediation UI requirements

4. mediation: "silent"

User mediation is suppressed for the given operation.

Question: What happens if the lifetimeTimer expires during credential discovery?

Let’s break down what happens when the lifetimeTimer expires during WebAuthn credential discovery:

Context:

  • lifetimeTimer: This timer represents the maximum time a WebAuthn Relying Party (website) is willing to wait for the user to interact with their authenticator and complete the credential discovery process (part of user login).
  • Credential Discovery: This occurs when a user attempts to log in and the website calls navigator.credentials.get(). The browser searches for suitable authenticators and prompts the user to choose and interact with one (e.g., tap a security key, use a fingerprint reader).

Sequence of Events Upon lifetimeTimer Expiry:

  1. Cancel Pending Requests: The browser immediately stops trying to communicate with any authenticators it has sent requests to. It will invoke the authenticatorCancel operation on each of these authenticators.
  2. Remove Authenticators from Queue: The browser removes those authenticators from its internal list (issuedRequests) of devices it was actively waiting for a response from.
  3. NotAllowedError: The browser throws a “NotAllowedError” DOMException. This signals to the website’s JavaScript code that the credential discovery process failed because the user took too long.

Implications for the User and the Website:

  • User Experience: The user will likely see an error message indicating that the login attempt timed out. The experience will vary depending on how the website handles this error.
  • Website Logic: The Relying Party’s script (the website’s JavaScript code) needs to catch this NotAllowedError and respond appropriately. Common responses include:
    • Displaying a timeout message to the user.
    • Giving the user the option to restart the authentication process.
    • Falling back to a different authentication method (e.g., password).

Security Considerations:

  • Preventing Information Leaks: Importantly, this error is thrown only after the lifetimeTimer expires. This is a crucial privacy measure. If an error were thrown immediately when no suitable credential was found, a malicious website could probe for the existence of specific credentials by timing how quickly the error occurred.

Key Points:

  • The lifetimeTimer protects both the user and the website from an authentication process hanging indefinitely.
  • The error handling after the timer expires is essential for a smooth user experience.
  • The delayed error mechanism helps to maintain user privacy.

Question: What are the conditions under which the client returns a NotAllowedError DOMException during credential discovery?

The WebAuthn specification outlines several specific conditions under which the client MUST return a “NotAllowedError” DOMException during the credential discovery process, which is part of the authentication ceremony initiated by navigator.credentials.get(). These conditions fall under two primary categories: user interaction and security/policy violations.

1. User Interaction:

  • User Cancels: If the user explicitly cancels the authentication ceremony through the user agent’s UI, the client MUST stop interacting with all authenticators and throw a “NotAllowedError” DOMException. This respects the user’s choice to not authenticate at this time.
  • No Eligible Credentials (Non-Conditional Mediation): If the Relying Party provides a non-empty allowCredentials list, the user agent is operating under non-conditional mediation, and determines that no authenticator is available for any of the listed credentials, the client MUST indicate this to the user. Once the user acknowledges this notification (e.g., closes a dialog box), the client MUST throw a “NotAllowedError” DOMException. This scenario occurs when the client can confidently determine no credential matching the Relying Party’s request can be used.
  • Timeout (Non-Conditional Mediation): If the timeout specified in the PublicKeyCredentialRequestOptions expires before a successful assertion is generated, and the user agent is operating under non-conditional mediation, the client MUST stop interacting with all authenticators and throw a “NotAllowedError” DOMException. This ensures the Relying Party doesn’t wait indefinitely.

2. Security and Policy Violations:

  • Cross-Origin without Transient Activation: If the navigator.credentials.get() call originates from a cross-origin iframe without transient activation (meaning user interaction hasn’t recently occurred in that context), the client MUST throw a “NotAllowedError” DOMException. This prevents silent access to credentials from untrusted contexts.
  • Opaque Origin: If the caller’s origin is an opaque origin, the client MUST throw a “NotAllowedError” DOMException. Opaque origins lack the necessary information to properly scope credentials. (See step 13 of [[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) in Section 5.1.4.1).
  • Invalid RP ID: If the RP ID (rpId) provided in the PublicKeyCredentialRequestOptions is not a registrable domain suffix of, nor equal to, the caller’s origin’s effective domain, the client MUST throw a “SecurityError” DOMException. This ensures that credentials are only used within their intended scope.
  • General Errors Before User Consent (Non-Conditional Mediation): If an authenticator returns any error other than “InvalidStateError” during an authentication ceremony under non-conditional mediation, and the user has not yet explicitly consented to use a credential, the client MUST throw a “NotAllowedError” DOMException. This hides specific error details from the Relying Party to prevent potential information leakage about the user’s credentials.
  • No Matching Credentials After User Consent (Non-Conditional Mediation): After the user has explicitly selected and authorized a credential under non-conditional mediation, if the client determines this selection doesn’t fulfill the Relying Party’s request (e.g., due to mismatched RP ID or credential type), the client MUST throw a “NotAllowedError” DOMException. This prevents information leakage about the user’s selection.

Important Note:

The rationale behind returning “NotAllowedError” in most cases where the user hasn’t given explicit consent is to prevent information leakage that could compromise user privacy. The client deliberately avoids revealing to the Relying Party whether a credential exists or not, or why an operation failed, unless the user has explicitly authorized the use of a specific credential.

It’s crucial for Relying Parties to handle “NotAllowedError” gracefully in their authentication flows, providing appropriate feedback to the user without revealing details about the underlying cause of the error.

Question: How does the client handle the “InvalidStateError” returned by an authenticator during credential creation?

The InvalidStateError during credential creation is a special case, and it’s handled differently from other errors. Here’s a breakdown of how the client (user agent) handles it according to the WebAuthn spec:

Context:

The authenticator returns InvalidStateError specifically when:

  • The excludeCredentials list provided by the Relying Party contains a credential that is already bound to the authenticator.
  • The user has already provided consent for the operation (meaning they’ve gone through the authorization gesture).

Client’s Actions:

  1. Isolate the Error: The client first removes the authenticator that returned InvalidStateError from the set of issuedRequests (authenticators currently being processed).
  2. Cancel Other Requests: The client then invokes the authenticatorCancel operation on all remaining authenticators in the issuedRequests set. This ensures any ongoing credential creation attempts on other authenticators are stopped.
  3. Throw a Specific Exception: The client throws an InvalidStateError DOMException. This signals to the Relying Party’s script that the operation failed due to the presence of an excluded credential.

Reasoning for Special Handling:

  • Explicit User Consent: Unlike other errors, InvalidStateError in this case implies the user has already consented to create a credential. The spec aims to be transparent about this to the Relying Party.
  • Guiding User Experience: By throwing a distinct InvalidStateError, the client provides the Relying Party with information to guide the user experience. The Relying Party can now inform the user about the presence of an excluded credential and guide them to use a different authenticator.

In Summary:

The client handles InvalidStateError during credential creation by stopping the operation, canceling other requests, and throwing a specific exception to inform the Relying Party about the presence of an excluded credential on the authenticator. This allows for a more user-friendly experience by enabling the Relying Party to guide the user appropriately.

Question: Describe the purpose and content of the AuthenticatorResponse interface.

Let’s break down the AuthenticatorResponse interface in WebAuthn.

Purpose

The AuthenticatorResponse interface serves as the foundational structure for responses received from authenticators (like security keys or platform authenticators) during WebAuthn ceremonies. It doesn’t hold the complete response on its own; instead, it acts as the base for two more specific response interfaces:

  • AuthenticatorAttestationResponse: Used during credential registration, containing details about the newly created credential.
  • AuthenticatorAssertionResponse: Used during authentication, providing proof of possession of the credential and user consent.

Think of AuthenticatorResponse as the common ground, providing a consistent way to handle the initial part of any authenticator’s reply.

Content

The AuthenticatorResponse interface has only one attribute:

  • clientDataJSON: This is a read-only ArrayBuffer holding a JSON serialization of the “client data”. This “client data” is crucial for security. It includes information like:
    • type: Indicates whether the request was for credential creation (webauthn.create) or assertion (webauthn.get).
    • challenge: The randomly generated challenge provided by the Relying Party, proving the request is fresh and not a replay.
    • origin: The origin of the web page requesting the WebAuthn operation.
    • crossOrigin: Indicates whether the operation originated from a cross-origin iframe.

Important Notes

  • The clientDataJSON is not sent directly to the authenticator. The client (browser) computes a hash of this data and sends it. The authenticator then signs this hash, along with other data, ensuring the response is linked to the specific request.
  • AuthenticatorResponse itself is not used directly by Relying Parties. They work with its derived interfaces, AuthenticatorAttestationResponse and AuthenticatorAssertionResponse, for the complete response data.

Example (Simplified)

interface AuthenticatorResponse {
    readonly attribute ArrayBuffer clientDataJSON;
};

Question: What is the role of the clientDataJSON attribute in authenticator responses?

What is clientDataJSON?

clientDataJSON is a JSON-encoded data structure that captures important contextual information about the WebAuthn request. It is generated by the client (typically the web browser) and passed to the authenticator during both registration and authentication ceremonies. The authenticator includes this data, unaltered, in its response.

Key information included in clientDataJSON:

  • type: Indicates the type of WebAuthn operation: "webauthn.create" for registration, "webauthn.get" for authentication.
  • challenge: The base64url-encoded challenge generated by the Relying Party. This challenge serves as a one-time, unpredictable value to prevent replay attacks.
  • origin: The origin (protocol, domain, port) of the Relying Party’s website that initiated the WebAuthn request.
  • topOrigin (optional): The origin of the top-level browsing context. This field is included if the request originates from an iframe in a different origin than the top-level page.
  • crossOrigin (optional): A boolean flag indicating whether the request came from a cross-origin context.

How clientDataJSON contributes to security:

  1. Binding the signature to the Relying Party’s website: The inclusion of the origin field binds the authenticator’s signature to the specific website that initiated the request. This ensures that the signature cannot be replayed on a different website.
  2. Preventing replay attacks: The challenge field acts as a unique nonce, ensuring that the same signature cannot be used for multiple requests.
  3. Enforcing scope: The origin and topOrigin fields, along with the Relying Party ID (rpId), help enforce the scope of the credential. This prevents a credential registered for one website from being used on another, even if they share the same domain.
  4. Transparency and verifiability: The clientDataJSON is returned to the Relying Party, allowing them to verify the context of the request and ensure its authenticity.

Question: Explain the purpose and content of the AuthenticatorAttestationResponse interface.

Let’s break down the AuthenticatorAttestationResponse interface in WebAuthn.

Purpose

The AuthenticatorAttestationResponse is the authenticator’s reply after a successful WebAuthn registration ceremony (initiated by navigator.credentials.create()). It’s essentially a package of data that proves:

  1. A new credential was created: This includes the credential’s public key.
  2. The credential was created by a specific authenticator: This is where attestation comes in, providing details about the authenticator’s origin and capabilities.

Think of it as a receipt from the authenticator, confirming the new credential’s birth certificate and providing information about its maker.

Content

The AuthenticatorAttestationResponse interface has the following attributes:

  • clientDataJSON: (Inherited from AuthenticatorResponse)
    • This is a byte array containing the client data in JSON format. It holds information about the registration request itself, including the challenge, origin, and whether the request originated from a cross-origin iframe.
    • It’s important to note that the client (browser) generates this data, but the authenticator includes it in the response for verification.
  • attestationObject:
    • This is a byte array containing the attestation object, the heart of the attestation process. The attestation object has two main components:
      • Authenticator Data (authData): This includes:
        • The authenticator’s AAGUID (a globally unique identifier).
        • The credential ID (a unique identifier for this credential).
        • The credential public key.
        • Flags indicating user presence, user verification, etc.
        • A signature counter (optional but recommended for security).
      • Attestation Statement (attStmt):
        • This is a cryptographically signed statement generated by the authenticator, providing evidence about the authenticator’s origin and the data it generated (including the credential public key).
        • The format and content of the attestation statement depend on the specific attestation type used by the authenticator (e.g., packed, TPM, Android Key, etc.).
  • getTransports():
    • Returns a list of transports (e.g., USB, NFC, BLE, internal) that the authenticator supports.
  • getAuthenticatorData():
    • A convenience method that extracts and returns the authenticator data directly from the attestationObject.
  • getPublicKey():
    • Another convenience method that extracts the credential public key in a standard SubjectPublicKeyInfo format (if the client understands the public key algorithm).
  • getPublicKeyAlgorithm():
    • Returns the COSE Algorithm Identifier for the credential public key, providing details about the signing algorithm.

[[transports]]

  • This internal slot contains the information returned by getTransports().

In a nutshell, the AuthenticatorAttestationResponse gives the Relying Party the raw materials to verify that the new credential is genuine and to learn more about the authenticator that generated it. The Relying Party’s server can then make trust decisions based on this information and store the necessary data for future authentication ceremonies.

Reference

Web Authentication: An API for accessing Public Key Credentials – Level 3 (w3.org)

Leave a Reply

Your email address will not be published. Required fields are marked *