diff --git a/spec/index.bs b/spec/index.bs index b2462419..ef6c714d 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -511,7 +511,7 @@ When asked to attempt to disconnect given an {{IdentityCredentialDisc |provider| and |globalObject|. 1. If |config| is failure, [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}}. 1. Let |disconnectUrl| be the result of [=computing the manifest URL=] given |provider|, - |config|.{{IdentityProviderAPIConfig/disconnect_endpoint}}, and |globalObject|. + |config|.{{IdentityProviderAPIConfig/disconnect_endpoint}}, true, and |globalObject|. 1. If |disconnectUrl| is failure, [=reject=] |promise| with a "{{NetworkError}}" {{DOMException}}. 1. [=Send a disconnect request=] with |disconnectUrl|, |options|, and |globalObject|, and let @@ -593,7 +593,50 @@ dictionary DisconnectedAccount { -### The CredentialRequestOptions ### {#browser-api-credential-request-options} +## The IdentityCredentialError Interface ## {#browser-api-identity-credential-error-interface} + + +This specification introduces a new type of {{DOMException}}, the {{IdentityCredentialError}}. It +is used when the [=user agent=] does not receive an identity assertion after the user has requested +to use a federated account. + + +dictionary IdentityCredentialErrorInit { + DOMString error; + DOMString url; +}; + + +
+  [Exposed=Window, SecureContext]
+  interface IdentityCredentialError : DOMException {
+    constructor(optional DOMString message = "", optional IdentityCredentialErrorInit options = {});
+    readonly attribute DOMString error;
+    readonly attribute DOMString url;
+  };
+
+ +
+Given a |realm|, a {{DOMString}} |message|, and an {{IdentityCredentialErrorInit}} |options|, the +{{IdentityCredentialError/constructor()}} must run the following steps: + 1. Invoke the {{DOMException}}'s {{DOMException/constructor()}}, passing |realm| and |message|. + 1. Set this.{{IdentityCredentialError/error}} to |options|.{{IdentityCredentialError/error}} + if it is present in |options|, or to "" otherwise. + 1. Set this.{{IdentityCredentialError/url}} to |options|.{{IdentityCredentialError/url}} + if it is present in |options|, or to "" otherwise. +
+ +
+ : {{IdentityCredentialError/error}} + :: The {{IdentityCredentialError/error}}'s attribute getter returns the value it is set to. + It represents the type of error which resulted in an {{IdentityCredential}} not being created. + : {{IdentityCredentialError/url}} + :: The {{IdentityCredentialError/url}}'s attribute getter returns the value it is set to. + It represents a URL where the user can learn more information about the error. +
+ + +## The CredentialRequestOptions ## {#browser-api-credential-request-options} This section defines the dictionaries passed into the JavaScript call: @@ -678,7 +721,7 @@ dictionary IdentityProviderRequestOptions : IdentityProviderConfig { -### The \[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) internal method ### {#browser-api-rp-sign-in} +## The \[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) internal method ## {#browser-api-rp-sign-in} The {{Credential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} @@ -716,7 +759,7 @@ requests. When the {{IdentityCredential}}'s \[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors) algorithm is invoked, the user agent MUST execute the following steps. This returns an -{{IdentityCredential}} (or throws an error to the caller). +{{IdentityCredential}} or throws an error to the caller. 1. Assert: These steps are running [=in parallel=]. 1. If the [=list/size=] of @@ -739,10 +782,10 @@ algorithm is invoked, the user agent MUST execute the following steps. This retu 1. Let |credential| be the result of running [=create an IdentityCredential=] with |provider|, |options|, and |globalObject|. 1. If |credential| is a pair: - 1. Let |throwImmediately| be the value of the second element of the pair. + 1. Let |delayException| be the value of the second element of the pair. 1. The user agent SHOULD wait a random amount of time before the next step if all of the following conditions hold: - * |throwImmediately| is false + * |delayException| is true * The promise rejection delay was not disabled by [=setdelayenabled|user agent automation=] * The user agent has not implemented another way to prevent @@ -757,7 +800,8 @@ algorithm is invoked, the user agent MUST execute the following steps. This retu However, UAs may have different UI approaches here and prevent it in a different way. 1. [=Queue a global task=] on the [=DOM manipulation task source=] to throw a new "{{NetworkError}}" {{DOMException}}. - 1. Otherwise, return |credential|. + 1. Otherwise, return |credential| (this may throw, since it may be an + {{IdentityCredentialError}}). @@ -765,14 +809,15 @@ algorithm is invoked, the user agent MUST execute the following steps. This retu The create an IdentityCredential algorithm invokes the various FedCM fetches, shows the user -agent UI, and creates the {{IdentityCredential}} that is then returned to the [=RP=]. +agent UI, and creates the {{IdentityCredential}} or {{IdentityCredentialError}} which is then +returned to the [=RP=].
To create an IdentityCredential given an {{IdentityProviderRequestOptions}} |provider|, a {{CredentialRequestOptions}} |options|, and a -|globalObject|, run the following steps. This returns an {{IdentityCredential}} -or a pair (failure, bool), where the bool indicates whether to skip delaying -the exception thrown. +|globalObject|, run the following steps. This returns an {{IdentityCredential}}, an +{{IdentityCredentialError}}, or a pair (failure, bool), where the bool indicates whether to delay +throwing the exception. 1. Assert: These steps are running [=in parallel=]. 1. Let |loginStatus| be the result of [=get the login status=] with the [=/origin=] of |provider|'s {{IdentityProviderConfig/configURL}}. @@ -872,32 +917,35 @@ the exception thrown. 1. If |acc| is [=eligible for auto reauthentication=] given |provider|, and |globalObject|, set |registeredAccount| to |acc| and increase |numRegisteredAccounts| by 1. 1. Let |permission|, |disclosureTextShown|, and |isAutoSelected| be set to false. - 1. If |mediation| is not "{{CredentialMediationRequirement/required}}", |requiresUserMediation| - is false, and |numRegisteredAccounts| is equal to 1: - 1. Set |account| to |registeredAccount| and |permission| to true. When doing this, the user - agent MAY show some UI to the user indicating that they are being - auto-reauthenticated. - 1. Set |isAutoSelected| to true. - 1. Otherwise, if |mediation| is "{{CredentialMediationRequirement/silent}}", return (failure, true). - 1. Otherwise, if |accountsList|'s size is 1: - 1. Set |account| to |accountsList|[0]. - 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| returns - [=compute the connection status/connected=], show a dialog to request user permission to sign - in via |account|, and set the result in |permission|. The user agent MAY use |options|'s - {{IdentityCredentialRequestOptions/context}} to customize the dialog. - 1. Otherwise, let |permission| be the result of running [=request permission to sign-up=] - algorithm with |account|, |config|, |provider|, and |globalObject|. Also set - |disclosureTextShown| to true. - 1. Otherwise: - 1. Set |account| to the result of running the [=select an account=] from the - |accountsList|. - 1. If |account| is failure, return (failure, true). - 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| is - [=compute the connection status/connected=], set |permission| to true. - 1. Otherwise: - 1. Let |permission| be the result of running the [=request permission to sign-up=] - algorithm with |account|, |config|, |provider|, and |globalObject|. - 1. Set |disclosureTextShown| to true. + 1. Perform the set of instructions corresponding to the first matching statement: +
+
If |mediation| is not "{{CredentialMediationRequirement/required}}", |requiresUserMediation| + is false, and |numRegisteredAccounts| is equal to 1:
+ 1. Set |account| to |registeredAccount|, |permission| to true, and |isAutoSelected| to true. + When doing this, the user agent MAY show some UI to the user indicating that they are being + auto-reauthenticated. +
If |mediation| is "{{CredentialMediationRequirement/silent}}":
+ 1. Return (failure, true). +
If |accountsList|'s size is 1:
+ 1. Set |account| to |accountsList|[0]. + 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| returns + [=compute the connection status/connected=], show a dialog to request user permission to sign + in via |account|, and set the result in |permission|. The user agent MAY use |options|'s + {{IdentityCredentialRequestOptions/context}} to customize the dialog. + 1. Otherwise, let |permission| be the result of running [=request permission to sign-up=] + algorithm with |account|, |config|, |provider|, and |globalObject|. Also set + |disclosureTextShown| to true. +
Otherwise:
+ 1. Set |account| to the result of running the [=select an account=] from the + |accountsList|. + 1. If |account| is failure, return (failure, true). + 1. If [=compute the connection status=] of |account|, |provider| and |globalObject| is + [=compute the connection status/connected=], set |permission| to true. + 1. Otherwise: + 1. Let |permission| be the result of running the [=request permission to sign-up=] + algorithm with |account|, |config|, |provider|, and |globalObject|. + 1. Set |disclosureTextShown| to true. +
1. Wait until the [=user agent=]'s dialogs requesting for user choice or permission to be closed, if any are created in the previous steps. 1. Assert: |account| is not null. @@ -905,6 +953,21 @@ the exception thrown. 1. Let |credential| be the result of running the [=fetch an identity assertion=] algorithm with |account|'s {{IdentityProviderAccount/id}}, |disclosureTextShown|, |isAutoSelected|, |provider|, |config|, and |globalObject|. + 1. If |credential| is an {{IdentityCredentialError}}: + 1. The [=user agent=] MUST show UI to the user indicating that the identity assertion fetch + has failed. + 1. The [=user agent=] MAY use the {{IdentityCredentialError/error}} in its UI to provide a + more specific error message to the user. For instance, if the value is one of + "invalid_request", "unauthorized_client", "access_denied", "server_error", and + "temporarily_unavailable", the [=user agent=] may note the reason in a user-friendly + manner. See [here](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1) for + more details about these. + 1. The [=user agent=] MAY use the {{IdentityCredentialError/url}} to display a hyperlink + in its UI so the user can click on it to learn more information about the error message. + 1. Wait until the UI is acknowledged or dismissed by the user. + + Note: this wait means the [=user agent=] rejects the call only after the UI has been + closed by the user. 1. Return |credential|.
@@ -1021,7 +1084,7 @@ or failure. in |config|. 1. If one of the previous two steps threw an exception, set |config| to failure. 1. Set |config|.{{IdentityProviderAPIConfig/login_url}} to the result of [=computing - the manifest URL=] with |provider|, |config| and |globalObject|. + the manifest URL=] with |provider|, |config|, true, and |globalObject|. 1. If |config|.{{IdentityProviderAPIConfig/login_url}} is null, return failure. 1. Wait for both |config| and |configInWellKnown| to be set. 1. If |configInWellKnown| is true, return |config|. Otherwise, return failure. @@ -1075,7 +1138,7 @@ To fetch the accounts given an {{IdentityProviderAPIConfig}} |config| {{IdentityProviderRequestOptions}} |provider|, and |globalObject|, run the following steps. This returns an {{IdentityProviderAccountList}}. 1. Let |accountsUrl| be the result of [=computing the manifest URL=] given |provider|, - |config|["{{IdentityProviderAPIConfig/accounts_endpoint}}"], and |globalObject|. + |config|["{{IdentityProviderAPIConfig/accounts_endpoint}}"], true, and |globalObject|. 1. If |accountsUrl| is failure, return an empty list. 1. Let |request| be a new request as follows: @@ -1188,16 +1251,21 @@ To fetch the account picture given an {{IdentityProviderAccount}} |ac The fetch an identity assertion algorithm is invoked after the user has granted permission to use FedCM with a specific [=IDP=] account. It fetches the [=identity assertion endpoint=] to obtain -the token that will be provided to the [=RP=]. +the token or error that will be provided to the [=RP=].
To fetch an identity assertion given a {{USVString}} |accountId|, a boolean |disclosureTextShown|, a boolean |isAutoSelected|, an {{IdentityProviderRequestOptions}} |provider|, an {{IdentityProviderAPIConfig}} |config|, - and |globalObject|, run the following steps. This returns an {{IdentityCredential}} or failure. + and |globalObject|, run the following steps. This returns an {{IdentityCredential}} or an + {{IdentityCredentialError}}. 1. Let |tokenUrl| be the result of [=computing the manifest URL=] given |provider|, - |config|["{{IdentityProviderAPIConfig/id_assertion_endpoint}}"], and |globalObject|. - 1. If |tokenUrl| is failure, return failure. + |config|["{{IdentityProviderAPIConfig/id_assertion_endpoint}}"], true, and |globalObject|. + 1. If |tokenUrl| is failure: + 1. [=Queue a global task=] on the [=DOM manipulation task source=] given |globalObject| to + let |error| be a new {{IdentityCredentialError}} given |globalObject|'s + [=global object/realm=]. + 1. Wait for |error| to be set, and return it. 1. Let |requestBody| be the result of running [=urlencoded serializer=] with a list containing: 1. ("client_id", |provider|'s {{IdentityProviderConfig/clientId}}) 1. ("nonce", |provider|'s {{IdentityProviderRequestOptions/nonce}}) @@ -1237,14 +1305,31 @@ To fetch an identity assertion given a {{USVString}} set to the following steps given a response |response| and |responseBody|: 1. Let |json| be the result of [=extract the JSON fetch response=] from |response| and |responseBody|. - 1. [=converted to an IDL value|Convert=] |json| to an {{IdentityProviderToken}}, |token|. - 1. If one of the previous two steps threw an exception, set |credential| to failure - and return. - 1. Let |credential| be a new {{IdentityCredential}} given |globalObject|'s - realm. - 1. Set |credential|'s {{IdentityCredential/token}} to |token|. - 1. Set |credential|'s {{IdentityCredential/isAutoSelected}} to - |isAutoSelected|. + 1. If |json|[|token|] and |json|[|error|] both [=map/exist=] or both do not [=map/exist=], + set |credential| to a new {{IdentityCredentialError}} given |globalObject|'s + [=global object/realm=] and "IdentityCredentialError", and return. + 1. If |json|[|token|] [=map/exists=]: + 1. [=converted to an IDL value|Convert=] |json| to an {{IdentityProviderToken}}, |token|. + 1. If one of the previous steps threw an exception, set |credential| to a new + {{IdentityCredentialError}} given |globalObject|'s [=global object/realm=] + and "IdentityCredentialError", and return. + 1. Let |credential| be a new {{IdentityCredential}} given |globalObject|'s + [=global object/realm=]. + 1. Set |credential|'s {{IdentityCredential/token}} to |token|. + 1. Set |credential|'s {{IdentityCredential/isAutoSelected}} to |isAutoSelected|. + 1. Otherwise, i.e. if |json|[|error|] [=map/exists=]: + 1. If |json|[|error|] is not an [=ordered map=], set |credential| to a new + {{IdentityCredentialError}} given |globalObject|'s [=global object/realm=] and + "IdentityCredentialError", and return. + 1. [=converted to an IDL value|Convert=] |json| to an {{IdentityCredentialErrorInit}}, + |errorInit|. + 1. If |errorInit|.{{IdentityCredentialErrorInit/url}} is present: + 1. Let |errorUrl| be the result of [=computing the manifest URL=] given |provider|, + |errorInit|.{{IdentityCredentialErrorInit/url}}, false, and |globalObject|. + 1. If |errorUrl| is failure, set |errorInit|.{{IdentityCredentialErrorInit/url}} + to "". + 1. Let |credential| be a new {{IdentityCredentialError}} given |globalObject|'s + [=global object/realm=], "IdentityCredentialError", and |errorInit|. 1. Wait for |credential| to be set. 1. Return |credential|.
@@ -1303,7 +1388,7 @@ To fetch the client metadata given an {{IdentityProviderAPIC an {{IdentityProviderRequestOptions}} |provider|, run the following steps. This returns an {{IdentityProviderClientMetadata}} or failure. 1. Let |clientMetadataUrl| be the result of [=computing the manifest URL=] given |provider|, - |config|["{{IdentityProviderAPIConfig/client_metadata_endpoint}}"], and |globalObject|. + |config|["{{IdentityProviderAPIConfig/client_metadata_endpoint}}"], true, and |globalObject|. 1. If |clientMetadataUrl| is failure, return failure. 1. Let |request| be a new request as follows: @@ -1383,9 +1468,9 @@ To fetch request given a [=/request=] |request|, |globalObject|, and
-When computing the manifest URL given an {{IdentityProviderRequestOptions}} |provider|, a -[=string=] |manifestString|, and |globalObject|, perform the following steps. This returns a -URL or failure. +When computing the manifest URL given an {{IdentityProviderConfig}} |provider|, a +[=string=] |manifestString|, a boolean |requireSameOrigin|, and |globalObject|, perform the following +steps. This returns a URL or failure. 1. Let |configUrl| be the result of running [=parse url=] with |provider|'s {{IdentityProviderConfig/configURL}} and |globalObject|. 1. Let |manifestUrl| be the result of running [=parse url=] given |manifestString| (the relative @@ -1396,7 +1481,10 @@ When computing the manifest URL given an {{IdentityProviderRequestOpt allowed. 1. If |manifestUrl| is failure, return failure. - 1. If |manifestUrl| is not [=same origin=] with |configUrl|, return failure. + 1. If |requireSameOrigin| and |manifestUrl| is not [=same origin=] with |configUrl|, return + failure. + 1. If |requireSameOrigin| is false and |manifestUrl|'s [=url/host=]'s [=host/registrable domain=] + is not equal to |configUrl|'s, return failure. 1. If |manifestUrl| is not a [=potentially trustworthy URL=], return failure. 1. Return |manifestUrl|.