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|.