diff --git a/.gitignore b/.gitignore index c7c003473..f576bccd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ spec/index.html spec/fedcm.idl +spec/login-status.html +spec/login-status.idl .DS_Store diff --git a/.pr-preview.json b/.pr-preview.json index bd783695b..4f5b89b33 100644 --- a/.pr-preview.json +++ b/.pr-preview.json @@ -4,4 +4,5 @@ "params": { "force": 1 } + } diff --git a/README.md b/README.md index 6487edade..9855501cf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is the repository for the W3C's FedID CG Federated Credentials Management A Explainer: [explainer.md](explainer.md) -Work-in-progress specification: +Work-in-progress specification: ## Introduction @@ -21,7 +21,7 @@ the removal of third-party cookies on federated login. Historically this has relied on third-party cookies or navigational redirects in order to function as they were the primitives provided by the web. -The [explainer](explainer.md) and [spec](https://fedidcg.github.io/FedCM) +The [explainer](explainer.md) and [spec](https://w3c-fedid.github.io/FedCM) provide a potential API and the rational behind how that API was designed. ## Contributing @@ -44,9 +44,9 @@ There are several ways to contribute to the Federated Credential Management API. * If you're an Identity Provider, there are two sides of the implementation that will be needed and any feedback on either side is appreciated. - 1. The [Identity Provider API](https://fedidcg.github.io/FedCM/#idp-api) describes + 1. The [Identity Provider API](https://w3c-fedid.github.io/FedCM/#idp-api) describes the manifest and API needed server side. - 2. The [Browser API](https://fedidcg.github.io/FedCM/#browser-api) describes the JavaScript + 2. The [Browser API](https://w3c-fedid.github.io/FedCM/#browser-api) describes the JavaScript interface to FedCM which will need to be utilized. * If you're a Relying Party (i.e. website) and would like to test the changes out @@ -55,7 +55,7 @@ There are several ways to contribute to the Federated Credential Management API. JavaScript. (Until an IDP provides first party JavaScript to work with FedCM this integration will be tricker). You can also review the demo provided by the HOWTO and take a look at the - [Relying Party API](https://fedidcg.github.io/FedCM/#rp-api) to see what is needed + [Relying Party API](https://w3c-fedid.github.io/FedCM/#rp) to see what is needed on the RP side. ## Code of Conduct diff --git a/explainer.md b/explainer.md deleted file mode 100644 index ea2ca7482..000000000 --- a/explainer.md +++ /dev/null @@ -1,678 +0,0 @@ -# Federated Credential Management (FedCM) -**Last Update:** Mar 08, 2022 - -## Introduction - -Over the last decade, identity federation has played a central role in raising -the bar for authentication on the web, in terms of ease-of-use (e.g. -password-less single sign-in), security (e.g. improved resistance to phishing -and credential stuffing attacks) and trustworthiness compared to per-site -usernames and passwords. In identity federation, a **RP (relying party)** relies -on an **IDP (identity provider)** to provide the user an account without -requiring a new username and password. - -Unfortunately, the mechanisms that identity federation was designed on (iframes, -redirects and cookies) are being abused to track users across the web. A user -agent isn’t able to differentiate between identity federation and tracking, the -mitigations for the various types of abuse make identity federation more -difficult. - -The Federated Credential Management API provides a use case specific abstraction -for federated identity flows on the web. The identity specific APIs allow the -browser to understand the context in which the RP and IDP exchange information, -inform the user as to the information and privilege levels being shared and -prevent unintended abuse. - -Specifically, the user agent: - -* gathers user consent to use an IDP to login to an RP -* keeps state if a relationship between the RP/IDP has been established -* provides APIs and capabilities to the RP/IDP based on the consent provided - -``` -┌─────────────────────────────────┐ ┌─────────────────────────────────┐ -| ┌─────────────────────────────┐ | | ┌─────────────────────────────┐ | -| | https://rp.example | | | | https://rp.example | | -| └─────────────────────────────┘ | | └─────────────────────────────┘ | -| ┌─────────────────────────────┐ | | ┌─────────────────────────────┐ | -| | | | | | | | -| | Welcome to my website! | | | | Welcome to my website! | | -| | | | | | | | -| | | | | | | | -| ┌─────────────────────────────┐ | | ┌─────────────────────────────┐ | -| | Choose an account | | | | Sign-in to rp.example | | -| | to sign-in to rp.example | | | | with idp.example? | | -| | | | | | | | -| | ┌────┐ | | | | ┌────┐ | | -| | | :) | John Doe | | ──► | | | :) | John Doe | | -| | └────┘ john@email.com | | | | └────┘ john@email.com | | -| | ┌────┐ | | | | | | -| | | :] | John Doe | | | | ┌─────────────────────────┐ | | -| | └────┘ john.doe@work.com | | | | | Continue as John | | | -| | | | | | └─────────────────────────┘ | | -| └─────────────────────────────┘ | | └─────────────────────────────┘ | -└─────────────────────────────────┘ └─────────────────────────────────┘ -``` - -### What Will Break - -With third party cookies blocked, here are the list of things known to degrade: - -* OIDC’s - [Front-channel logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html). Front-channel logout requires the IDP to embed several RP iframes which rely - on the RPs cookies. -* Social Widgets. In order to provide social widgets the IDPs third-party cookie - must be provided from the RPs top level origin. -* Personalized buttons. The display of personalized login information on a button - in the RP origin is implemented as an IDP iframe that requires third party - cookies. -* Session Refresh without top-level navigation or popups. - - -### Goals - -* Enable all federated identity flows (including - [what will break](#what-will-break)) without the use of third-party cookies in - a way that makes the web meaningfully more private and usable compared to - the [next best alternative](#related-work) -* Maximize backwards compatibility, especially for RPs -* Allow identity protocols to be extended independent of browser changes -* Reuse as much from OIDC / SAML / OAuth as possible - -### Out-of-Scope - -Features which are out-of-scope for the **current** version of FedCM, but are -**not** conflicting with our goals: - -* Sign into the IDP -* Support future interventions/mitigations beyond third-party cookie - deprecation: FedCM is not a stop-gap solution built entirely for third-party - cookie deprecation, and is likely to be a stepping stone towards a future - that includes continued privacy-preserving efforts (e.g. against navigational - tracking). Knowing that that is on the horizon, FedCM addresses a smaller - problem now, and builds a small piece of the foundation (i.e. a browser - mediated federated account chooser), rather than dealing with all problems - at once. - -### Non-goals - -* Address problems that are better addressed by [related](#related-work) - technologies or controls (e.g. [user settings](#settings), - [admin policies](#enterprise-policies)) -* Zero RP/IDP change deployment: redeployments should be minimized but a - non-zero number of IDP/RP changes may be required -* Replace OIDC / SAML / OAuth: these efforts should continue to thrive by - offering a better identity-specific foundation - -## Use Cases - -The below use case scenarios illustrate some basic supported flows. Each -supported flow below occurs inside an iframe or in an XHR request. - - -### Sign-up - -A Sign-up occurs when the user is registering a new account at the -Relying Party using their Identity Provider. - -For instance, a user navigates to a Relying Party in their browser -and creates an account. The Relying Party displays supported -Identity Providers to the user who selects their favorite. The user -is prompted "Do you want to sign up on the Relying Party with the Identity Provider?". -Upon user agreement an account is created with the Identity Provider and -the user has a session initialized on Relying Party. - -### Sign-in - -After a user navigates to a Relying Party in a browser and creates an -account by going through their sign up flow, the user can -sign into their account once their session expires. - -A sign-in occurs when the Identity Provider believes it is -necessary to gather an explicit permission from the user to sign into a -Relying Party, typically after the user goes through a -sign out flow. - -For example, after the user has done the sign out flow of the -Relying Party they decide to log in again. The user visits the -Relying Party and selects their Identity Provider to sign-in. The -Identity Provider knows: - * the user already has an account with the Relying Party. - * the user has logged out of the Relying Party. - -The user is then prompted, "Do you want to sign in on the Relying Party with the Identity Provider?" -and upon user agreement the Relying Party creates a new session with -the users existing account. - -## Example - -The following is an example of a website allowing a user to login with -`idp.example`. - -```js -let nonce; -async function login() { - // Assume we have a method returning a random number. Store the value in a variable which can - // later be used to check against the value in the token returned. - nonce = random(); - // Prompt the user to select an account from the IDP to use for - // federated login within the RP. If resolved successfully, the Promise - // returns an IdentityCredential object from which the `token` can be - // extracted. - return await navigator.credentials.get({ - mediation: "optional", // "optional" is the default - identity: { - providers: [{ - configURL: "https://idp.example/fedcm.json", - clientId: "123", - nonce: nonce - } - }); -} -``` - -## Key scenarios - - * [RP initiated login](#rp-initiated-login) - * [Personalized login buttons](#personalized-login-buttons) - * [IDP initiated RP login](#idp-initiated-rp-login) - * [Token creation](#token-creation) - * [Token refresh](#token-refresh) - * [Cross-Origin iframe Support](#cross-origin-iframe-support) - * [IDP initiated front-channel logout](#idp-initiated-front-channel-logout) - - -## Detailed Design - -There are two sides to the FedCM API. Changes for the RP and changes for the -IDP. - -``` -┌───────────┐ ┌───────────┐ ┌───────────┐ -│ │ │ │ │ │ -│ Relying │ │ User │ │ Identity │ -│ Party │ │ Agent │ │ Provider │ -│ │ │ │ │ │ -│ │ │ │ │ │ -│ │ ┌──────────► *-+ Credential│ ┌────────────► *-+ manifest │ -│ │ │ │ Manager │ │ │ │ -│ │ ├ │ API │ ├────────────► *-+ accounts │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ ├────────────► *-+ client │ -│ │ │ │ │ │ │ metadata │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ ├────────────► *-+ token │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ ┌───────┐ │ │ │ ┌───────┐ │ │ │ ┌───────┐ │ -│ │ JS ├─┼───┘ ┌────────┼─┤ HTTP ├─┼─────┘ ┌────────┼─┤ JS │ │ -│ └───────┘ │ │ │ └───────┘ │ │ │ └───────┘ │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ │ │ │ │ │ │ │ -│ logout +-* ◄────┘ │ logoutRPs +-* ◄────────┘ │ │ -│ │ │ │ │ │ -│ │ │ │ │ │ -└───────────┘ └───────────┘ └───────────┘ -``` - -The RP is mostly done in JavaScript except if they desire front-channel logout -capabilities which requires an HTTP endpoint to be implemented. - - -### JS API - -#### RP Initiated Login - -The RP initiated `login` API allows an RP to log the user into using their -federated credentials. The result of the `login` call allows the RP to know if -the user has consented to the authentication. The returned tokens can then be -used as needed. - -```js -let nonce = random(); -// If successful, returns a Promise containing an IdentityCredential |cred| object. -// The token for logging in is in cred.token. -const cred = await navigator.credentials.get({ - mediation: "optional", // "optional" is the default - federated: { - providers: [{ - configURL: "https://idp.example/fedcm.json", - clientId: "123", - nonce: nonce - }] - } -}); -if (cred) { - const token = cred.token; -} -``` - -The `login` method will call into the IDP's -[Manifest Endpoint](#fedcm.json), -[Accounts Endpoint](#accounts_endpoint), -[Client Metadata Endpoint](#client_metadata_endpoint) and -[Token Endpoint](#token_endpoint). - -#### Personalized Login Buttons - -Personalized login buttons allow an RP and IDP (for which the user has an -active consented credential), to display user information in the login button -itself. Displaying the user's name, email or profile photo in the login button -makes it clearer to the user which federated identity provider they used in the -past and simplifies the decision of which provider to select on subsequent -logins. - -> The implementation of this has not been worked out at this point, it could -either be done by calling the `login` API to create a new ID token which would -then need to be revoked, or a separate API which could be called on the IDP to -return the personalization information. - -#### IDP Initiated RP Login - -Provides the IDP the ability to create the credential objects for an RP. In the -case where a top-level redirect to the IDP sign in pages has occurred this -allows the IDP to control the timing of the prompt for user consent and to -provide context for the prompt. - -> The implementation of this has not been worked out at this point, and we are -still investigating the shape of the API, below is a sketch. - - -```js -const cred = await navigator.credentials.get({ - federated: { - providers: [{ - relyingPartyOrigin: "https://rp.example" - }] - } -}); -await cred.authorize({ - name: "dan", - email "dan@example" - account_id: 123, - tokens: "access", - scopes: "calendar.read profile" -}); -``` - -#### Token Creation - -There are multiple types of token which can be requested from the IDP. - -* ID token: Provides information on the users identity -* Access token: Provides access to some resources -* Refresh token: Provides ability to retrieve a new access token - -```js -const cred = await navigator.credentials.get({ - mediation: "optional", // "optional" is the default - federated: { - providers: [{ - url: "https://idp.example", - clientId: "123" - }] - } -}); -let nonce = random(); -const tokens = await cred.login({ - nonce: nonce, - tokens: ["access", "refresh", "id"], - scopes: ["calendar.read", "profile"] -}); -``` - -The `login` method will call into the IDP's -[Manifest Endpoint](#fedcm.json), -[Accounts Endpoint](#accounts_endpoint), -[Client Metadata Endpoint](#client_metadata_endpoint) and -[Token Endpoint](#token_endpoint). - -#### Token Refresh - -Access tokens can be short lived. The `refresh` call provides the ability for -the RP to request new tokens, optionally providing the `refresh_token` in the -request. - -```js -tokens = await cred.refresh({ - tokens: ["access", "refresh"], - scopes: ["calendar.read", "profile"], - refresh_token: tokens.refreshToken -}); -``` - -The IDP calls for `refresh` have not been flushed out yet. - -#### Cross-Origin iframe Support - -FedCM adds the `fedcm` [policy-controlled feature](https://w3c.github.io/webappsec-permissions-policy/#policy-controlled-feature) -with a [default allowlist](https://w3c.github.io/webappsec-permissions-policy/#default-allowlist) -of `'self'`, which satisfies [`sameOriginAsAncestor`](https://www.w3.org/TR/credential-management-1/#same-origin-with-its-ancestors) -in the Credential Management Level 1 specification. - -This Permissions Policy feature allows the RP to specify that FedCM may be called -inside the iframe by adding an `allow=fedcm` attribute. This allows IDP embedded -iframe content to continue to function when updated to use FedCM calls. - -Optional cross-origin iframe support is needed because there are cases, such as -learning management systems, where third-party content is embedded inside the LMS. -That third-party content may embed other content as well, the embedded iframes -need to be able to retrieve access tokens for the user in order to request resources. -In this case, the `sameOriginAsAncestor` setting would not help as the embeddings are -specifically cross site. See [fedidcg/use-case-library issue #13](https://github.com/fedidcg/use-case-library/issues/13) -for more context. - -#### IDP Initiated Front-Channel logout - -Some IDPs provide the ability to log the user out of supporting RPs when the -user logs out of the IDP. The FedCM API provides this capability through the -`logoutRPs` call. This will allow the IDP to logout any RP for which the user -has previously approved the RP, IDP communication. - -The front-channel logout URL is exchanged with the IDP through a back-channel -and is outside the scope of the FedCM API. - -```js -FederatedCredential.logoutRPs([{ - url: "https://rp1.example/logout", - accountId: "123", -}, { - url: "https://rp2.example/logout", - accountId: "456", -}]); -``` - -The `logoutRPs` method will call into the RP `logout` endpoints. - -### HTTP API - -The IDP is required to expose a number of endpoints which are used to gather -the required information needed for authentication along with token generation -and revocation. - -The URLs are provided through a `fedcm.json` file which is located at the path -provided by the RP JS call to `navigator.credentials.get` provided in the `url` -for the `federated` `provider`. - -#### Endpoint Properties - -| Endpoint | Cookies | Client Id | Referrer | -|:-------------------------|:------------:|:---------:|:--------:| -| fedcm.json | no | no | no | -| accounts_endpoint | yes | no | no | -| token_endpoint | yes | yes | yes | -| client_metadata_endpoint | no | yes | yes | -| revocation_endpoint | yes | yes | yes | - -* Client Id is an identifier provided to the RP by the IDP during signup -* All requests sent from the browser include a `Sec-FedCM-CSRF` header to - prevent CSRF attacks - -#### `fedcm.json` - -The `fedcm.json` request is used to provide the UA with information needed to -interact with the IDP during federation. - -For example: - -```http -GET /fedcm.json HTTP/1.1 -Host: idp.example -Accept: application/json -Sec-FedCM-CSRF: ?1 -``` - -```json -{ - "accounts_endpoint": "/accounts", - "client_metadata_endpoint": "/metadata", - "token_endpoint": "/tokens", - "revocation_endpoint": "/revocation", - "branding": { - "background_color": "green", - "color": "0xFFEEAA", - "icons": [{ - "url": "https://idp.example/icon.ico", - "size": 10 - }] - } -} -``` - -#### `accounts_endpoint` - -The `accounts_endpoint` request is used to provide account information to be -shown in the browser mediation dialogs. - -For example: - -```http -GET /accounts HTTP/1.1 -Host: idp.example -Accept: application/json -Cookie: 0x23223 -Sec-FedCM-CSRF: ?1 -``` - -```json -{ - "accounts": [{ - "id": "1234", - "given_name": "John", - "name": "John Doe", - "email": "john_doe@idp.example", - "picture": "https://idp.example/profile/123", - "approved_clients": ["123", "456", "789"] - }, { - "id": "5678", - "given_name": "Johnny", - "name": "Johnny", - "email": "johnny@idp.example", - "picture": "https://idp.example/profile/456", - "approved_clients": ["abc", "def", "ghi"] - }] -} -``` - -The information returned from the `accounts_endpoint` is not exposed to the RP, -but instead used by the browser to render the mediated account chooser dialog. - -#### `client_metadata_endpoint` - -The `client_metadata_endpoint` request allows the IDP to return RP specific -information needed in the mediated dialogs. - -For example: - -```http -GET /metadata?client_id=1234 HTTP/1.1 -Host: idp.example -Referer: https://rp.example/ -Accept: application/json -Sec-FedCM-CSRF: ?1 -``` - -```json -{ - "privacy_policy_url": "https://rp.example/clientmetadata/privacy_policy.html", - "terms_of_service_url": "https://rp.example/clientmetadata/terms_of_service.html" -} -``` - -#### `token_endpoint` - -The `token_endpoint` returns tokens for the user from the IDP. - -For example: - -```http -POST /tokens HTTP/1.1 -Host: idp.example -Referer: https://rp.example/ -Content-Type: application/x-www-form-urlencoded -Cookie: 0x23223 -Sec-FedCM-CSRF: ?1 - -account_id=123&client_id=client1234&nonce=Ct60bD&consent_acquired=true -``` - -```json -{ - "id_token": "eyJhbGciOiJIUzI.eyJzdWIiOiIx.SflKxwRJSMeKKF2Q" -} -``` - -#### `revocation_endpoint` - -The `revocation_endpoint` is used to inform the IDP to destroy all tokens -provided to the user. - -For example: - -```http -POST /revocation HTTP/1.1 -Host: idp.example -Referer: https://rp.example/ -Content-Type: application/x-www-form-urlencoded -Cookie: 0x23223 -Sec-FedCM-CSRF: ?1 - -account_id=123&client_id=client1234 -``` - - -## Related Work - -This is a set of related work that we expect to be used in conjunction with this -proposal. - -### First Party Sets - -FedCM gathers the users consent to avoid [unwanted cross-contexts recognition](https://w3ctag.github.io/privacy-principles/#hl-recognition-cross-context) and deliberately leaves to each user agent the -delineation of [partitions](https://w3ctag.github.io/privacy-principles/#dfn-partition) -and the [privacy boundary](https://github.com/w3ctag/privacy-principles/issues/1) they want to set for their users. - -We expect this proposal to work well either in conjunction with, in the absence -of or in coordination with [First Party Sets](https://github.com/privacycg/first-party-sets). - -By that we mean that FedCM gathers the user's consent: - -* at every [cross-party](https://github.com/privacycg/first-party-sets#sameparty-cookies-and-first-party-sets) data exchange, for browsers that adopt First Party Sets as a widening of the [machine enforceable contexts](https://w3ctag.github.io/privacy-principles/#dfn-machine-enforceable-context) or -* at every cross-site data exchange, for browsers that don't or -* at every cross-site data exchange but with wording that takes into account first party sets ([example](https://github.com/privacycg/first-party-sets/issues/28)) - -FedCM is being designed to work under different privacy boundaries chosen -by different browsers. While First Party Sets complements FedCM they are not -required. - -### Enterprise Policies - -Enterprise Policies are policies that administrators set for devices managed and -supplied by their enterprise. While expected to cover a large set of devices, -the community has stated there is a substantial number of employees that bring -their own devices that need federation to work (namely, front channel logout) in -the absence of third party cookies. - -### Settings - -Browser Settings are controls that users have to change how their browser works. -While constructive and useful, they are hard to discover proactively (compared -to Web Platform APIs that can be invoked by websites). - -## Alternatives considered - -### Status Quo - -A trivial alternative that is worth noting as a baseline is to "do nothing" and -keep federation using low-level primitives like iframes and third party cookies. - -That seemed clear to reject based on the already deployed, planned and -increasing constraints that are being put in place for cross-site communication -through third-party cookies. - -> Publicly announced browser positions on third-party cookies: -> -> 1. [Safari](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/): - third-party cookies are **already** blocked by **default** -> 1. [Firefox](https://blog.mozilla.org/blog/2019/09/03/todays-firefox-blocks-third-party-tracking-cookies-and-cryptomining-by-default/): - third-party cookies are **already** blocked **by a blocklist** -> 1. [Chrome](https://blog.google/products/chrome/privacy-sustainability-and-the-importance-of-and/): - on iOS **already** blocked **by default** and intends to offer **alternatives** - to make them **obsolete** in the - [near term](https://www.blog.google/products/chrome/building-a-more-private-web/) - on other platforms. - -The only alternative to federation is the proliferation of usernames/passwords -(or native applications), which we believe isn't healthy for the web. - -### Heuristics - -Browser vendors have applied a series of -[temporary heuristics](https://developer.mozilla.org/en-US/docs/Web/Privacy/Storage_Access_Policy#automatic_storage_access_upon_interaction) -to mitigate tracking while maintaining backwards compatibility. As we learn from -their experience, there are a few challenges: - -* they are easy to circumvent and abuse -* they are hard to cover all cases and maintain -* the use of allowlists and denylists is hard to scale - -### The Storage Access API - -The -[Document.requestStorageAccess()](https://developer.mozilla.org/en-US/docs/Web/API/Document/requestStorageAccess) -API grants first-party storage to cross-origin subframes. In conjunction with -iframes, an IDP could expose its service via cross-site postMessage -communication once first-party storage has been granted. - -The general-purpose nature of the API leads to a couple of downsides: - -* A lowest-common-denominator policy (e.g. a permission prompt that warns users - of the worst case scenario) which we expect to lead to permission blindness - to users short and long term. -* Its inability to solve identity-specific problems, like front-channel logout -* General / non-specific consent text for the end-user that is difficult to - understand / difficult to give consent to - -### The [Login Status API](https://github.com/privacycg/is-logged-in) - -The Login Status API allows a website to inform the browser of the users login -status: - -```js -await navigator.recordFederatedLogin("https://idp.example", "username"); -``` - -As it suggests, its weakest point is -[defending against abuse](https://github.com/privacycg/is-logged-in#defending-against-abuse) -, and a lot of the most promising mitigations is to show browser mediated UX. - -It explicitly notes an -[integration with FedCM](https://github.com/privacycg/is-logged-in#federated-logins) -but it is unclear at the moment why the "logged in" bit could not be -inferred from invoking `credential.login()` and requiring the website to -call `navigator.recordFederatedLogin()`. - -Further investigation on the relationship of FedCM and that proposal is taking -place in -[privacycg/is-logged-in issue#44](https://github.com/privacycg/is-logged-in/issues/44). - - -## Prior Art - -By descending order of proximity: - -* [Mozilla Persona](https://github.com/mozilla/persona) -* [Web Login](https://github.com/junosuarez/web-login) -* [OpenYOLO](https://github.com/openid/OpenYOLO-Web) -* [Basic Auth](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) -* Web Login -* [Web Sign-in](https://microformats.org/wiki/web-sign-in) -* [WebID](https://dvcs.w3.org/hg/WebID/raw-file/tip/spec/identity-respec.html) - -## References & Acknowledgements - -The FedID CG and the Privacy CG have provided invaluable feedback on the design -of the API. diff --git a/explorations/HOWTO-chrome.md b/explorations/HOWTO-chrome.md index 1ee0a9efc..64396c168 100644 --- a/explorations/HOWTO-chrome.md +++ b/explorations/HOWTO-chrome.md @@ -333,3 +333,169 @@ IdP fails by either returning some network error or saying that the disconnectio was unsuccessful, or if the `account_id` is nowhere to be found, the browser will remove from local storage all of the federated accounts associated with the (RP, IDP). + +### Button Flow +The button flow differs from the widget flow in several ways. The most significant +difference is that the button flow requires a user gesture such as clicking on a +sign-in button. This means that a user must be able to successfully sign in with a +federated account using this flow. In contrast, the widget flow is an optimized +flow that can reduce sign-in friction. This means that if the widget flow is +unavailable, a user can still click a "Sign in with IdP" button to continue. See +illustrative [mocks here](https://docs.google.com/presentation/d/1iURrPakaHgBfQ6mAefKijjxToiTTgBSPz1rtaV0od98/edit?usp=sharing). + +#### Button Mode API +To use the Button Mode API: +* Enable the experimental feature `FedCmButtonMode` in `chrome://flags`. +* Make sure to invoke the API behind [transient user activation](https://html.spec.whatwg.org/multipage/interaction.html#transient-activation). +* Invoke the API with the `mode` parameter like so: + ```js + return await navigator.credentials.get({ + identity: { + providers: [{ + configURL: "https://idp.example/config.json", + clientId: "123", + nonce: nonce + }], + mode: "button" + } + }); + ``` + +The browser will send a new parameter to the IdP representing the request type by including +`mode=button` in the request sent to the ID assersion endpoint: +``` +POST /fedcm_assertion_endpoint HTTP/1.1 +Host: idp.example +Origin: https://rp.example/ +Content-Type: application/x-www-form-urlencoded +Cookie: 0x23223 +Sec-Fetch-Dest: webidentity + +account_id=123&client_id=client1234&nonce=Ct60bD&disclosure_text_shown=true&is_auto_selected=false +&mode=button +``` +Note that we currently only include the new parameter in the button flow. Other flow +types such as "widget" (name TBD) will be added when Chrome ships this feature. + +#### Use Other Account API +This API allows users to use other accounts in the account chooser when, for example, IdPs +support multiple accounts or replacing the existing account. + +To use the Use Other Account API: +* Enable the experimental feature `FedCmUseOtherAccount` in `chrome://flags`. +* IdP specifies the following in the FedCM config file: +``` +{ + "accounts_endpoint" : ..., + "modes: { + "button": { + "supports_use_other_account": true|false, + } + } +} +``` + +### Continuation API +This API lets the IdP request that the authorization flow should continue +in a popup window that is controlled by the IdP. This can be used to request +additional permission, to ask a user to confirm their account details, or +for a variety of other use cases. + +To use this feature: +* Enable the experimental feature `FedCmAuthz` in chrome://flags +* Return a "continue_on" field with a URL instead of a token + from the ID assertion endpoint. For example: + ```js + { + "continue_on": "https://idp.example/finish_login?account_id=123" + } + ``` +* When the authorization flow finishes, call `IdentityProvider.resolve` to close the + popup and provide the token that will be passed to the RP: + ```js + IdentityProvider.resolve("this is the token"); + ``` +* If the account ID has changed (for example, if the popup provided a "Switch + User" function), you can specify it in a second parameter: + ```js + IdentityProvider.resolve("this is the token", {accountId: "123"}); + ``` +* If the user cancels the login flow, call `IdentityProvider.close` to close + the popup and reject the promise that was returned from `navigator.credentials.get`: + ```js + IdentityProvider.close(); + ``` + +### Parameters API +This feature lets RPs specify additional key/value pairs that will get sent +to the ID assertion endpoint. + +To use this feature: +* Enable the experimental feature `FedCmAuthz` in chrome://flags +* Add a `params` field to the `navigator.credentials.get` call: + ```js + navigator.credentials.get({ + identity: { + providers: [{ + configURL: "https://idp.example/config.json", + clientId: "123", + nonce: nonce, + params: { + "key": "value", + "anything_goes": "yes", + "really": "yes", + "scopes": "calendar.readonly", + "dpop": "something", + "moar": "sure", + } + }], + } + }); + ``` +* These key/value pairs will be sent as-is in the ID assertion request: + `account_id=123&key=value&anything_goes=yes&really=yes&scopes=calendar.readonly&dpop=something&moar=sure&...` + + +### Multiple configURLs +This feature lets you have multiple different config files under the same +eTLD+1 as long as they all have the same accounts_endpoint. This can be +useful to specify different branding or different ID assertion endpoints. + +To use this feature: +* Enable the experimental feature `FedCmAuthz` in chrome://flags +* Add the login_url and accounts_endpoint to the .well-known/web-identity + file: + ```js + { + "provider_urls": [ + // keep this unchanged + ], + "accounts_endpoint": "https://fedcm.idp.example/accounts", + "login_url": "https://fedcm.idp.example/login.html" + } + ``` + +### Account labels +The account labels API lets IdPs give a list of labels to an account and +lets different config files specify a filter for those labels. + +To use the API: +* Enable the experimental feature `FedCmAuthz` in chrome://flags +* Add a `labels` field to accounts in the account endpoint: + ```js + { + "name": "John Smith", + //... + "labels": ["label1"] + } + ``` +* Add the desired label to the config file: + ```js + { + "accounts_endpoint": "...", + // ... + "accounts": { + "include": "label1" + } + } + ``` diff --git a/explorations/README.md b/explorations/README.md index aff15873f..b34b9c1bc 100644 --- a/explorations/README.md +++ b/explorations/README.md @@ -13,7 +13,7 @@ active exploration into the problem space, potential end states, and possible solutions. These all serve the purpose of providing context, understanding and a shared idea of where we _could_ go. For a more specific document on what can be done right now, please see the -[Federated Credential Management API](https://fedidcg.github.io/FedCM) proposal. +[Federated Credential Management API](https://w3c-fedid.github.io/FedCM) proposal. This exploration has been broken into several sections. @@ -22,7 +22,7 @@ This exploration has been broken into several sections. * What are some [related problems](related_problems.md)? * What has been tried [previously](prior.md)? 1. Description of where we [should](proposal.md) be and accompanying - [FedCM API](https://fedidcg.github.io/FedCM) proposal. + [FedCM API](https://w3c-fedid.github.io/FedCM) proposal. * What [alternatives](alternatives_considered.md) have been explored? 1. Potential [roadmap](roadmap.md) to the [proposal](proposal.md). diff --git a/explorations/alternatives_considered.md b/explorations/alternatives_considered.md index 5bda8f1b0..4a932bd5d 100644 --- a/explorations/alternatives_considered.md +++ b/explorations/alternatives_considered.md @@ -1,6 +1,6 @@ # Alternatives Considered -Now that we have a deep understanding of (a) the [problem](README.md) and (b) the [motivations](https://fedidcg.github.io/FedCM/#privacy-threat-model) and [topology](activation.md) of the parties involved, lets look at some **why not**s. +Now that we have a deep understanding of (a) the [problem](README.md) and (b) the [motivations](https://w3c-fedid.github.io/FedCM/#privacy) and [topology](activation.md) of the parties involved, lets look at some **why not**s. ## The Status Quo diff --git a/explorations/cookies.md b/explorations/cookies.md index df93709fa..ad7254999 100644 --- a/explorations/cookies.md +++ b/explorations/cookies.md @@ -1,6 +1,6 @@ # Cookies -This is a **proposal** for a high level API to support identity federation under this [threat model](https://fedidcg.github.io/FedCM/#privacy-threat-model). +This is a **proposal** for a high level API to support identity federation under this [threat model](https://w3c-fedid.github.io/FedCM/#privacy). It is widely known that browsers are either **already** blocking third party cookies or are planning to. @@ -18,7 +18,7 @@ This is a proposal to preserve these operations in the absence of third party co Cross-site communication is used throughout the entire lifecycle of the user signing in to a RP with an IDP, beginning at signing-up a new user all the way through managing the sessions (e.g. signing in, signing out or renewing refresh tokens). -From a [privacy threat model](https://fedidcg.github.io/FedCM/#privacy-threat-model) perspective, the design of this proposal is anchored on the observation that the most critical moment is when the identities between the RP and the IDP are joined for the very first time, namely when the user creates a new account in the RP using the identifiers from the IDP or when a user signs-in to an RP with an IDP for the first time in the browser: once the identities are joined, any arbitrary/uncontrolled cross-side communication can happen (with or without the browser's permission, e.g. via backchannel or cookie-less requests). +From a [privacy threat model](https://w3c-fedid.github.io/FedCM/#privacy) perspective, the design of this proposal is anchored on the observation that the most critical moment is when the identities between the RP and the IDP are joined for the very first time, namely when the user creates a new account in the RP using the identifiers from the IDP or when a user signs-in to an RP with an IDP for the first time in the browser: once the identities are joined, any arbitrary/uncontrolled cross-side communication can happen (with or without the browser's permission, e.g. via backchannel or cookie-less requests). In this proposal, the browser: diff --git a/explorations/debug-network-requests-chrome.md b/explorations/debug-network-requests-chrome.md new file mode 100644 index 000000000..e64b0ad29 --- /dev/null +++ b/explorations/debug-network-requests-chrome.md @@ -0,0 +1,31 @@ +While the Chrome team is trying to integrate FedCM into devtools, we suggest developers to use the following tools to debug network requests. + +1. Open chrome://net-export from the URL bar. Select “Include raw bytes” and +click “Start Logging to Disk”. Select a location to save the logs when prompted. +

+ +

+2. Open the page that calls the FedCM API. e.g. https://webid-fcm.glitch.me +

+ +

+3. Go through the FedCM flow to debug all network requests +

+ +

+4. Stop the logging on chrome://net-export +

+ +

+5. Load the logs into https://netlog-viewer.appspot.com/ +

+ +

+6. Under `Events` from the left, filter the logs with `type:URL_REQUEST` +

+ +

+7. The network requests that were sent to different endpoints can be viewed +individually. They also include the response that the browser has received. If +raw bytes are selected from step 1, the detailed response such as name, token +string etc. will show up on the right. diff --git a/explorations/directed_identifiers.md b/explorations/directed_identifiers.md index 50e141de0..894c13555 100644 --- a/explorations/directed_identifiers.md +++ b/explorations/directed_identifiers.md @@ -21,7 +21,7 @@ # Directed identifiers This document explores the ideas of [directed identifiers](glossary.md#directed-identifier) and [verifiably directed identifiers](glossary.md#verifiably-directed-identifier) in FedCM. -Directed identifiers are included in the FedCM proposal as an attempt to mitigate [Relying Party tracking](README.md#the-rp-tracking-problem) of users by means of [identifier correlation](https://fedidcg.github.io/FedCM/#attack-scenarios-by-rp-cross-site-correlation). As traditional tracking mechanisms have become less accessible, a fallback method for following user activity across the web has been for web sites with account systems to correlate personal identifiers associated with each account. For example, all sites that require users to use email addresses as login identifiers can collude to uniquely identify a given user across all of those sites, and profile that user's full activity across them. +Directed identifiers are included in the FedCM proposal as an attempt to mitigate [Relying Party tracking](README.md#the-rp-tracking-problem) of users by means of [identifier correlation](https://w3c-fedid.github.io/FedCM#attack-scenarios-by-rp-cross-site-correlation). As traditional tracking mechanisms have become less accessible, a fallback method for following user activity across the web has been for web sites with account systems to correlate personal identifiers associated with each account. For example, all sites that require users to use email addresses as login identifiers can collude to uniquely identify a given user across all of those sites, and profile that user's full activity across them. Conceptually, a directed identifer is a limited-scope identifier that has a one-way mapping from a user identifier that is known to an Identity Provider. The original identifier cannot practically be derived from the directed identifier by anyone other than the IdP or possibly the user. diff --git a/explorations/glossary.md b/explorations/glossary.md index 4cd499564..1223baffb 100644 --- a/explorations/glossary.md +++ b/explorations/glossary.md @@ -86,7 +86,7 @@ References: [OIDC terminology](https://openid.net/specs/openid-connect-core-1_0. ### IDP tracking * _A privacy threat in which an [Identity Provider](#identity-provider-idp) is able to surveil or correlate user activity across the web._ -References: [FedCM Threat Model](https://fedidcg.github.io/FedCM/#attack-scenarios-by-idp) +References: [FedCM Threat Model](https://w3c-fedid.github.io/FedCM/#idp-intrusion) ### Relying Party (RP) @@ -106,7 +106,7 @@ References: [OIDC terminology](https://openid.net/specs/openid-connect-core-1_0. ### RP tracking * _A privacy threat in which a [Relying Party](#relying-party-rp) is able to surveil or correlate user activity across the web._ -References: [FedCM Threat Model](https://fedidcg.github.io/FedCM/#attack-scenarios-by-rp) +References: [FedCM Threat Model](https://w3c-fedid.github.io/FedCM/#rp-fingerprinting) ### Standard claims diff --git a/explorations/navigations.md b/explorations/navigations.md index 15bffcba9..2181354b4 100644 --- a/explorations/navigations.md +++ b/explorations/navigations.md @@ -1,10 +1,10 @@ # Navigations -This is an **early exploration** of the design alternatives to address [bounce tracking](README.md#stage-2-bounce-tracking) under [this threat model](https://fedidcg.github.io/FedCM/#privacy-threat-model). +This is an **early exploration** of the design alternatives to address [bounce tracking](README.md#stage-2-bounce-tracking) under [this threat model](https://w3cping.github.io/privacy-threat-model/). This section goes over the **what** and the **how**. It presuposes that you have read and started from: -- The **why**: the [problem statement](problem.md) and the [motivations](https://fedidcg.github.io/FedCM/#privacy-threat-model) and the [topology](activation.md) of the parties involved. +- The **why**: the [problem statement](problem.md) and the [motivations](https://w3cping.github.io/privacy-threat-model/) and the [topology](activation.md) of the parties involved. - The **why not**: the [alternatives considered](alternatives_considered.md) (e.g. the [prior art](prior.md), the [status quo](alternatives_considered.md#the-status-quo) and the [requestStorageAccess API](alternatives_considered.md#the-request-storage-access-api)). We'll then go over the [high-level overview](#high-level-design) and a breakdown into two smaller problems: @@ -39,7 +39,7 @@ We'll go over each of these separately next. The consumer API is the Web Platform privacy-oriented API that relying parties call to request information from a specific identity provider, to be used in replacement of the current redirect/popup affordances that are currently used. -From the perspective of [The Privacy Threat Model](https://fedidcg.github.io/FedCM/#privacy-threat-model), there are two notably distinct uses of federation: +From the perspective of [The Privacy Threat Model](https://w3cping.github.io/privacy-threat-model/), there are two notably distinct uses of federation: * [signing-in](glossary.md#federated-sign-in) and * [authorization](glossary.md#authorization) @@ -128,7 +128,7 @@ Now that we looked at the surface area introduced for relying parties, lets turn The purpose of the Provider API is to fulfill the invocation of [The Consumer API](#the-Consumer-api) by coordinating with the identity provider. -From the perspective of [The Privacy Threat Model](https://fedidcg.github.io/FedCM/#privacy-threat-model), the Provider API has a much wider set of choices and trade-offs: +From the perspective of [The Privacy Threat Model](https://w3cping.github.io/privacy-threat-model/), the Provider API has a much wider set of choices and trade-offs: 1. Because of the [classification problem](README.md#the-classification-problem), we want to prevent a tracker from abusing this API by impersonating an IDP to track users. 1. Because of the [RP tracking problem](README.md#the-rp-tracking-problem), we want to promote directed identifiers as much as we can. diff --git a/explorations/proposal.md b/explorations/proposal.md index abbf3629f..1f2bcf2f3 100644 --- a/explorations/proposal.md +++ b/explorations/proposal.md @@ -127,7 +127,7 @@ Notably, for cases where the IDP controls the deployment of the JavaScript runni ```javascript // This is just a possible starting point, largely TBD. // -// Note, this historical, see https://fedidcg.github.io/FedCM for the current API. +// Note, this historical, see https://w3c-fedid.github.io/FedCM for the current API. // let {idToken} = await navigator.credentials.get({ provider: 'https://accounts.example.com', @@ -151,7 +151,7 @@ In this formulation, the triggering of the API works similarly as before, but th ```javascript // This is just a possible starting point, largely TBD. // -// Note, this historical, see https://fedidcg.github.io/FedCM for the current API. +// Note, this historical, see https://w3c-fedid.github.io/FedCM for the current API. // let {idToken} = await navigator.credentials.get({ provider: 'https://accounts.example.com', diff --git a/explorations/static/delegation-api-flow.svg b/explorations/static/delegation-api-flow.svg index 7fdef6f19..3754507e5 100644 --- a/explorations/static/delegation-api-flow.svg +++ b/explorations/static/delegation-api-flow.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/delegation-api-recovery-legacy-flow.svg b/explorations/static/delegation-api-recovery-legacy-flow.svg index da81c69c5..f3f6135d5 100644 --- a/explorations/static/delegation-api-recovery-legacy-flow.svg +++ b/explorations/static/delegation-api-recovery-legacy-flow.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/explorations/static/delegation-api-recovery-signin-flow.svg b/explorations/static/delegation-api-recovery-signin-flow.svg index 698f8e1f9..212405eb9 100644 --- a/explorations/static/delegation-api-recovery-signin-flow.svg +++ b/explorations/static/delegation-api-recovery-signin-flow.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/explorations/static/delegation-api-signup-flow.svg b/explorations/static/delegation-api-signup-flow.svg index 157c8fb56..fb449177d 100644 --- a/explorations/static/delegation-api-signup-flow.svg +++ b/explorations/static/delegation-api-signup-flow.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/explorations/static/mock1.svg b/explorations/static/mock1.svg index 29f6e9020..e058fbe8e 100644 --- a/explorations/static/mock1.svg +++ b/explorations/static/mock1.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock10.svg b/explorations/static/mock10.svg index 88e23527f..70a3c08ae 100644 --- a/explorations/static/mock10.svg +++ b/explorations/static/mock10.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock11.svg b/explorations/static/mock11.svg index c6c73c881..d315f937e 100644 --- a/explorations/static/mock11.svg +++ b/explorations/static/mock11.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock12.svg b/explorations/static/mock12.svg index ed97954e2..a7b9b19af 100644 --- a/explorations/static/mock12.svg +++ b/explorations/static/mock12.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock14.svg b/explorations/static/mock14.svg index 9abc05e14..ca2d351b7 100644 --- a/explorations/static/mock14.svg +++ b/explorations/static/mock14.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock15.svg b/explorations/static/mock15.svg index e5da77895..956e2c769 100644 --- a/explorations/static/mock15.svg +++ b/explorations/static/mock15.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock16.svg b/explorations/static/mock16.svg index 8f7808b09..3f6054ef3 100644 --- a/explorations/static/mock16.svg +++ b/explorations/static/mock16.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock17.svg b/explorations/static/mock17.svg index a3b8b1c76..258b11668 100644 --- a/explorations/static/mock17.svg +++ b/explorations/static/mock17.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock18.svg b/explorations/static/mock18.svg index b82d8e00e..bc9284035 100644 --- a/explorations/static/mock18.svg +++ b/explorations/static/mock18.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock19.svg b/explorations/static/mock19.svg index ae6e3e476..5304d7d6e 100644 --- a/explorations/static/mock19.svg +++ b/explorations/static/mock19.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock2.svg b/explorations/static/mock2.svg index a129b6e8e..b579e2c1f 100644 --- a/explorations/static/mock2.svg +++ b/explorations/static/mock2.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock20.svg b/explorations/static/mock20.svg index 4e49f58d5..7c9e5e220 100644 --- a/explorations/static/mock20.svg +++ b/explorations/static/mock20.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock21.svg b/explorations/static/mock21.svg index d4992df86..c3522e30c 100644 --- a/explorations/static/mock21.svg +++ b/explorations/static/mock21.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock22.svg b/explorations/static/mock22.svg index 4c8bca329..4111c50fa 100644 --- a/explorations/static/mock22.svg +++ b/explorations/static/mock22.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock24.svg b/explorations/static/mock24.svg index ecb6e5fb7..ab6ffa28d 100644 --- a/explorations/static/mock24.svg +++ b/explorations/static/mock24.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock25.svg b/explorations/static/mock25.svg index d66333c89..13d419d1f 100644 --- a/explorations/static/mock25.svg +++ b/explorations/static/mock25.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock26.svg b/explorations/static/mock26.svg index 20f4c6860..8054d6633 100644 --- a/explorations/static/mock26.svg +++ b/explorations/static/mock26.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock27.svg b/explorations/static/mock27.svg index 56ba1decf..5678aca9a 100644 --- a/explorations/static/mock27.svg +++ b/explorations/static/mock27.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock28.svg b/explorations/static/mock28.svg index 7f1e0c406..6af4846b1 100644 --- a/explorations/static/mock28.svg +++ b/explorations/static/mock28.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock29.svg b/explorations/static/mock29.svg index e8e6eaff4..66b552fce 100644 --- a/explorations/static/mock29.svg +++ b/explorations/static/mock29.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock30.svg b/explorations/static/mock30.svg index 6bf40ed81..6b93fd267 100644 --- a/explorations/static/mock30.svg +++ b/explorations/static/mock30.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock32.svg b/explorations/static/mock32.svg index 26e19f1d0..a04c3ae0b 100644 --- a/explorations/static/mock32.svg +++ b/explorations/static/mock32.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock33.svg b/explorations/static/mock33.svg index 9d9797fa4..8f4395604 100644 --- a/explorations/static/mock33.svg +++ b/explorations/static/mock33.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock35.svg b/explorations/static/mock35.svg index 0eeddc180..855e5fad9 100644 --- a/explorations/static/mock35.svg +++ b/explorations/static/mock35.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock36.svg b/explorations/static/mock36.svg index 531d4dd81..42d91f17d 100644 --- a/explorations/static/mock36.svg +++ b/explorations/static/mock36.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock38.svg b/explorations/static/mock38.svg index 6745c9f7c..13059fcdb 100644 --- a/explorations/static/mock38.svg +++ b/explorations/static/mock38.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock4.svg b/explorations/static/mock4.svg index a9b60409b..7d21af682 100644 --- a/explorations/static/mock4.svg +++ b/explorations/static/mock4.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock41.svg b/explorations/static/mock41.svg index b89c92d92..e4cce10bf 100644 --- a/explorations/static/mock41.svg +++ b/explorations/static/mock41.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock44.svg b/explorations/static/mock44.svg index bc843a75f..6ab787f8b 100644 --- a/explorations/static/mock44.svg +++ b/explorations/static/mock44.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock6.svg b/explorations/static/mock6.svg index 919be8f35..b02c8cbcc 100644 --- a/explorations/static/mock6.svg +++ b/explorations/static/mock6.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock7.svg b/explorations/static/mock7.svg index 993455457..81798b994 100644 --- a/explorations/static/mock7.svg +++ b/explorations/static/mock7.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/mock9.svg b/explorations/static/mock9.svg index bd1f7a83b..2c224bcaa 100644 --- a/explorations/static/mock9.svg +++ b/explorations/static/mock9.svg @@ -1,6 +1,6 @@ - + diff --git a/explorations/static/net-export/fedcm-1.png b/explorations/static/net-export/fedcm-1.png new file mode 100644 index 000000000..d713d0c5d Binary files /dev/null and b/explorations/static/net-export/fedcm-1.png differ diff --git a/explorations/static/net-export/fedcm-2.png b/explorations/static/net-export/fedcm-2.png new file mode 100644 index 000000000..937fe3ad1 Binary files /dev/null and b/explorations/static/net-export/fedcm-2.png differ diff --git a/explorations/static/net-export/net-export-entry.png b/explorations/static/net-export/net-export-entry.png new file mode 100644 index 000000000..3fd687b72 Binary files /dev/null and b/explorations/static/net-export/net-export-entry.png differ diff --git a/explorations/static/net-export/net-export-stop.png b/explorations/static/net-export/net-export-stop.png new file mode 100644 index 000000000..9565abc3c Binary files /dev/null and b/explorations/static/net-export/net-export-stop.png differ diff --git a/explorations/static/net-export/netlog-viewer-entry.png b/explorations/static/net-export/netlog-viewer-entry.png new file mode 100644 index 000000000..023ad3b3a Binary files /dev/null and b/explorations/static/net-export/netlog-viewer-entry.png differ diff --git a/explorations/static/net-export/netlog-viewer-example.png b/explorations/static/net-export/netlog-viewer-example.png new file mode 100644 index 000000000..83e64e411 Binary files /dev/null and b/explorations/static/net-export/netlog-viewer-example.png differ diff --git a/explorations/static/permission_based_flow.svg b/explorations/static/permission_based_flow.svg index 83acd0c1b..d4b4374a5 100644 --- a/explorations/static/permission_based_flow.svg +++ b/explorations/static/permission_based_flow.svg @@ -1,6 +1,6 @@ - + diff --git a/privacy_questionnaire.md b/privacy_questionnaire.md index 9558c3b09..3eb402c40 100644 --- a/privacy_questionnaire.md +++ b/privacy_questionnaire.md @@ -107,8 +107,8 @@ resulting from browsing in incognito mode should be cleared once the user ends t ### 15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? -Security section is work in progress: https://fedidcg.github.io/FedCM/#security Privacy is discussed -more in depth: https://fedidcg.github.io/FedCM/#privacy +Security section is work in progress: https://w3c-fedid.github.io/FedCM/#security Privacy is discussed +more in depth: https://w3c-fedid.github.io/FedCM#privacy ### 16. Do features in your specification enable origins to downgrade default security protections? diff --git a/spec/img/mock13.svg b/spec/img/mock13.svg index c1c95aae5..0e3124f5d 100644 --- a/spec/img/mock13.svg +++ b/spec/img/mock13.svg @@ -1,6 +1,6 @@ - + diff --git a/spec/img/mock23.svg b/spec/img/mock23.svg index 73f3db597..5c0ab47bc 100644 --- a/spec/img/mock23.svg +++ b/spec/img/mock23.svg @@ -1,6 +1,6 @@ - + @@ -26,7 +26,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -102,7 +102,7 @@ - + diff --git a/spec/img/mock3.svg b/spec/img/mock3.svg index b53dc96f3..cfe689696 100644 --- a/spec/img/mock3.svg +++ b/spec/img/mock3.svg @@ -1,6 +1,6 @@ - + @@ -47,7 +47,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -74,7 +74,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -96,7 +96,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -126,6 +126,6 @@ - + diff --git a/spec/img/mock31.svg b/spec/img/mock31.svg index 1870eef94..7b51bef42 100644 --- a/spec/img/mock31.svg +++ b/spec/img/mock31.svg @@ -1,6 +1,6 @@ - + diff --git a/spec/img/mock34.svg b/spec/img/mock34.svg index bf8359888..d5ec07f76 100644 --- a/spec/img/mock34.svg +++ b/spec/img/mock34.svg @@ -1,6 +1,6 @@ - + @@ -43,7 +43,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -96,6 +96,6 @@ - + diff --git a/spec/img/mock37.svg b/spec/img/mock37.svg index 29cdb03d2..a4d8d416a 100644 --- a/spec/img/mock37.svg +++ b/spec/img/mock37.svg @@ -1,6 +1,6 @@ - + @@ -38,7 +38,7 @@ - + @@ -78,7 +78,7 @@ - + @@ -127,7 +127,7 @@ - + @@ -136,7 +136,7 @@ - + @@ -203,7 +203,7 @@ - + diff --git a/spec/img/mock42.svg b/spec/img/mock42.svg index e557a8b98..f73442f67 100644 --- a/spec/img/mock42.svg +++ b/spec/img/mock42.svg @@ -1,6 +1,6 @@ - + diff --git a/spec/img/mock43.svg b/spec/img/mock43.svg index a7a52878a..c7ca753c8 100644 --- a/spec/img/mock43.svg +++ b/spec/img/mock43.svg @@ -1,6 +1,6 @@ - + diff --git a/spec/img/mock45.svg b/spec/img/mock45.svg index 73f0d2e81..6d650435c 100644 --- a/spec/img/mock45.svg +++ b/spec/img/mock45.svg @@ -1,6 +1,6 @@ - + @@ -54,7 +54,7 @@ - + @@ -100,7 +100,7 @@ - + @@ -119,6 +119,6 @@ - + diff --git a/spec/img/mock46.svg b/spec/img/mock46.svg index 4b2f074b0..eae964246 100644 --- a/spec/img/mock46.svg +++ b/spec/img/mock46.svg @@ -1,6 +1,6 @@ - + diff --git a/spec/img/mock5.svg b/spec/img/mock5.svg index 0f9521a3c..7a15559dd 100644 --- a/spec/img/mock5.svg +++ b/spec/img/mock5.svg @@ -1,6 +1,6 @@ - + @@ -40,10 +40,10 @@ - + - + diff --git a/spec/index.bs b/spec/index.bs index bf35cb4d4..0bb47b1d3 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -1,12 +1,15 @@
@@ -43,6 +46,13 @@ spec: webdriver; urlPrefix: https://w3c.github.io/webdriver/
 spec: webappsec-fetch-metadata; urlPrefix: https://w3c.github.io/webappsec-fetch-metadata/
     type: dfn
         text: Directly User-Initiated Requests; url: directly-user-initiated
+spec: login-status; urlPrefix: https://w3c-fedid.github.io/login-status
+    type: dfn
+        text: logged-in; url: logged-in
+        text: logged-out; url: logged-out
+        text: unknown; url: unknown
+        text: get the login status; url: get-the-login-status
+        text: set the login status; url: set-the-login-status
 
@@ -85,10 +96,10 @@ script + svg :is(polygon, text) { } - - - - + + + + # Introduction # {#introduction} @@ -327,130 +338,6 @@ value (which is used when a resource loaded as a third-party, not first-party). for an [=IDP=] to adopt the FedCM API. It doesn't introduce security issues on the API because the [=RP=] cannot inspect the results from the fetches in any way. - -## The Login Status API ## {#browser-api-login-status} - - -### Login Status Map ### {#hdr-login-status-map} - -Each [=user agent=] keeps a global, persistent Login Status -map, an initially empty [=map=]. The [=map/keys=] in this map are -[=/origin=] (of [=IDPs=]), and the [=map/values=] are enums that can be one of -"unknown", "logged-in", -and "logged-out". - -
-To get the login status for an [=/origin=] |origin|: -1. If [=Login Status map=][|origin|] exists, return it. -1. Otherwise, return [=unknown=]. - -
- -
-To set the login status for an [=/origin=] |origin| to -value |value|: -1. Assert that |value| is one of [=logged-in=] or [=logged-out=]. -1. [=map/Set=] [=Login Status map=][|origin|] to |value|. - -
- -### HTTP header API ### {#login-status-http} - -[=IDPs=] can set the login status using an HTTP [=response=] [=header=] as follows. - -Issue: The HTTP header checking should move into the Fetch spec, since it - affects all resource loads. - -For each [=http-redirect fetch=] and [=http fetch=]'s [=response=], let |value| -be the result of [=get a structured field value=] from the response's header -list with name "Set-Login" and type "`item`". If |value| is not null, -process this header as follows: - -
-1. Let |origin| be the response's [=response/URL=]'s [=/origin=]. -1. Let |client| be the [=/request=]'s [=request/client=]. -1. If the request's [=request/destination=] is not `"document"`: - 1. If |client| is null, return. - 1. If |origin| is not [=same origin=] with the [=/request=]'s - [=request/origin=], return. - 1. If |client| is not [=same-origin with its ancestors=], return. -1. Assert that |value| is a tuple. -1. Let |token| be the first entry of |value|. -1. If |token| is `"logged-in"`, [=set the login status=] for |origin| - to [=logged-in=]. -1. If |token| is `"logged-out"`, [=set the login status=] for |origin| - to [=logged-out=]. - -
- -### JavaScript API ### {#login-status-javascript} - -[=IDPs=] can also use a JavaScript API to update the stored login status: - - -
-enum LoginStatus {
-  "logged-in",
-  "logged-out",
-};
-
-[Exposed=Window, SecureContext] 
-interface NavigatorLogin {
-  Promise<undefined> setStatus(LoginStatus status);
-};
-
-partial interface Navigator {
-  [SecureContext] readonly attribute NavigatorLogin login;
-};
-
- -
-When {{NavigatorLogin/setStatus()}} is called with argument |status|: -1. If the [=current settings object=] is not [=same-origin with its ancestors=], - throw a {{SecurityError}} {{DOMException}}. -1. Let |origin| be the [=current settings object=]'s - [=environment settings object/origin=]. -1. Let |value| be [=logged-in=] if |status| is `"logged-in"` or [=logged-out=] - if |status| is `"logged-out"`. -1. [=Set the login status=] for |origin| to |value|. - -
- -### Clearing the Login Status Map data ### {#login-status-clear-data} - -User agents MUST also clear the [=Login Status map=] data when: - : the user clears all cookies or site settings data - :: The user agent MUST clear the entire map. - : the user clears all cookies or all site data for a specific origin - :: The user agent MUST remove all entries that would be affected - by the deleted cookies, that is, any entry with an origin - to which a deleted cookie could be sent to. - - Note: For example, domain cookies may affect subdomains of - the deleted origin, e.g. clearing cookies for `google.com` - should also reset the login status for `accounts.google.com`, - since it may rely on a domain cookie for google.com. - : the user deletes individual cookies (if allowed by the user agent) - :: the behavior is user agent-defined. - - Note: The user agent MAY want to reset the state to [=unknown=], - since is impossible to know whether this cookie affects - authorization state. - : the user agent receives a Clear-Site-Data header with a - value of `"cookies"` or `"*"`, and the [=/request=]'s [=request/client=] is - not null, and the client's [=environment settings object/origin=] is [=same - origin=] with the [=top-level origin=] - :: while [$clear cookies for origin|clearing cookies for - origin$] it MUST remove any entries in the [=Login Status Map=] where - the [=map/key=] is the input origin. - - Issue: Once Clear-Site-Data [supports partitioned cookies](https://github.com/w3c/webappsec-clear-site-data/issues/72), - this wording should be updated. - -Note: Other website-initiated cookie changes should not affect this map. When - [=IDP=] login state changes, it should send an explicit [=Set-Login=] header. - [=RP=] state should not affect this map since it only reflects [=IDP=] state. - ## The connected accounts set ## {#browser-connected-accounts-set} @@ -469,24 +356,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 connected accounts set. -
-To compute the connection status given an {{IdentityProviderConfig}} |provider|, an -{{IdentityProviderAccount}} |account|, and a |globalObject|, run the following steps. This returns -connected or -disconnected. - 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=]. +
+To compute the connected account key 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|). +
+ +
+When asked whether an {{IdentityProviderAccount}} |account| is +eligible for auto reauthentication 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|. +
+ +
+When asked to compute the connection status given an {{IdentityProviderAccount}} +|account|, an {{IdentityProviderConfig}} |provider| and a |globalObject|, run the following steps. +This returns connected or +disconnected. + 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=].
@@ -535,6 +442,7 @@ This specification introduces a new type of {{Credential}}, called an {{Identity interface IdentityCredential : Credential { static Promise<undefined> disconnect(optional IdentityCredentialDisconnectOptions options = {}); readonly attribute USVString? token; + readonly attribute boolean isAutoSelected; }; @@ -544,6 +452,10 @@ This specification introduces a new type of {{Credential}}, called an {{Identity : {{IdentityCredential/token}} :: The {{IdentityCredential/token}}'s attribute getter returns the value it is set to. It represents the minted {{IdentityProviderToken/token}} provided by the [=IDP=]. + : {{IdentityCredential/isAutoSelected}} + :: {{IdentityCredential/isAutoSelected}}'s attribute getter returns the value it is + set to. It represents whether the user's identity credential was automatically selected when + going through the UI flow which resulted in this {{IdentityCredential}}. : {{Credential/[[type]]}} :: The {{IdentityCredential}}'s {{Credential/[[type]]}}'s value is "identity". : {{Credential/[[discovery]]}} @@ -590,10 +502,10 @@ When asked to attempt to disconnect 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|. @@ -758,7 +670,7 @@ dictionary IdentityProviderRequestOptions : IdentityProviderConfig { agent to show to the user. If provided, the user agent will not show accounts which do not match this login hint value. It generally matches some attribute from the desired {{IdentityProviderAccount}}. - : {{IdentityProviderConfig/domainHint}} + : {{IdentityProviderRequestOptions/domainHint}} :: A string representing the domain hint corresponding to a domain which the [=RP=] is interested in, or "any" if the [=RP=] wants any account associated with at least one domain hint. If provided, the user agent will not show accounts which do not match the domain hint @@ -891,8 +803,8 @@ the exception thrown. 1. Let |config| be the result of running [=fetch the config file=] with |provider| and |globalObject|. 1. If |config| is failure, return (failure, false). - 1. Fetch accounts list step: Let |accountsList| be the result of - [=fetch the accounts list=] with |config|, |provider|, and |globalObject|. + 1. Fetch accounts step: Let |accountsList| be the result of + [=fetch the accounts=] with |config|, |provider|, and |globalObject|. 1. If |accountsList| is failure, or the size of |accountsList| is 0: 1. [=Set the login status=] for the [=/origin=] of the {{IdentityProviderConfig/configURL}} to [=logged-out=]. @@ -928,7 +840,7 @@ the exception thrown. 1. If |result| is failure, return (failure, true). The user agent MAY show a dialog to the user before or after returning failure indicating this failure. - 1. Otherwise, go back to the [=fetch accounts list step=]. + 1. Otherwise, go back to the [=fetch accounts step=]. 1. Assert: |accountsList| is not failure and the size of |accountsList| is not 0. 1. [=Set the login status=] for the [=/origin=] of the @@ -938,14 +850,14 @@ the exception thrown. {{IdentityProviderAccount/login_hints}} does not [=list/contain=] |provider|'s {{IdentityProviderRequestOptions/loginHint}}. 1. If |accountList| is now empty, go to the [=mismatch dialog step=]. - 1. If |provider|'s {{IdentityProviderConfig/domainHint}} is not empty: + 1. If |provider|'s {{IdentityProviderRequestOptions/domainHint}} is not empty: 1. For every |account| in |accountList|: - 1. If {{IdentityProviderConfig/domainHint}} is "any": + 1. If {{IdentityProviderRequestOptions/domainHint}} is "any": 1. If |account|'s {{IdentityProviderAccount/domain_hints}} is empty, remove |account| from |accountList|. 1. Otherwise, remove |account| from |accountList| if |account|'s {{IdentityProviderAccount/domain_hints}} does not [=list/contain=] |provider|'s - {{IdentityProviderConfig/domainHint}}. + {{IdentityProviderRequestOptions/domainHint}}. 1. If |accountList| is now empty, go to the [=mismatch dialog step=]. 1. For each |acc| in |accountsList|: 1. If |acc|["{{IdentityProviderAccount/picture}}"] is present, [=fetch the account picture=] @@ -957,47 +869,42 @@ 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. Let |permission| and |disclosureTextShown| both be false. + 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 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. 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. 1. If |permission| is false, then return (failure, true). 1. Let |credential| be the result of running the [=fetch an identity assertion=] algorithm with - |account|'s {{IdentityProviderAccount/id}}, |disclosureTextShown|, |provider|, |config|, and - |globalObject|. + |account|'s {{IdentityProviderAccount/id}}, |disclosureTextShown|, |isAutoSelected|, + |provider|, |config|, and |globalObject|. 1. Return |credential|.
@@ -1022,51 +929,59 @@ or failure. 1. Set |rootUrl|'s [=url/scheme=] to |configUrl|'s [=url/scheme=]. 1. Set |rootUrl|'s [=url/host=] to |configUrl|'s [=url/host=]'s [=host/registrable domain=]. 1. Set |rootUrl|'s [=url/path=] to the list «".well-known", "web-identity"». - 1. Let |wellKnownRequest| be a new [=/request=] as follows: - - : [=request/URL=] - :: |rootUrl| - : [=request/client=] - :: null - : [=request/window=] - :: "no-window" - : [=request/service-workers mode=] - :: "none" - : [=request/destination=] - :: "webidentity" - : [=request/origin=] - :: a unique [=opaque origin=] - : [=request/header list=] - :: a [=list=] containing a single [=header=] with [=header/name=] set to `Accept` and - [=header/value=] set to `application/json` - : [=request/referrer policy=] - :: "no-referrer" - : [=request/credentials mode=] - :: "omit" - : [=request/mode=] - :: "no-cors" - - Issue: The spec is yet to be updated so that all requests are created - with [=request/mode=] set to "user-agent-no-cors". See the relevant - [pull request](https://github.com/whatwg/fetch/pull/1533) for details. - 1. Let |config|, |configInWellKnown| both be null. - 1. [=Fetch request=] with |wellKnownRequest| and |globalObject|, and with processResponseConsumeBody - 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 {{IdentityProviderWellKnown}}, - |discovery|. - 1. If one of the previous two steps threw an exception, or if the - [=list/size=] of |discovery|["{{IdentityProviderWellKnown/provider_urls}}"] is - greater than 1, set |configInWellKnown| to false. - - Issue: [relax](https://github.com/fedidcg/FedCM/issues/333) the size of the - provider_urls array. + 1. Let |rpOrigin| be |globalObject|'s [=associated Document=]'s [=Document/origin=]. + 1. If |rpOrigin| is not an [=opaque origin=], and |rootUrl|'s [=url/host=] is equal + to |rpOrigin|'s [=host/registrable domain=], and |rootUrl|'s [=url/scheme=] is + equal to |rpOrigin|'s [=origin/scheme=], set |configInWellKnown| to true. - 1. Otherwise, set to |configInWellKnown| to true if - |discovery|["{{IdentityProviderWellKnown/provider_urls}}"][0] [=string/is=] equal to - |provider|'s {{IdentityProviderConfig/configURL}}, and to false otherwise. + Note: Because domain cookies are valid across an entire site, there is no privacy + benefit from doing the well-known check if the RP and IDP are in the same site. + 1. Otherwise: + 1. Let |wellKnownRequest| be a new [=/request=] as follows: + + : [=request/URL=] + :: |rootUrl| + : [=request/client=] + :: null + : [=request/window=] + :: "no-window" + : [=request/service-workers mode=] + :: "none" + : [=request/destination=] + :: "webidentity" + : [=request/origin=] + :: a unique [=opaque origin=] + : [=request/header list=] + :: a [=list=] containing a single [=header=] with [=header/name=] set to `Accept` and + [=header/value=] set to `application/json` + : [=request/referrer policy=] + :: "no-referrer" + : [=request/credentials mode=] + :: "omit" + : [=request/mode=] + :: "no-cors" + + Issue: The spec is yet to be updated so that all requests are created + with [=request/mode=] set to "user-agent-no-cors". See the relevant + [pull request](https://github.com/whatwg/fetch/pull/1533) for details. + + 1. [=Fetch request=] with |wellKnownRequest| and |globalObject|, and with processResponseConsumeBody + 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 {{IdentityProviderWellKnown}}, + |discovery|. + 1. If one of the previous two steps threw an exception, or if the + [=list/size=] of |discovery|["{{IdentityProviderWellKnown/provider_urls}}"] is + greater than 1, set |configInWellKnown| to false. + + Issue: [relax](https://github.com/fedidcg/FedCM/issues/333) the size of the + provider_urls array. + + 1. Otherwise, set to |configInWellKnown| to true if + |discovery|["{{IdentityProviderWellKnown/provider_urls}}"][0] [=string/is=] equal to + |provider|'s {{IdentityProviderConfig/configURL}}, and to false otherwise. 1. Let |configRequest| be a new request as follows: @@ -1148,15 +1063,15 @@ dictionary IdentityProviderAPIConfig { -### Fetch the accounts list ### {#fetch-accounts-list} +### Fetch the accounts ### {#fetch-accounts} -The fetch the accounts list algorithm fetches the [=accounts list endpoint=] to determine the +The fetch the accounts algorithm fetches the [=accounts endpoint=] to determine the list of [=IDP=] accounts that the user is signed in to, so that the user agent can later show the FedCM UI to the user.
-To fetch the accounts list given an {{IdentityProviderAPIConfig}} |config|, an +To fetch the accounts given an {{IdentityProviderAPIConfig}} |config|, an {{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|, @@ -1277,9 +1192,9 @@ the token that will be provided to the [=RP=].
To fetch an identity assertion given a {{USVString}} - |accountId|, a boolean |disclosureTextShown|, an {{IdentityProviderRequestOptions}} |provider|, - an {{IdentityProviderAPIConfig}} |config|, and |globalObject|, run the following steps. This - returns an {{IdentityCredential}} or failure. + |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. 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. @@ -1288,6 +1203,7 @@ To fetch an identity assertion given a {{USVString}} 1. ("nonce", |provider|'s {{IdentityProviderRequestOptions/nonce}}) 1. ("account_id", |accountId|) 1. ("disclosure_text_shown", |disclosureTextShown|) + 1. ("is_auto_selected", |isAutoSelected|) 1. Let |request| be a new request as follows: : [=request/url=] @@ -1314,11 +1230,7 @@ To fetch an identity assertion given a {{USVString}} : [=request/credentials mode=] :: "include" : [=request/mode=] - :: "no-cors" - - Issue: The spec is yet to be updated so that all requests are created - with [=request/mode=] set to "user-agent-no-cors". See the relevant - [pull request](https://github.com/whatwg/fetch/pull/1533) for details. + :: "cors" 1. Let |credential| be null. 1. [=Fetch request=] with |request| and |globalObject|, and with processResponseConsumeBody @@ -1331,6 +1243,8 @@ To fetch an identity assertion given a {{USVString}} 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. Wait for |credential| to be set. 1. Return |credential|.
@@ -1515,10 +1429,10 @@ success or failure. 1. Assert: |loginUrl| is not failure (the [=user agent=] has previously checked that |config|.{{IdentityProviderAPIConfig/login_url}} is a valid URL). 1. Let |queryList| be a new [=list=]. - 1. If |provider|'s {{IdentityProviderConfig/loginHint}} is not empty, [=list/append=] - ("login_hint", {{IdentityProviderConfig/loginHint}}) to |queryList|. - 1. If |provider|'s {{IdentityProviderConfig/domainHint}} is not empty, [=list/append=] - ("domain_hint", {{IdentityProviderConfig/domainHint}}) to |queryList|. + 1. If |provider|'s {{IdentityProviderRequestOptions/loginHint}} is not empty, [=list/append=] + ("login_hint", {{IdentityProviderRequestOptions/loginHint}}) to |queryList|. + 1. If |provider|'s {{IdentityProviderRequestOptions/domainHint}} is not empty, [=list/append=] + ("domain_hint", {{IdentityProviderRequestOptions/domainHint}}) to |queryList|. 1. If |queryList| is not [=list/empty=]: 1. Let |queryParameters| be the result of the [=urlencoded serializer=] with |queryList|. 1. If |loginUrl|'s [=url/query=] is not null or empty, prepend "&" to |queryParameters|. @@ -1536,8 +1450,8 @@ success or failure. with the [=/origin=] of the {{IdentityProviderAPIConfig/login_url}}. Note: The IDP login flow may set this value to logged-in using - either the [[#login-status-javascript|JavaScript]] or - [[#login-status-http|HTTP header]] API during the login + either the [login-status.html#login-status-javascript](JavaScript) or + [login-status.html#login-status-http](HTTP header) API during the login flow. It is also possible that this change happened in a different browsing context. 1. If |loginStatus| is [=logged-in=], return success. @@ -1581,7 +1495,7 @@ An {{IdentityUserInfo}} represents user account information from a user. This in to the [=IDP=] once the user has already used the FedCM API to login in the [=RP=]. That is, it is exposed when there exists an account |account| such that the [=connected accounts set=] [=list/contains=] the triple ([=RP=], [=IDP=], |account|). The information matches what is received from the -accounts list endpoint. The [=IDP=] can obtain this information by invoking the +accounts endpoint. The [=IDP=] can obtain this information by invoking the {{IdentityProvider/getUserInfo()}} static method from an iframe matching the [=/origin=] of its {{IdentityProviderConfig/configURL}}. @@ -1611,9 +1525,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 @@ -1633,10 +1547,10 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP |globalObject|. 1. If |config| is failure, [=reject=] |promise| with a new "{{NetworkError}}" {{DOMException}}. - 1. Let |accountsList| be the result of [=fetch the accounts list=] with |config|, |provider|, + 1. Let |accountsList| be the result of [=fetch the accounts=] with |config|, |provider|, and |globalObject|. - 1. Let |isReturningAccount| be a new [=list=] of the same length as |accountsList|, with all - values initially set to false. + 1. Let |isEligibleForAutoReauthentication| be a new [=list=] of the same length as |accountsList|, + with all values initially set to false. 1. For each |i| from 0 to the length of |accountsList| minus 1: 1. Let |account| be |accountsList|[|i|]. 1. If |account|["{{IdentityProviderAccount/approved_clients}}"] is not empty and it does not @@ -1647,10 +1561,10 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP account out of band. 1. [=Compute the connection status=] of |provider|, |account|, and |globalObject|. If the - result is [=compute the connection status/connected=], set |isReturningAccount|[|i|] to - true. - 1. If |isReturningAccount| does not [=list/contain=] true, [=reject=] |promise| with a new - "{{NetworkError}}" {{DOMException}}. + result is [=compute the connection status/connected=], set + |isEligibleForAutoReauthentication|[|i|] to true. + 1. If |isEligibleForAutoReauthentication| does not [=list/contain=] true, [=reject=] |promise| with + a new "{{NetworkError}}" {{DOMException}}. 1. Let |userInfoList| be a new [=list=]. 1. Let |notReturningUserInfos| be a new [=list=]. 1. For each |i| from 0 to the length of |accountsList| minus 1: @@ -1665,7 +1579,7 @@ When invoking the {{IdentityProvider/getUserInfo()}} method given an {{IdentityP :: |account|["{{IdentityProviderAccount/given_name}}"] : {{IdentityUserInfo/picture}} :: |account|["{{IdentityProviderAccount/picture}}"] - 1. If |isReturningAccount|[|i|], [=list/append=] |userInfo| to |userInfoList|. + 1. If |isEligibleForAutoReauthentication|[|i|], [=list/append=] |userInfo| to |userInfoList|. 1. Otherwise, [=list/append=] |userInfo| to |notReturningUserInfos|. 1. [=list/Extend=] |userInfo| with |notReturningUserInfos|. 1. [=Resolve=] a new {{Promise}} with |userInfoList|. @@ -1692,7 +1606,7 @@ to execute these in such a way that it does not allow the user to be tracked (by impersonating an [=IDP=]) on to the site using FedCM. The following table has information about the network requests performed: - +
@@ -1848,12 +1762,12 @@ For example: -## Accounts list endpoint ## {#idp-api-accounts-endpoint} +## Accounts endpoint ## {#idp-api-accounts-endpoint} -The accounts list endpoint provides the list of accounts the user has at the [=IDP=]. +The accounts endpoint provides the list of accounts the user has at the [=IDP=]. -The [=accounts list endpoint=] is fetched in the [=fetch the accounts list=] algorithm: +The [=accounts endpoint=] is fetched in the [=fetch the accounts=] algorithm: (a) **with** [=IDP=] cookies, (b) **with** the Sec-Fetch-Dest header set to `webidentity`, @@ -1898,7 +1812,7 @@ Every {{IdentityProviderAccount}} is expected to have members with the following matching a given value is shown to the user. : domain_hints :: A list of strings which correspond to all of the domain hints which match with this account. - An [=RP=] can use the {{IdentityProviderConfig/domainHint}} to request that only an account + An [=RP=] can use the {{IdentityProviderRequestOptions/domainHint}} to request that only an account matching a given value or containing some domain hint is shown to the user. @@ -2034,8 +1948,6 @@ the Origin header value is represented by the [=IDP=]-specific, the [=user agent=] cannot perform this check. -Note: An [=IDP=] should validate the nonce, if present, to prevent CSRF-style attacks. - The response body must be a JSON object that can be [=converted to an IDL value|converted=] to an {{IdentityProviderToken}} without an exception. Every {{IdentityProviderToken}} is expected to have members with the following semantics: @@ -2085,7 +1997,7 @@ method: (b) **with** [=IDP=] cookies, (c) **with** the [=RP=]'s origin in the Origin header, (d) **with** the Sec-Fetch-Dest header set to `webidentity`, -(e) **without** following [[RFC9110#section-10.2.2|HTTP redirects]], and +(e) **without** following [[RFC9110#field.location|HTTP redirects]], and (f) in "cors" [=request/mode=]. It will also contain the following in the request body `application/x-www-form-urlencoded`: @@ -2343,8 +2255,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}} @@ -2372,8 +2284,8 @@ The [=remote end steps=] are: Note: This command lets automation verify that the - [context api](https://github.com/fedidcg/FedCM/blob/main/proposals/context-api.md) - was applied properly + [context api](#dom-identitycredentialrequestoptions-context) + was applied properly The [=remote end steps=] are: @@ -2519,6 +2431,16 @@ credentialed requests it receives, which ensures that the request was initiated based on the FedCM API. A malicious actor cannot spam FedCM API calls, so this is sufficient protection for the new [=IDP=] endpoints. + +## CORS Header ## {#sec-cors-header} + + +The FedCM API allows the response from the [=identity assertion endpoint=] to be shared to the +[=RP=]. Because of this, we impose the requirement that the [=IDP=] explicitly consents to this +sharing taking place by using the "cors" [=request/mode=] when fetching this endpoint. This also +helps with servers that may accidentally ignore the Sec-Fetch-Dest: they cannot +ignore CORS, as without it the fetch will fail. + ## Browser Surface Impersonation ## {#browser-surface-impersonation} @@ -2608,7 +2530,7 @@ origin of the fetched URLs. or referrer. Thus, anyone could perform this fetch, and the information contained therein is considered public. -* The [=accounts list endpoint=] fetch can't be used to track users because it is performed with cookies from the +* The [=accounts endpoint=] fetch can't be used to track users because it is performed with cookies from the [=IDP=] but, importantly, without the {{id_assertion_endpoint_request/client_id}} or referrer. This in theory is a new power that the [=RP=] gains that it would not have otherwise. Preventing too many of these fetches may be important, but [=IDP=]s are already expected to protect against DoS attacks. In addition, the @@ -2928,3 +2850,44 @@ Note: write down the Acknowledgements section. } } + + +# FPWD Issues # {#openissues} + +Note: The WG has labeled the following issues as critical open issues that must be formally addressed before publication of a Candidate Recommendation. + + + + + diff --git a/w3c.json b/w3c.json index df8f7eac0..b9330fe75 100644 --- a/w3c.json +++ b/w3c.json @@ -1,5 +1,5 @@ { - "group": [80485] -, "contacts": ["cwilso"] -, "repo-type": "cg-report" + "group": "wg/fedid" +, "contacts": ["simoneonofri", "hlflanagan", "wseltzer"] +, "repo-type": "rec-track" }
Endpointcookiesclient_idorigin
manifestsnonono
accounts_endpointyesnono