跳转到主要内容

category

本文详细介绍了身份提供程序(IdP)与Federated Credential Management(FedCM)API集成所需的所有步骤。

IdP integration steps

To integrate with FedCM, an IdP needs to do the following:

  1. Provide a well-known file to identify the IdP.
  2. Provide a config file and endpoints for accounts list and assertion issuance (and optionally, client metadata).
  3. Update its login status using the Login Status API.

Provide a well-known file

存在潜在的隐私问题,即IdP能够辨别用户是否在未经明确同意的情况下访问了RP。这具有跟踪影响,因此需要IdP提供一个众所周知的文件来验证其身份并缓解此问题。

众所周知的文件是通过一个不遵循重定向的无条件GET请求来请求的。这有效地防止了IdP了解是谁提出了请求以及哪个RP试图连接。

众所周知的文件必须从IdP的eTLD+1以/.aknowledge/web标识提供。例如,如果IdP端点在https://accounts.idp.example/,他们必须在https://idp.example/.well-known/web-identity.众所周知的文件内容应该具有以下JSON结构:

JSON

{
  "provider_urls": ["https://accounts.idp.example/config.json"]
}

The provider_urls member should contain an array of URLs pointing to valid IdP config files that can be used by RPs to interact with the IdP. The array length is currently limited to one.

The Sec-Fetch-Dest HTTP header

All requests sent from the browser via FedCM include a Sec-Fetch-Dest: webidentity header. All IdP endpoints that receive credentialed requests (i.e. accounts_endpoint and id_assertion_endpoint) must confirm this header is included to protect against CSRF attacks.

Provide a config file and endpoints

IdP配置文件提供了浏览器处理身份联合流和管理登录所需的端点列表。端点需要与配置的原点相同。
浏览器通过GET方法对配置文件进行无条件请求,该方法不遵循重定向。这有效地防止了IdP了解是谁提出了请求以及哪个RP试图连接。

The config file (hosted at https://accounts.idp.example/config.json in our example) should have the following JSON structure:

JSON

{
  "accounts_endpoint": "/accounts.php",
  "client_metadata_endpoint": "/client_metadata.php",
  "id_assertion_endpoint": "/assertion.php",
  "login_url": "/login",
  "branding": {
    "background_color": "green",
    "color": "0xFFEEAA",
    "icons": [
      {
        "url": "https://idp.example/icon.ico",
        "size": 25
      }
    ]
  }
}

The properties are as follows:

accounts_endpoint

The URL for the accounts list endpoint, which returns a list of accounts that the user is currently signed in to on the IdP. The browser uses these to create a list of sign-in choices to show to the user in the browser-provided FedCM UI.

client_metadata_endpoint Optional

The URL for the client metadata endpoint, which provides URLs pointing to the RP's metadata and terms of service pages, to be used in the FedCM UI.

id_assertion_endpoint

The URL for the ID assertion endpoint, which when sent valid user credentials should respond with a validation token that the RP can use to validate the authentication.

login_url

The login page URL for the user to sign into the IdP.

branding Optional

Contains branding information that will be used in the browser-supplied FedCM UI to customize its appearance as desired by the IdP.

The following table summarizes the different requests made by the FedCM API:

Endpoint/resource Method Credentialed (with cookies) Includes Origin
well-known/config.json GET No No
accounts_endpoint GET Yes No
client_metadata_endpoint GET No Yes
id_assertion_endpoint POST Yes Yes

Note: For a description of the FedCM flow in which these endpoints are accessed, see FedCM sign-in flow.

Note: None of the requests made by the FedCM API to the endpoints detailed here allow for following redirects, for privacy purposes.

The accounts list endpoint

The browser sends credentialed requests (i.e. with a cookie that identifies the user that is signed in) to this endpoint via the GET method. The request has no client_id parameter, Origin header, or Referer header. This effectively prevents the IdP from learning which RP the user is trying to sign in to. The list of accounts returned is RP-agnostic.

For example:

HTTP

GET /accounts.php HTTP/1.1
Host: idp.example
Accept: application/json
Cookie: 0x23223
Sec-Fetch-Dest: webidentity

The response to a successful request returns a list of all the IdP accounts that the user is currently signed in with (not specific to any particular RP), with a JSON structure that matches the following:

JSON

{
  "accounts": [
    {
      "id": "john_doe",
      "given_name": "John",
      "name": "John Doe",
      "email": "john_doe@idp.example",
      "picture": "https://idp.example/profile/123",
      "approved_clients": ["123", "456", "789"],
      "login_hints": ["john_doe", "john_doe@idp.example"]
    },
    {
      "id": "johnny",
      "given_name": "Johnny",
      "name": "Johnny",
      "email": "johnny@idp.example",
      "picture": "https://idp.example/profile/456",
      "approved_clients": ["abc", "def", "ghi"],
      "login_hints": ["johnny", "johnny@idp.example"]
    }
  ]
}

This includes the following information:

id

The unique ID of the user.

name

The family name of the user.

email

The email address of the user.

given_name Optional

The given name of the user.

picture Optional

The URL of the user's avatar image.

approved_clients Optional

An array of RP clients that the user has registered with.

login_hints Optional

An array of strings representing the account. These strings are used to filter the list of account options that the browser offers for the user to sign-in. This occurs when the loginHint property is provided within identity.providers in a related get() call. Any account with a string in its login_hints array that matches the provided loginHint is included.

Note:: If the user is not signed in to any IdP accounts, the endpoint should respond with HTTP 401 (Unauthorized).

The client metadata endpoint

The browser sends uncredentialed requests to this endpoint via the GET method, with the clientId passed into the get() call as a parameter.

For example:

HTTP

GET /client_metadata.php?client_id=1234 HTTP/1.1
Host: idp.example
Origin: https://rp.example/
Accept: application/json
Sec-Fetch-Dest: webidentity

The response to a successful request includes URLs pointing to the RP's metadata and terms of service pages, to be used in the browser-supplied FedCM UI. This should follow the JSON structure seen below:

JSON

{
  "privacy_policy_url": "https://rp.example/privacy_policy.html",
  "terms_of_service_url": "https://rp.example/terms_of_service.html"
}

The ID assertion endpoint

The browser sends credentialed requests to this endpoint via the POST method, with a content type of application/x-www-form-urlencoded. The request also includes a payload including details about the attempted sign-in and the account to be validated.

It should look something like this:

HTTP

POST /assertion.php 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=true

A request to this endpoint is sent as a result of the user choosing an account to sign in with from the relevant browser UI. When sent valid user credentials, this endpoint should respond with a validation token that the RP can use to validate the user on its own server, according to the usage instructions outlined by the IdP they are using for identity federation. Once the RP validates the user, they can sign them in, sign them up to their service, etc.

JSON

{
  "token": "***********"
}

The request payload contains the following params:

client_id

The RP's client identifier (which matches the clientId from the original get() request).

account_id

The unique ID of the user account to be signed in (which matches the user's id from the accounts list endpoint response).

nonce Optional

The request nonce, provided by the RP.

disclosure_text_shown

A string of "true" or "false" indicating whether the disclosure text was shown or not. The disclosure text is the information shown to the user (which can include the terms of service and privacy policy links, if provided) if the user is signed in to the IdP but doesn't have an account specifically on the current RP (in which case they'd need to choose to "Continue as..." their IdP identity and then create a corresponding account on the RP).

is_auto_selected

A string of "true" or "false" indicating whether the authentication validation request has been issued as a result of auto-reauthentication, i.e. without user mediation. This can occur when the get() call is issued with a mediation option value of "optional" or "silent". It is useful for the IdP to know whether auto reauthentication occurred for performance evaluation and in case higher security is desired. For example, the IdP could return an error code telling the RP that it requires explicit user mediation (mediation="required").

Note: If the get() call succeeds, the is_auto_selected value is also communicated to the RP via the IdentityCredential.isAutoSelected property.

ID assertion error responses

If the IdP cannot issue a token — for example if the client is unauthorized — the ID assertion endpoint will respond with an error response containing information about the nature of the error. For example:

JSON

{
  "error": {
    "code": "access_denied",
    "url": "https://idp.example/error?type=access_denied"
  }
}

The error response fields are as follows:

code Optional

A string. This can be either a known error from the OAuth 2.0 specified error list or an arbitrary string.

url Optional

A URL. This should be a web page containing human-readable information about the error to display to users, such as how to fix the error or contact customer. The URL must be same-site with the IdP's config URL.

This information can be used in a couple of different ways:

  • The browser can display a custom UI to the user informing them of what went wrong (see the Chrome documentation for an example). Bear in mind that if the request failed because the IdP server is unavailable, it obviously can't return any information. In such cases, the browser will report this via a generic message.
  • The associated RP navigator.credentials.get() call used to attempt sign-in will return the above information when the promise rejects, which can be used to handle the error. For example, an RP may wish to follow-up the browser's custom UI with some information to help the user succeed in a future sign-in attempt. The get() Error API example shows what this looks like in code.

Update login status using the Login Status API

登录状态API允许IdP通知浏览器其在特定浏览器中的登录(登录)状态-我们的意思是“是否有任何用户在当前浏览器上登录到IdP”。浏览器为每个IdP存储此状态;FedCM API然后使用它来减少它对IdP的请求数量(因为当没有用户登录IdP时,它不需要浪费时间请求帐户)。它还可以缓解潜在的定时攻击。

For each known IdP (identified by its config URL) the browser keeps a tri-state variable representing the login state with three possible values:

  • "logged-in": The IdP has at least one user account signed in. Note that, at this stage, the RP and browser don't know which user that is. Information on specific users is returned from the IdP's accounts_endpoint at a later point in the FedCM flow.
  • "logged-out": All IdP accounts are currently signed out.
  • "unknown": The sign-in status of this IdP is not known. This is the default value.

Setting login status

The IdP should update its login status when a user signs into or out of the IdP. This can be done in two different ways:

  • The Set-Login HTTP response header can be set in a top-level navigation or a same-origin subresource request:

    HTTP

    Set-Login: logged-in
    
    Set-Login: logged-out
    
  • The Navigator.login.setStatus() method can be called from the IdP origin:

    JS

    /* Set logged-in status */
    navigator.login.setStatus("logged-in");
    
    /* Set logged-out status */
    navigator.login.setStatus("logged-out");
    

How login status affects federated sign-in flow

When an RP attempts federated sign-in, the login status is checked:

  • If the login status is "logged-in", a request is made to the IdP's accounts list endpoint and available accounts for sign-in are displayed to the user in the browser-provided FedCM dialog.
  • If the login status is "logged-out", the promise returned by the FedCM get() request rejects without making a request to the accounts list endpoint. In such a case it is up to the developer to handle the flow, for example by prompting the user to go and sign in to a suitable IdP.
  • If the login status is "unknown", a request is made to the IdP's accounts list endpoint and the login status is updated depending on the response:
    • If the endpoint returns a list of available accounts for sign-in, update the status to "logged-in" and display the sign-in options to the user in the browser-provided FedCM dialog.
    • If the endpoint returns no accounts, update the status to "logged-out"; the promise returned by the FedCM get() request then rejects.

What if the browser and the IdP login status become out of sync?

尽管登录状态API通知浏览器IdP的登录状态,但浏览器和IdP可能会不同步。例如,IdP会话可能会过期,这意味着所有用户帐户最终都已注销,但登录状态仍设置为“已登录”(应用程序无法将登录状态设置为“注销”)。在这种情况下,当尝试联合登录时,将向IdP的帐户列表端点发出请求,但不会返回任何可用帐户,因为会话不再可用。

When this occurs, the browser can dynamically let a user sign into the IdP by opening the IdP's sign-in page in a dialog (the sign-in URL is found in the IdP's config file login_url ). The exact nature of this flow is up to the browser; for example, Chrome handles it like this.

Once the user is signed in to the IdP, the IdP should:

See also