diff --git a/index.bs b/index.bs index 80717951e..6227bac49 100644 --- a/index.bs +++ b/index.bs @@ -1228,6 +1228,12 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S : Non-Discoverable Credential :: This is a [=credential=] whose [=credential ID=] must be provided in {{PublicKeyCredentialRequestOptions/allowCredentials}} when calling {{CredentialsContainer/get()|navigator.credentials.get()}} because it is not [=client-side discoverable credential|client-side discoverable=]. See also [=server-side credentials=]. +: Registrable Origin Label +:: The first [=domain label=] of the [=registrable domain=] of a [=domain=], + or null if the [=registrable domain=] is null. + For example, the [=registrable origin label=] of both `example.co.uk` and `www.example.de` is `example` + if both `co.uk` and `de` are [=public suffixes=]. + : Public Key Credential :: Generically, a *credential* is data one entity presents to another in order to *authenticate* the former to the latter [[RFC4949]]. The term [=public key credential=] refers to one of: a [=public key credential source=], the @@ -1824,11 +1830,21 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o : is present :: If |pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialRpEntity/id}} [=is not a - registrable domain suffix of and is not equal to=] |effectiveDomain|, throw a "{{SecurityError}}" {{DOMException}}. + registrable domain suffix of and is not equal to=] |effectiveDomain|, and if the client + +
+ : supports [[#sctn-related-origins|related origin requests]] + :: 1. Let |rpIdRequested| be the value of |pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialRpEntity/id}}. - : Is not present - :: Set |pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialRpEntity/id}} to - |effectiveDomain|. + 1. Run the [$related origins validation procedure$] with arguments |callerOrigin| and |rpIdRequested|. + If the result is [FALSE], throw a "{{SecurityError}}" {{DOMException}}. + + : does not support [[#sctn-related-origins|related origin requests]] + :: throw a "{{SecurityError}}" {{DOMException}}. +
+ + : is not present + :: Set |pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialRpEntity/id}} to |effectiveDomain|. Note: |pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}.{{PublicKeyCredentialRpEntity/id}} represents the @@ -2314,19 +2330,31 @@ When this method is invoked, the user agent MUST execute the following algorithm PKI-based security.
  • - If |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} is not present, then set |rpId| to - |effectiveDomain|. + If |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} +
    - Otherwise: + : is present + :: If |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} [=is not a + registrable domain suffix of and is not equal to=] |effectiveDomain|, and if the client - 1. If |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} [=is not a registrable domain suffix of and is not - equal to=] |effectiveDomain|, throw a "{{SecurityError}}" {{DOMException}}. +
    + : supports [[#sctn-related-origins|related origin requests]] + :: 1. Let |rpIdRequested| be the value of |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} + + 1. Run the [$related origins validation procedure$] with arguments |callerOrigin| and |rpIdRequested|. + If the result is [FALSE], throw a "{{SecurityError}}" {{DOMException}}. - 1. Set |rpId| to |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}}. + : does not support [[#sctn-related-origins|related origin requests]] + :: throw a "{{SecurityError}}" {{DOMException}}. +
    - Note: |rpId| represents the caller's [=RP ID=]. The [=RP ID=] defaults to being the caller's [=environment - settings object/origin=]'s [=effective domain=] unless the caller has explicitly set - |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} when calling {{CredentialsContainer/get()}}. + : is not present + :: Set |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} to |effectiveDomain|. +
    + + Note: |rpId| represents the caller's [=RP ID=]. The [=RP ID=] defaults to being the caller's [=environment + settings object/origin=]'s [=effective domain=] unless the caller has explicitly set + |pkOptions|.{{PublicKeyCredentialRequestOptions/rpId}} when calling {{CredentialsContainer/get()}}.
  • 1. Let |clientExtensions| be a new [=map=] and let |authenticatorExtensions| be a new [=map=]. @@ -3963,6 +3991,7 @@ Note: The {{UserVerificationRequirement}} enumeration is deliberately not refere "hybridTransport", "passkeyPlatformAuthenticator", "userVerifyingPlatformAuthenticator", + "relatedOrigins" }; @@ -3985,6 +4014,9 @@ Note: The {{ClientCapability}} enumeration is deliberately not referenced, see [ : userVerifyingPlatformAuthenticator :: The [=WebAuthn Client=] supports usage of a [=user-verifying platform authenticator=]. + + : relatedOrigins + :: The [=WebAuthn Client=] supports [[#sctn-related-origins|Related Origin Requests]]. ### User-agent Hints Enumeration (enum PublicKeyCredentialHints) ### {#enum-hints} @@ -4046,7 +4078,62 @@ To override this default policy and indicate that a cross-origin <{iframe}> is a [=[RPS]=] utilizing the WebAuthn API in an embedded context should review [[#sctn-seccons-visibility]] regarding [=UI redressing=] and its possible mitigations. +## Using Web Authentication across related origins ## {#sctn-related-origins} + +By default, Web Authentication requires that the [=RP ID=] be equal to the [=determines the set of origins on which the public key credential may be exercised|origin=]'s [=effective domain=], or a [=is a registrable domain suffix of or is equal to|registrable domain suffix=] of the [=determines the set of origins on which the public key credential may be exercised|origin=]'s [=effective domain=]. + +This can make deployment challenging for large environments where multiple country-specific domains are in use (e.g. example.com vs example.co.uk vs example.sg), where alternative or brand domains are required (e.g. myexampletravel.com vs examplecruises.com), and/or where platform as a service providers are used to support mobile apps. + +[=[WRPS]=] can opt in to allowing [=WebAuthn Clients=] to enable a credential to be created and used across a limited set of related [=origin|origins=]. +Such [=[RPS]=] MUST choose a common [=RP ID=] to use across all ceremonies from related origins. + +A JSON document MUST be hosted at the `webauthn` well-known URL [[!RFC8615]] for the [=RP ID=]. The JSON document MUST be returned as follows: + - The content type MUST be `application/json`. + - The top-level JSON object MUST contain a key named `origins` whose value MUST be an array of one or more strings containing web origins. + +For example, for the RP ID `example.com`: + + +{ + "origins": [ + "https://example.co.uk", + "https://example.de", + "https://example.sg", + "https://example.net", + "https://exampledelivery.com", + "https://exampledelivery.co.uk", + "https://exampledelivery.de", + "https://exampledelivery.sg", + "https://myexamplerewards.com", + "https://examplecars.com" + ] +} + + +[=WebAuthn Clients=] supporting this feature MUST support at least five [=registrable origin labels=]. Client policy SHOULD define an upper limit to prevent abuse. + +[=WebAuthn Clients=] supporting this feature SHOULD include {{ClientCapability/relatedOrigins}} in their response to [[#sctn-getClientCapabilities|getClientCapabilities()]]. + +### Validating Related Origins ### {#sctn-validating-relation-origin} + +The related origins validation procedure, given arguments |callerOrigin| and |rpIdRequested|, is as follows: + +1. Let |maxLabels| be the maximum number of [=registrable origin labels=] allowed by client policy. +1. Fetch the `webauthn` well-known URL [[!RFC8615]] for the RP ID |rpIdRequested| (i.e., https://|rpIdRequested|/.well-known/webauthn). + 1. If the fetch fails, the response does not have a content type of `application/json`, or does not have a status code (after following redirects) of 200, then throw a "{{SecurityError}}" {{DOMException}}. + 1. If the body of the resource is not a valid JSON object, then throw a "{{SecurityError}}" {{DOMException}}. + 1. If the value of the |origins| property of the JSON object is missing, or is not an array of strings, then throw a "{{SecurityError}}" {{DOMException}}. +1. Let |labelsSeen| be a new empty [=set=]. +1. [=set/For each=] |originItem| of |origins|: + 1. Let |url| be the result of running the [=URL parser=] with |originItem| as the input. If that fails, [=continue=]. + 1. Let |domain| be the [=effective domain=] of |url|. If that is null, [=continue=]. + 1. Let |label| be [=registrable origin label=] of |domain|. + 1. If |label| is empty or null, [=continue=]. + 1. If the [=set/size=] of |labelsSeen| is greater than or equal to |maxLabels| and |labelsSeen| does not [=set/contain=] |label|, [=continue=]. + 1. If |callerOrigin| and |url| are [=same origin=], return [TRUE]. + 1. If the [=set/size=] of |labelsSeen| is less than |maxLabels|, [=set/append=] |label| to |labelsSeen|. +1. Return [FALSE]. # WebAuthn Authenticator Model # {#sctn-authenticator-model} @@ -8616,6 +8703,11 @@ For example: {{CollectedClientData/origin}} to exactly equal some element of a list of allowed origins, for example the list ["https://example.org", "https://login.example.org"]. +- A web application leveraging [[#sctn-related-origins|related origin requests]] might also require + {{CollectedClientData/origin}} to exactly equal some element of a list of allowed origins, + for example the list ["https://example.co.uk", "https://example.de", "https://myexamplerewards.com"]. + This list will typically match the origins listed in the well-known URI for the [=RP ID=]. See [[#sctn-related-origins]]. + - A web application served at a large set of domains that changes often might parse {{CollectedClientData/origin}} structurally and require that the URL scheme is https and that the authority equals or is any subdomain of the [=RP ID=] - for example,