Skip to content

Commit

Permalink
Fix FedCM connection status computations (#526)
Browse files Browse the repository at this point in the history
* rebase

* rebase
  • Loading branch information
npm1 authored Aug 16, 2024
1 parent 90128b5 commit 5013145
Showing 1 changed file with 59 additions and 46 deletions.
105 changes: 59 additions & 46 deletions spec/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -472,24 +472,44 @@ cross-origin communication.
If a user clears browsing data for an |origin| (cookies, localStorage, etc.), the user agent MUST
[=list/remove=] all triples with an [=/origin=] matching the |origin| from <a>connected accounts set</a>.

<div algorithm="compute the connection status">
To <dfn>compute the connection status</dfn> given an {{IdentityProviderConfig}} |provider|, an
{{IdentityProviderAccount}} |account|, and a |globalObject|, run the following steps. This returns
<dfn for="compute the connection status">connected</dfn> or
<dfn for="compute the connection status">disconnected</dfn>.
1. If |account| [=map/contains=] {{IdentityProviderAccount/approved_clients}} and
|account|'s {{IdentityProviderAccount/approved_clients}} does not [=list/contain=]
|provider|'s {{IdentityProviderConfig/clientId}}, return
[=compute the connection status/disconnected=].
<div algorithm>
To <dfn>compute the connected account key</dfn> given an {{IdentityProviderConfig}} |provider|, an
{{IdentityProviderAccount}} |account|, and a |globalObject|, run the following steps. It returns a
triple of the form (rp, idp, account).
1. Let |configUrl| be the result of running [=parse url=] with |provider|'s
{{IdentityProviderConfig/configURL}} and |globalObject|.
1. Let |idpOrigin| be the [=url/origin=] corresponding to |configUrl|.
1. Let |rpOrigin| be |globalObject|'s [=associated Document=]'s [=Document/origin=].
1. Let |accountId| be |account|'s {{IdentityProviderAccount/id}}.
1. Let |triple| be (|rpOrigin|, |idpOrigin|, |accountId|).
1. If [=connected accounts set=] [=list/contains=] |triple| , return
1. Return (|rpOrigin|, |idpOrigin|, |accountId|).
</div>

<div algorithm>
When asked whether an {{IdentityProviderAccount}} |account| is
<dfn>eligible for auto reauthentication</dfn> given an {{IdentityProviderConfig}} |provider| and a
|globalObject|, run the following steps. This returns a boolean.
1. If |account| [=map/contains=] {{IdentityProviderAccount/approved_clients}} and
|account|'s {{IdentityProviderAccount/approved_clients}} does not [=list/contain=]
|provider|'s {{IdentityProviderConfig/clientId}}, return false.
1. Let |triple| be the result of running [=compute the connected account key=] given |provider|,
|account|, and |globalObject|.
1. Return whether [=connected accounts set=] [=list/contains=] |triple|.
</div>

<div algorithm="compute the connection status">
When asked to <dfn>compute the connection status</dfn> given an {{IdentityProviderAccount}}
|account|, an {{IdentityProviderConfig}} |provider| and a |globalObject|, run the following steps.
This returns <dfn for="compute the connection status">connected</dfn> or
<dfn for="compute the connection status">disconnected</dfn>.
1. If |account| [=map/contains=] {{IdentityProviderAccount/approved_clients}}:
1. If |account|'s {{IdentityProviderAccount/approved_clients}} [=list/contains=]|provider|'s
{{IdentityProviderConfig/clientId}}, return [=compute the connection status/connected=].
1. Return [=compute the connection status/disconnected=].
1. Let |triple| be the result of running [=compute the connected account key=] given |provider|,
|account|, and |globalObject|.
1. If [=connected accounts set=] [=list/contains=] |triple|, return
[=compute the connection status/connected=].
1. Otherwise, return [=compute the connection status/disconnected=].
1. Return [=compute the connection status/disconnected=].
</div>

<div algorithm>
Expand Down Expand Up @@ -598,10 +618,10 @@ When asked to <dfn>attempt to disconnect</dfn> given an {{IdentityCredentialDisc
"{{NetworkError}}" {{DOMException}}.
1. If the user has disabled the FedCM API on the |globalObject|, [=reject=] |promise| with a
"{{NetworkError}}" {{DOMException}}.
1. If there does not exist an account |account| such that [=compute the connection status=] of
|options|, |account|, and |globalObject| returns
[=compute the connection status/connected=], then [=reject=] |promise| with a
"{{NetworkError}}" {{DOMException}}. This check can be performed by iterating over the
1. If there does not exist an account |account| such that [=connected accounts set=]
[=list/contains=] the result of [=compute the connected account key=] given |account|,
|options|, and |globalObject|, then [=reject=] |promise| with a "{{NetworkError}}"
{{DOMException}}. This check can be performed by iterating over the
[=connected accounts set=] or by keeping a separate data structure to make this lookup fast.
1. Let |config| be the result of running [=fetch the config file=] with
|provider| and |globalObject|.
Expand Down Expand Up @@ -965,41 +985,35 @@ the exception thrown.
1. Let |registeredAccount|, |numRegisteredAccounts| be null and 0, respectively.
1. Let |account| be null.
1. For each |acc| in |accountsList|:
1. Let |accState| be the result of running the [=compute the connection status=] algorithm given
|provider| and |acc|.
1. If |accState| is [=compute the connection status/connected=], set |registeredAccount| to
|acc| and increase |numRegisteredAccounts| by 1.
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 |accountState| to the result of running
[=compute the connection status=] algorithm given |provider| and |account|. When doing this,
the user agent MAY show some UI to the user indicating that they are being
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
<dfn>auto-reauthenticated</dfn>.
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. Set |accountState| to the result of running the [=compute the connection status=] algorithm
given |provider|, |account|, and |globalObject|.
1. If |accountState| is [=compute the connection status/disconnected=],
let |permission| be the result of running [=request permission to sign-up=] algorithm
with |account|, |accountState|, |config|, |provider|, and |globalObject|. Also set
|disclosureTextShown| to true.
1. Otherwise, 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
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. Set |accountState| to the result of running the [=compute the connection status=] algorithm
given |provider| and |account|.
1. If |accountState| is [=compute the connection status/disconnected=]:
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. Otherwise, set |permission| 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.
Expand Down Expand Up @@ -1627,9 +1641,9 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP
1. Let |document| be |globalObject|'s [=associated Document=].
1. If |document| is not [=allowed to use=] the [=identity-credentials-get=]
[=policy-controlled feature=], throw a "{{NotAllowedError}}" {{DOMException}}.
1. If there does not exist an account |account| such that [=compute the connection status=] of
|provider|, |account|, and |globalObject| returns
[=compute the connection status/connected=], then throw a new "{{NetworkError}}"
1. If there does not exist an account |account| such that [=connected accounts set=]
[=list/contains=] the result of [=compute the connected account key=] given |account|,
|provider|, and |globalObject|, then [=reject=] |promise| with a "{{NetworkError}}"
{{DOMException}}. This check can be performed by iterating over the
[=connected accounts set=] or by keeping a separate data structure to make this lookup fast.
1. Let |configUrl| be the result of running [=parse url=] with |provider|'s
Expand All @@ -1651,7 +1665,7 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP
{{DOMException}}.
1. Let |accountsList| be the result of [=fetch the accounts=] with |config|, |provider|,
and |globalObject|.
1. Let |hasReturningAccount| be false.
1. Let |hasAccountEligibleForAutoReauthentication| be false.
1. For each |account| in |accountsList|:
1. If |account|["{{IdentityProviderAccount/approved_clients}}"] is not empty and it does not
[=list/contain=] |provider|'s {{IdentityProviderConfig/clientId}}, continue.
Expand All @@ -1660,11 +1674,10 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP
This could be useful for instance in cases where the user has disconnected the
account out of band.

1. [=Compute the connection status=] of |provider|, |account|, and |globalObject|. If the
result is [=compute the connection status/connected=], set |hasReturningAccount| to
true.
1. If |hasReturningAccount| is false, [=reject=] |promise| with a new "{{NetworkError}}"
{{DOMException}}.
1. If |account| is [=eligible for auto reauthentication=] given |provider| and
|globalObject|, set |hasAccountEligibleForAutoReauthentication| to true.
1. If |hasAccountEligibleForAutoReauthentication| is false, [=reject=] |promise| with a new
"{{NetworkError}}" {{DOMException}}.
1. Let |userInfoList| be a new [=list=].
1. For each |account| in |accountsList|:
1. [=list/Append=] an {{IdentityUserInfo}} to |userInfoList| with the following values:
Expand Down Expand Up @@ -2350,8 +2363,8 @@ The [=remote end steps=] are:
if present
1. `idpConfigUrl` set to the {{IdentityProviderConfig/configURL}} of the
IDP this account belongs to
1. `loginState` to `"SignUp"` if |accountState| is [=compute the connection status/disconnected=]
and `"SignIn"` otherwise
1. `loginState` to `"SignUp"` if |accountState| is
[=compute the connection status/disconnected=] and `"SignIn"` otherwise
1. `termsOfServiceUrl` to the {{IdentityProviderClientMetadata/terms_of_service_url}}
if one was provided and the `loginState` is `"SignUp"`, otherwise {{undefined}}
1. `privacyPolicyUrl` to the {{IdentityProviderClientMetadata/privacy_policy_url}}
Expand Down

0 comments on commit 5013145

Please sign in to comment.