Implement an identity solution with FedCM

FedCM (Federated Credential Management) is a privacy-preserving approach to federated identity services (such as "Sign in with...") that allows users to log into sites without sharing their personal information with the identity service or the site.

The FedCM implementation includes several core steps for both IdP (Identity Provider) and RP (Relying Party).

IdPs must complete the following steps to implement FedCM:

RPs need to complete the following steps to enable FedCM on their site:

Implement FedCM as an IdP

Learn more about the steps to implement FedCM on the IdP's side.

Create a well-known file

To prevent trackers from abusing the API, a well-known file must be served from /.well-known/web-identity of eTLD+1 of the IdP.

The well-known file can include the following properties:

Property Required Description
provider_urls required Array of IdP configuration file paths. Ignored (but still required) if accounts_endpoint and login_url are specified.
accounts_endpoint recommended, requires login_url
URL for the accounts endpoint. This allows for multiple configuration support, as long as each config file uses the same login_url and accounts_endpoint URL.

Note: The parameter is supported from Chrome 132.
login_url recommended, requires accounts_endpoint The login page URL for the user to sign in to the IdP. This allows for multiple configuration support, as long as each config file uses the same login_url and accounts_endpoint.

Note: the parameter is supported starting in Chrome 132 and later.

For example, if the IdP endpoints are served under https://accounts.idp.example/, they must serve a well-known file at https://idp.example/.well-known/web-identity as well as an IdP config file. Here's an example well-known file content:

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

IdPs can accommodate multiple config files for an IdP, by specifying accounts_endpoint and login_url in the well-known file.
This feature can be useful in these cases:

  • An IdP needs to support multiple different test and production configurations.
  • An IdP needs to support different configurations per region (for example, eu-idp.example and us-idp.example).

To support multiple configurations (for example, to differentiate between test and production environment), IdP must specify accounts_endpoint and login_url:

  {
    // This property is required, but will be ignored when IdP supports
    // multiple configs (when `accounts_endpoint` and `login_url` are
    // specified), as long as `accounts_endpoint` and `login_url` in
    // that config file match those in the well-known file.
    "provider_urls": [ "https://idp.example/fedcm.json" ],

    // Specify accounts_endpoint and login_url properties to support
    // multiple config files.
    // Note: The accounts_endpoint and login_url must be identical
    // across all config files. Otherwise,
    // the configurations won't be supported.
    "accounts_endpoint": "https://idp.example/accounts",
    "login_url": "https://idp.example/login"
  }

Create an IdP config file and endpoints

The IdP config file provides a list of required endpoints for the browser. IdPs must host one or multiple config files, and the required endpoints and URLs. All JSON responses must be served with application/json content-type.

The config file's URL is determined by the values provided to the navigator.credentials.get() call executed on an RP.

  const credential = await navigator.credentials.get({
    identity: {
      context: 'signup',
      providers: [{
        configURL: 'https://accounts.idp.example/config.json',
        clientId: '********',
        nonce: '******'
      }]
    }
  });
  const { token } = credential;

RP will pass the config file's URL to the FedCM API call to let the user sign in:

  // Executed on RP's side:
  const credential = await navigator.credentials.get({
    identity: {
      context: 'signup',
      providers: [{
        // To allow users to sign in with an IdP using FedCM, RP specifies the IdP's config file URL:
        configURL: 'https://accounts.idp.example/fedcm.json',
        clientId: '********',
  });
  const { token } = credential;

The browser will fetch the config file with a GET request without the Origin header or the Referer header. The request doesn't have cookies and doesn't follow redirects. This effectively prevents the IdP from learning who made the request and which RP is attempting to connect. For example:

  GET /config.json HTTP/1.1
  Host: accounts.idp.example
  Accept: application/json
  Sec-Fetch-Dest: webidentity

IdP must implement a config endpoint that responds with a JSON. The JSON includes the following properties:

Property Description
accounts_endpoint (required) URL for the accounts endpoint.
accounts.include (optional) Custom account label string, determining which accounts should be returned when this config file is used, for example: "accounts": {"include": "developer"}.
An IdP can implement custom account labelling as follows:

For example, an IdP implements "https://idp.example/developer-config.json" config file with "accounts": {"include": "developer"} specified. The IdP also marks some accounts with "developer" label using the labels parameter in the accounts endpoint. When an RP calls navigator.credentials.get() with "https://idp.example/developer-config.json" config file specified, only accounts with the "developer" label will be returned.
client_metadata_endpoint (optional) URL for the client metadata endpoint.
id_assertion_endpoint (required) URL for the ID assertion endpoint.
disconnect (optional) URL for the disconnect endpoint.
login_url (required) The login page URL for the user to sign in to the IdP.
branding (optional) Object which contains various branding options.
branding.background_color (optional) Branding option which sets the background color of the "Continue as..." button. Use the relevant CSS syntax, namely hex-color, hsl(), rgb(), or named-color.
branding.color (optional) Branding option which sets the text color of the "Continue as..." button. Use the relevant CSS syntax, namely hex-color, hsl(), rgb(), or named-color.
branding.icons (optional) Array of icon objects. These icons are displayed in the sign-in dialog. The icon object has two parameters:
  • url (required): URL of the icon image. This does not support SVG images.
  • size (optional): Icon dimensions, assumed by the application to be square and single resolution. This number must be greater than or equal to 25px in passive mode and greater than or equal to 40px in active mode.
modes Object that contains specifications on how FedCM UI will be displayed in different modes:
  • active
  • passive
modes.active Object containing properties that allow for customization of FedCM behavior in a specific mode. Both modes.active and modes.passive can contain the following parameter:
  • supports_use_other_account: boolean specifying whether the user can sign in with an account different from the one they're currently logged in with (if IdP supports multiple accounts).

Note: Use Other Account feature and active mode are supported from Chrome 132.
modes.passive

Here's an example response body from the IdP:

  {
    "accounts_endpoint": "/accounts.example",
    "client_metadata_endpoint": "/client_metadata.example",
    "id_assertion_endpoint": "/assertion.example",
    "disconnect_endpoint": "/disconnect.example",
    "login_url": "/login",
    // When RPs use this config file, only those accounts will be
    //returned that include `developer` label in the accounts endpoint.
    "accounts": {"include": "developer"},
    "modes": {
        "active": {
          "supports_use_other_account": true,
        }
    },
    "branding": {
      "background_color": "green",
      "color": "#FFEEAA",
      "icons": [{
        "url": "https://idp.example/icon.ico",
        "size": 25
      }]
    }
  }

Once the browser fetches the config file, it sends subsequent requests to the IdP endpoints:

IdP endpoints
IdP endpoints

Use other account

Users can switch to an account that's different from the one they're currently logged in with, if the IdP supports multiple accounts or replacing the existing account.

To enable the user to choose other accounts, the IdP must specify this feature in the config file:

  {
    "accounts_endpoint" : "/accounts.example",
    "modes": {
      "active": {
        // Allow the user to choose other account (false by default)
        "supports_use_other_account": true
      }
      // "passive" mode can be configured separately
    }
  }

Accounts endpoint

The IdP's accounts endpoint returns a list of accounts that the user is signed in on the IdP. If the IdP supports multiple accounts, this endpoint will return all signed in accounts.

The browser sends a GET request with cookies with SameSite=None, but without a client_id parameter, the Origin header or the Referer header. This effectively prevents the IdP from learning which RP the user is trying to sign in to. For example:

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

Upon receiving the request, the server should:

  1. Verify that the request contains a Sec-Fetch-Dest: webidentity HTTP header.
  2. Match the session cookies with the IDs of the already signed-in accounts.
  3. Respond with the list of accounts.

The browser expects a JSON response that includes an accounts property with an array of account information with following properties:

Property Description
id (required) Unique ID of the user.
name (required) Given and family name of the user.
email (required) Email address of the user.
given_name (optional) Given name of the user.
picture (optional) URL of the user avatar image.
approved_clients (optional) An array of RP client IDs which the user has registered with.
login_hints (optional) An array of all possible filter types that the IdP supports to specify an account. The RP can invoke navigator.credentials.get() with the loginHint property to selectively show the specified account.
domain_hints (optional) An array of all the domains the account is associated with. The RP can call navigator.credentials.get() with a domainHint property to filter the accounts.
labels (optional) Array of string Custom Account Labels that an account is associated with.
An IdP can implement custom account labelling as follows:
  • Specify account labels in the accounts endpoint (using this labels parameter).
  • Create a config file for each specific label.

For example, an IdP implements https://idp.example/developer-config.json config file with "accounts": {"include": "developer"} specified. The IdP also marks some accounts with "developer" label using the labels parameter in the accounts endpoint. When an RP calls navigator.credentials.get() with https://idp.example/developer-config.json config file specified, only accounts with the "developer" label will be returned.

Custom Account Labels are different from Login hint and domain hint in such a way that they are fully maintained by the IdP server, and the RP only specifies the config file to use.

Example response body:

  {
    "accounts": [{
      "id": "1234",
      "given_name": "John",
      "name": "John Doe",
      "email": "john_doe@idp.example",
      "picture": "https://idp.example/profile/123",
      // Ids of those RPs where this account can be used
      "approved_clients": ["123", "456", "789"],
      // This account has 'login_hints`. When an RP calls `navigator.credentials.get()`
      // with a `loginHint` value specified, for example, `exampleHint`, only those
      // accounts will be shown to the user whose 'login_hints' array contains the `exampleHint`.
      "login_hints": ["demo1", "exampleHint"],
      // This account is labelled. IdP can implement a specific config file for a
      // label, for example, `https://idp.example/developer-config.json`. Like that
      // RPs can filter out accounts by calling `navigator.credentials.get()` with
      // `https://idp.example/developer-config.json` config file.
      "labels": ["hr", "developer"]
    }, {
      "id": "5678",
      "given_name": "Johnny",
      "name": "Johnny",
      "email": "johnny@idp.example",
      "picture": "https://idp.example/profile/456",
      "approved_clients": ["abc", "def", "ghi"],
      "login_hints": ["demo2"],
      "domain_hints": ["@domain.example"]
    }]
  }

If the user is not signed in, respond with HTTP 401 (Unauthorized).

The returned accounts list is consumed by the browser and won't be available to the RP.

ID assertion endpoint

The IdP's ID assertion endpoint returns an assertion for their signed-in user. When the user signs in to an RP website using navigator.credentials.get() call, the browser sends a POST request with cookies with SameSite=None and a content-type of application/x-www-form-urlencoded to this endpoint with the following information:

Property Description
client_id (required) The RP's client identifier.
account_id (required) The unique ID of the signing in user.
disclosure_text_shown Results in a string of "true" or "false" (rather than a boolean). The result is "false" in these cases:
  • If the disclosure text was not shown because the RP's client ID was included in the approved_clients property list of the response from the accounts endpoint.
  • If the disclosure text was not shown because the browser has observed a sign-up moment in the past in the absence of approved_clients.
  • If the fields parameter does not include one or more of the three fields ("name", "email" and "picture"), for example, fields=[ ], or fields=['name', 'picture']. This is needed for backward compatibility with older IdP implementations that expect a disclosure string to always include all three fields.
is_auto_selected If auto-reauthentication is performed on the RP, is_auto_selected indicates "true". Otherwise "false". This is helpful to support more security related features. For example, some users may prefer a higher security tier which requires explicit user mediation in authentication. If an IdP receives a token request without such mediation, they could handle the request differently. For example, return an error code such that the RP can call the FedCM API again with mediation: required.
fields (optional) Array of strings that specifies the user information ("name", " email", "picture") that RP needs IdP to share with them.
The browser will send fields, disclosure_text_shown, and disclosure_shown_for listing the specified fields in the POST request, as in the following example.

Note: The Fields parameter is supported from Chrome 132.
params (optional) Any valid JSON object that allows to specify additional custom key-value parameters, for example:
  • scope: A string value containing additional permissions that RP needs to request, for example "drive.readonly calendar.readonly"
  • nonce: A random string provided by the RP to ensure the response is issued for this specific request. Prevents replay attacks.
  • Other custom key-value parameters.
When the browser sends a POST request, the params value will be serialized to JSON and then percent-encoded.

Note: Parameters API is supported by Chrome 132 and later.

Example HTTP header:

  POST /assertion.example HTTP/1.1
  Host: accounts.idp.example
  Origin: https://rp.example/
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x23223
  Sec-Fetch-Dest: webidentity

  // disclosure_text_shown is set to 'false', as the 'name' field value is missing in 'fields' array
  // params value is serialized to JSON and then percent-encoded.
  account_id=123&client_id=client1234&disclosure_text_shown=false&is_auto_selected=true&params=%22%7B%5C%22nonce%5C%22%3A%5C%22nonce-value%5C%22%7D%22.%0D%0A4&disclosure_text_shown=true&fields=email,picture&disclosure_shown_for=email,picture

Upon receiving the request, the server should:

  1. Respond to the request with CORS (Cross-Origin Resource Sharing).
  2. Verify that the request contains a Sec-Fetch-Dest: webidentity HTTP header.
  3. Match the Origin header against the RP origin determined by the client_id. Reject if they don't match.
  4. Match account_id against the ID of the already signed-in account. Reject if they don't match.
  5. Respond with a token. If the request is rejected, respond with an error response.

IdP can decide how they issue the token. In general, it's signed with information such as the account ID, client ID, issuer origin, and nonce, so that the RP can verify the token is genuine.

The browser expects a JSON response that includes the following property:

Property Description
token A token is a string that contains claims about the authentication.
continue_on Redirect URL that enables a multi-step sign-in flow.

The returned token is passed to the RP by the browser, so that the RP can validate the authentication.

  {
    // IdP can respond with a token to authenticate the user
    "token": "***********"
  }
Continue on feature

The IdP can provide a redirect URL in the ID assertion endpoint response to enable a multi-step sign-in flow. This is useful when the IdP needs to request additional information or permissions, for example:

  • Permission to access the user's server-side resources.
  • Verification that contact information is up-to-date.
  • Parental controls.

The ID assertion endpoint can return a continue_on property which includes an absolute or relative path to the ID assertion endpoint.

  {
    // In the id_assertion_endpoint, instead of returning a typical
    // "token" response, the IdP decides that it needs the user to
    // continue on a popup window:
    "continue_on": "https://idp.example/continue_on_url"
  }

If the response contains the continue_on parameter, a new popup window is opened and navigates the user to the specified path. After the user interaction with the continue_on page, the IdP should call IdentityProvider.resolve() with the token passed as an argument so that the promise from the original navigator.credentials.get() call can be resolved:

  document.getElementById('example-button').addEventListener('click', async () => {
    let accessToken = await fetch('/generate_access_token.cgi');
    // Closes the window and resolves the promise (that is still hanging
    // in the relying party's renderer) with the value that is passed.
    IdentityProvider.resolve(accessToken);
  });

The browser will then automatically close the popup and return the token to the API caller. A one-time IdentityProvider.resolve() call is the only way for the parent window (RP) and the popup window (IdP) to communicate.
If the user rejects the request, IdP can close the window by calling IdentityProvider.close().

  IdentityProvider.close();

The Continuation API requires explicit user interaction (clicks) to function. Here's how Continuation API works with different mediation modes:

  • In passive mode:
    • mediation: 'optional' (default): the Continuation API will only work with a user gesture, such as clicking on a button on the page or on the FedCM UI. When auto re-authentication is triggered without a user gesture, no popup window is opened, and the promise is rejected.
    • mediation: 'required': Always asks the user to interact, so the Continuation API always works.
  • In active mode:
    • User activation is always required. The Continuation API is compatible.

If for some reason the user has changed their account in the popup (for example, the IdP offers a "use other account" function, or in delegation cases), the resolve call takes an optional second argument allowing something like:

  IdentityProvider.resolve(token, {accountId: '1234');
Return an error response

The id_assertion_endpoint can also return an "error" response, which has two optional fields:

  • code: The IdP can choose one of the known errors from the OAuth 2.0 specified error list (invalid_request, unauthorized_client, access_denied, server_error and temporarily_unavailable) or use any arbitrary string. If the latter, Chrome renders the error UI with a generic error message and passes the code to the RP.
  • url: It identifies a human-readable web page with information about the error to provide additional information about the error to users. This field is useful to users because browsers cannot provide rich error messages in a built-in UI. For example: links for next steps, or customer service contact information. If a user wants to learn more about the error details and how to fix it, they could visit the provided page from the browser UI for more details. The URL must be of the same-site as the IdP configURL.
  // id_assertion_endpoint response
  {
    "error" : {
      "code": "access_denied",
      "url" : "https://idp.example/error?type=access_denied"
    }
  }

Custom Account Labels

With Custom Account Labels, the IdP can annotate user accounts with labels, and the RP can choose to only fetch accounts with specific labels by specifying the configURL for that specific label. This can be useful when an RP needs to filter out accounts by specific criteria, for example, to only display role-specific accounts such as "developer" or "hr".

Similar filtering is possible using the Domain Hint and the Login Hint features, by specifying them in the navigator.credentials.get() call. However, Custom Account Labels can filter users by specifying the config file, which is especially useful when multiple configURLs are used. Custom Account Labels are also different in that they are provided from the IdP server, as opposed to from the RP, like login or domain hints.

Consider an IdP that wants to differentiate between "developer" and "hr" accounts. To achieve this, the IdP needs to support two configURLs for "developer" and "hr" respectively:

  • The developer config file https://idp.example/developer/fedcm.json has a "developer" label, and the enterprise config file https://idp.example/hr/fedcm.json has an "hr" label as follows:
  // The developer config file at `https://idp.example/developer/fedcm.json`
  {
    "accounts_endpoint": "https://idp.example/accounts",
    "client_metadata_endpoint": "/client_metadata",
    "login_url": "https://idp.example/login",
    "id_assertion_endpoint": "/assertion",
    "accounts": {
      // Account label
      "include": "developer"
    }
  }
  // The hr config file at `https://idp.example/hr/fedcm.json`
  {
    "accounts_endpoint": "https://idp.example/accounts",
    "client_metadata_endpoint": "/client_metadata",
    "login_url": "https://idp.example/login",
    "id_assertion_endpoint": "/assertion",
    "accounts": {
      // Account label
      "include": "hr"
    }
  }
  • With such a setup, the well-known file should include accounts_endpoint and login_url to allow multiple configURLs:
  {
    "provider_urls": [ "https://idp.example/fedcm.json" ],
    "accounts_endpoint": "https://idp.example/accounts",
    "login_url": "https://idp.example/login"
  }
  • The common IdP accounts endpoint (in this example https://idp.example/accounts) returns a list of accounts that includes a labels property with assigned labels in an array for each account:
  {
  "accounts": [{
    "id": "123",
    "given_name": "John",
    "name": "John Doe",
    "email": "john_doe@idp.example",
    "picture": "https://idp.example/profile/123",
    "labels": ["developer"]
    }], [{
    "id": "4567",
    "given_name": "Jane",
    "name": "Jane Doe",
    "email": "jane_doe@idp.example",
    "picture": "https://idp.example/profile/4567",
    "labels": ["hr"]
    }]
  }

When an RP wants to allow "hr" users to sign in, they can specify the configURL https://idp.example/hr/fedcm.json in the navigator.credentials.get() call:

  let { token } = await navigator.credentials.get({
    identity: {
      providers: [{
        clientId: '1234',
        nonce: '234234',
        configURL: 'https://idp.example/hr/fedcm.json',
      },
    }
  });

As a result, only the account ID of 4567 is available for the user to sign in. The account ID of 123 is silently hidden by the browser so that the user won't be provided with an account that's not supported by the IdP on this site.

  • Labels are strings. If the labels array or include field contains something that is not a string, it is ignored.
  • If no labels are specified in the configURL, all accounts will be displayed in the FedCM account chooser.
  • If no labels are specified for an account, it will only be displayed in the account chooser if the configURL also does not specify a label.
  • If no account matches the requested label in passive mode (similar to the Domain Hint feature), the FedCM dialog shows a login prompt, which allows the user to sign in to an IdP account. For the active mode, the login popup window is directly opened.

Disconnect endpoint

By invoking IdentityCredential.disconnect(), the browser sends a cross-origin POST request with cookies with SameSite=None and a content-type of application/x-www-form-urlencoded to this disconnect endpoint with the following information:

Property Description
account_hint A hint for the IdP account..
client_id The RP's client identifier.
  POST /disconnect.example HTTP/1.1
  Host: idp.example
  Origin: rp.example
  Content-Type: application/x-www-form-urlencoded
  Cookie: 0x123
  Sec-Fetch-Dest: webidentity

  account_hint=account456&client_id=rp123

Upon receiving the request, the server should:

  1. Respond to the request with CORS (Cross-Origin Resource Sharing).
  2. Verify that the request contains a Sec-Fetch-Dest: webidentity HTTP header.
  3. Match the Origin header against the RP origin determined by the client_id. Reject if they don't match.
  4. Match account_hint against the IDs of the already signed-in accounts.
  5. Disconnect the user account from the RP.
  6. Respond to the browser with the identified user account information in a JSON format.

An example response JSON payload looks like this:

  {
    "account_id": "account456"
  }

Instead, if the IdP wishes the browser to disconnect all accounts associated with the RP, pass a string that does not match any account ID, for example "*".

Client metadata endpoint

The IdP's client metadata endpoint returns the relying party's metadata such as the RP's privacy policy, terms of service, and logo icons. RPs should provide links to their privacy policy and terms of service to the IdP in advance. These links are displayed in the sign-in dialog when the user hasn't registered on the RP with the IdP yet.

The browser sends a GET request using the client_id navigator.credentials.get without cookies. For example:

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

Upon receiving the request, the server should:

  1. Determine the RP for the client_id.
  2. Respond with the client metadata.

The properties for the client metadata endpoint include:

Property Description
privacy_policy_url (optional) RP privacy policy URL.
terms_of_service_url (optional) RP terms of service URL.
icons (optional) Array of objects, such as [{ "url": "https://rp.example/rp-icon.ico", "size": 40}]

The browser expects a JSON response from the endpoint:

  {
    "privacy_policy_url": "https://rp.example/privacy_policy.html",
    "terms_of_service_url": "https://rp.example/terms_of_service.html",
    "icons": [{
          "url": "https://rp.example/rp-icon.ico",
          "size": 40
      }]
  }

The returned client metadata is consumed by the browser and won't be available to the RP.

Login URL

This endpoint is used to let the user sign in to the IdP.

With the Login Status API, the IdP must inform the user's login status to the browser. However, the status could be out of sync, such as when the session expires. In such a scenario, the browser can dynamically let the user sign in to the IdP through the login page URL specified with the idp config file's login_url.

The FedCM dialog displays a message suggesting a sign in, as shown in the following image.

A
A FedCM dialog suggesting to sign in to the IdP.

When the user clicks the Continue button, the browser opens a popup window for the IdP's login page.

An example FedCM dialog.
An example dialog shown after clicking on the sign in to the IdP button.

The dialog is a regular browser window that has first-party cookies. Whatever happens within the dialog is up to the IdP, and no window handles are available to make a cross-origin communication request to the RP page. After the user is signed in, the IdP should:

  • Send the Set-Login: logged-in header or call the navigator.login.setStatus("logged-in") API to inform the browser that the user has been signed in.
  • Call IdentityProvider.close() to close the dialog.
A user signs into an RP after signing in to the IdP using FedCM.

Inform the browser about the user's login status

The Login Status API is a mechanism where a website, especially an IdP, informs the browser the user's login status on the IdP. With this API, the browser can reduce unnecessary requests to the IdP and mitigate potential timing attacks.

IdPs can signal the user's login status to the browser by sending an HTTP header or by calling a JavaScript API when the user is signed in on the IdP or when the user is signed out from all their IdP accounts. For each IdP (identified by its config URL) the browser keeps a tri-state variable representing the login state with possible values:

  • logged-in
  • logged-out
  • unknown (default)
Login state Description
logged-in When the user's login status is set to logged-in, the RP calling FedCM makes requests to the IdP's accounts endpoint and displays available accounts to the user in the FedCM dialog.
logged-out When the user's login status is logged-out, calling the FedCM silently fails without making a request to the IdP's accounts endpoint.
unknown (default) The unknown status is set before the IdP sends a signal using the Login Status API. When the status is unknown, the browser makes a request to the IdP's accounts endpoint and updates the status based on the response from the accounts endpoint.

To signal that the user is signed in, send an Set-Login: logged-in HTTP header in a top-level navigation or a same-site subresource request at the IdP origin:

  Set-Login: logged-in

Alternatively, call the JavaScript method navigator.login.setStatus('logged-in') from the IdP origin in a top-level navigation:

  navigator.login.setStatus('logged-in')

The user's login status will be set as logged-in.

To signal that the user is signed out from all their accounts, send a Set-Login: logged-out HTTP header in a top-level navigation or a same-site subresource request at the IdP origin:

  Set-Login: logged-out

Alternatively, call the JavaScript API navigator.login.setStatus('logged-out') from the IdP origin in a top-level navigation:

  navigator.login.setStatus('logged-out')

The user's login status will be set as logged-out.

The unknown status is set before the IdP sends a signal using the Login Status API. The browser makes a request to the IdP's accounts endpoint and update the status based on the response from the accounts endpoint:

  • If the endpoint returns a list of active accounts, update the status to logged-in and open the FedCM dialog to show those accounts.
  • If the endpoint returns no accounts, update the status to logged-out and fail the FedCM call.

Let the user sign in through a dynamic login flow

Even though the IdP keeps informing the user's login status to the browser, it could be out of sync, such as when the session expires. The browser tries to send a credentialed request to the accounts endpoint when the login status is logged-in, but the server returns no accounts because the session is no longer available. In such a scenario, the browser can dynamically let the user sign in to the IdP through a popup window.

Implement FedCM as RP

Once the IdP's configuration and endpoints are available, RPs can call navigator.credentials.get() to request allowing users to sign in to the RP with the IdP.

Before calling the API, you need to confirm that FedCM is available on the user's browser. To check if FedCM is available, wrap this code around your FedCM implementation:

  if ('IdentityCredential' in window) {
    // If the feature is available, take action
  } else {
    // FedCM is not supported, use a different identity solution
  }

To allow users to sign in to the IdP on an RP using FedCM, the RP can call navigator.credentials.get(), for example:

  const credential = await navigator.credentials.get({
    identity: {
      context: 'signin',
      providers: [{
        configURL: 'https://accounts.idp.example/config.json',
        clientId: '********',
        mode: 'active',
        params: {
          nonce: '******'
        }
      }]
    }
  });
  const { token } = credential;

Context property

With the optional context property, RP can modify the string in the FedCM dialog UI (for example, "Sign in to rp.example…", "Use idp.example…") to accommodate predefined authentication contexts, for example. The context property can have the following values:

  • signin (default)
  • signup
  • use
Diagram explaining the UI components of the FedCM dialog: on the top left side, an icon is displayed. To the right of the icon is a context component displaying the message 'Sign in to RP with IdP'. At the bottom is a 'Continue' button with custom text and background color.
How branding is applied to the FedCM dialog

For example, setting context to use will result in the following message:

A FedCM dialog displaying a customized context message: instead of 'Sign in' with FedCM the context message says 'Use' FedCM.
FedCM dialog displaying a customized context message.

The browser handles sign-up and sign-in use cases differently depending on the existence of approved_clients in the response from the accounts list endpoint. The browser won't display a disclosure text "To continue with ...." if the user has already signed up to the RP.
The providers property takes an array of IdentityProvider objects that have the following properties:

Providers property

The providers property takes an array of IdentityProvider objects that have the following properties:

Property Description
configURL (required) A full path of the IdP config file.
clientId (required) The RP's client identifier, issued by the IdP.
nonce (optional) A random string to ensure the response is issued for this specific request. Prevents replay attacks.
loginHint (optional) By specifying one of login_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.
domainHint (optional) By specifying one of domain_hints values provided by the accounts endpoints, the FedCM dialog selectively shows the specified account.
mode (optional) String that specifies the UI mode of FedCM. It can be of one of these values:
  • "active": FedCM prompt must be initiated by user interaction (such as clicking a button).
  • "passive": FedCM prompt will be initiated without direct user interaction.
See the overview page to learn more about the difference between active and passive modes.

Note: mode parameter is supported from Chrome 132.
fields (optional) Array of strings that specifies the user information ("name", " email", "picture") that RP needs IdP to share with them.
Note: Field API is supported by Chrome 132 and later.
parameters (optional) Custom object that allows to specify additional key-value parameters:
  • scope: A string value containing additional permissions that RP needs to request, for example "drive.readonly calendar.readonly"
  • nonce: A random string to ensure the response is issued for this specific request. Prevents replay attacks.
  • Other custom key-value parameters.

Note: parameters is supported from Chrome 132.

Active mode

FedCM supports different UX mode configurations. Passive mode is the default mode, and developers don't need to configure it.

To use FedCM in active mode:

  1. Check the feature availability in the user's browser.
  2. Invoke the API with a transient user gesture, such as a button click.
  3. Pass the mode parameter to the API call:
  let supportsFedCmMode = false;
  try {
    navigator.credentials.get({
      identity: Object.defineProperty(
        // Check if this Chrome version supports the Mode API.
        {}, 'mode', {
          get: function () { supportsFedCmMode = true; }
        }
      )
    });
  } catch(e) {}

  if (supportsFedCmMode) {
    // The button mode is supported. Call the API with mode property:
    return await navigator.credentials.get({
      identity: {
        providers: [{
          configURL: 'https://idp.example/config.json',
          clientId: '123',
        }],
        // The 'mode' value defines the UX mode of FedCM.
        // - 'active': Must be initiated by user interaction (e.g., clicking a button).
        // - 'passive': Can be initiated without direct user interaction.
        mode: 'active'
      }
    });
  }

Custom icon in active mode

Active mode allows IdPs to include the RP's official logo icon directly in the client metadata endpoint response. The RP must provide their branding data in advance.

Call FedCM from within a cross-origin iframe

FedCM can be invoked from within a cross-origin iframe using an identity-credentials-get permissions policy, if the parent frame allows it. To do so, append the allow="identity-credentials-get" attribute to the iframe tag as follows:

  <iframe src="https://fedcm-cross-origin-iframe.glitch.me" allow="identity-credentials-get"></iframe>

You can see it in action in an example.

Optionally, if the parent frame wants to restrict the origins to call FedCM, send a Permissions-Policy header with a list of allowed origins.

  Permissions-Policy: identity-credentials-get=(self "https://fedcm-cross-origin-iframe.glitch.me")

You can learn more about how the Permissions Policy works at Controlling browser features with Permissions Policy.

Login Hint API

Using the Login Hint, the RP can recommend which account a user should sign in with. This can be helpful for re-authenticating users who're not sure which account they've used before.

RPs can selectively show a specific account by invoking navigator.credentials.get() with the loginHint property with one of login_hints values fetched from the accounts list endpoint, as shown in the following code sample:

  return await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/manifest.json',
        clientId: '123',
        // Accounts endpoint can specify a 'login_hints' array for an account.
        // When RP specifies a 'exampleHint' value, only those accounts will be
        // shown to the user whose 'login_hints' array contains the 'exampleHint'
        // value
        loginHint : 'exampleHint'
      }]
    }
  });

When no accounts match the loginHint, the FedCM dialog shows a login prompt, which allows the user to login to an IdP account matching the hint requested by the RP. When the user taps on the prompt, a popup window is opened with the login URL specified in the config file. The link is then appended with the login hint and the domain hint query parameters.

Domain Hint API

RPs can selectively show only accounts associated with a certain domain. This can be useful for the RPs that are restricted to a corporate domain.

To display specific domain accounts only, RP should call navigator.credentials.get() with the domainHint property with one of domain_hints values fetched from the accounts list endpoint, as shown in the following code sample:

  return await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/manifest.json',
        clientId: 'abc',
        // Accounts endpoint can specify a 'domain_hints' array for an account.
        // When RP specifies a '@domain.example' value, only those accounts will be
        // shown to the user whose 'domain_hints' array contains the
        // '@domain.example' value
        domainHint : '@domain.example'
      }]
    }
  });

When no accounts match the domainHint, the FedCM dialog shows a login prompt, which allows the user to login to an IdP account matching the hint requested by the RP. When the user taps on the prompt, a popup window is opened with the login URL specified in the config file. The link is then appended with the login hint and the domain hint query parameters.

An example login prompt when no accounts match the domainHint.
An example login prompt when no accounts match the domainHint.

Custom parameters

The Custom Parameters feature allows the RP to provide additional key-value parameters to the ID assertion endpoint. With the Parameters API, RPs can pass additional parameters to the IdP to request permissions for resources beyond basic sign-in. Passing additional parameters can be useful in these scenario:

  • The RP needs to request additional permissions dynamically that the IdP has, such as billing address or calendar access. The user can authorize these permissions through an IdP-controlled UX flow that is launched using the Continue on feature, and the IdP would then share this information.

To use the API, the RP adds parameters to the params property as an object in the navigator.credentials.get() call:

  let {token} = await navigator.credentials.get({
    identity: {
      providers: [{
        clientId: '1234',
        configURL: 'https://idp.example/fedcm.json',
        // Key/value pairs that need to be passed from the
        // RP to the IdP but that don't really play any role with
        // the browser.
        params: {
          IDP_SPECIFIC_PARAM: '1',
          foo: 'BAR'
        }
      },
    }
  });

The browser will automatically translate this into a POST request to the IdP with parameters as a single url-encoded JSON-serialized object:

  // The assertion endpoint is drawn from the config file
  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

  // params are translated into urlencoded version of `{"IDP_SPECIFIC_PARAM":"1","foo":"bar"}`
  account_id=123&client_id=client1234&params=%22%7B%5C%22IDP_SPECIFIC_PARAM%5C%22%3A1%2C%5C%22foo%5C%22%3A%5C%22BAR%5C%22%7D%22.

If the RP needs any additional permissions, the IdP can provide a redirect link. For example, in node.js:

  if (rpRequestsPermissions) {
    // Response with a URL if the RP requests additional permissions
    return res.json({
      continue_on: '/example-redirect',
    });
  }

Fields

The RP can specify the user information (any combination of name, email address, and profile picture) they need the IdP to share with them. The requested information will be included in the disclosure UI of the FedCM dialog. The user will see a message notifying them that idp.example will share the requested information with the rp.example if the user chooses to sign in.

A FedCM active mode dialog displaying a disclosure message. To continue, the identity provider will share the user's email address and profile picture with the website.
Disclosure message in active mode: the RP requests the IdP to share only the user email and profile picture.

To use the Fields feature, RP should add a fields array in the navigator.credentials.get() call. The fields can contain any permutation of name, email and picture. This can be expanded to include more values in the future. A request with fields would look like this:

  let { token } = await navigator.credentials.get({
    identity: {
      providers: [{
        // RP requests the IdP to share only user email and profile picture
        fields: [ 'email', 'picture'],
        clientId: '1234',
        configURL: 'https://idp.example/fedcm.json',

      },
    }
  });

The browser will automatically translate it into an HTTP request to the ID assertion endpoint that includes the RP-specified fields parameter, with the fields that the browser disclosed to the user in a disclosure_shown_for parameter. For backwards compatibility, the browser will also send disclosure_text_shown=true if the disclosure text was shown and the requested fields include all the three fields: 'name', 'email', and 'picture'.

  POST /id_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

  // The RP only requested to share email and picture. The browser will send `disclosure_text_shown=false`, as the 'name' field value is missing
  account_id=123&client_id=client1234&disclosure_text_shown=false&fields=email,picture&disclosure_shown_for=email,picture

If fields is an empty array, the user agent will skip the disclosure UI.

A FedCM passive mode dialog that does not display a disclosure UI message.
The disclosure message is not displayed in the passive mode. In the button flow, the disclosure UI is skipped entirely.

This is the case even if the response from the accounts endpoint does not contain a client ID that matches the RP in approved_clients.

In this case, the disclosure_text_shown sent to the ID assertion endpoint is false in the HTTP body:

  POST /id_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=234234&disclosure_text_shown=false

Show an error message

Sometimes, the IdP may not be able to issue a token for legitimate reasons, such as when the client is unauthorized, or the server is temporarily unavailable. If the IdP returns an "error" response, the RP can catch it, and Chrome can notify the user by showing browser UI with the error information provided by the IdP.

A
A FedCM dialog showing the error message after the user's sign-in attempt fails. The string is associated with the error type.
  try {
    const cred = await navigator.credentials.get({
      identity: {
        providers: [
          {
            configURL: 'https://idp.example/manifest.json',
            clientId: '1234',
          },
        ],
      }
    });
  } catch (e) {
    const code = e.code;
    const url = e.url;
  }

Auto-reauthenticate users after the initial authentication

FedCM auto-reauthentication ("auto-reauthn" in short) can let users reauthenticate automatically, when they come back after their initial authentication using FedCM. "The initial authentication" here means the user creates an account or signs into the RP's website by tapping on the "Continue as..." button on FedCM's sign-in dialog for the first time on the same browser instance.

While the explicit user experience makes sense before the user has created the federated account to prevent tracking (which is one of the main goals of FedCM), it is unnecessarily cumbersome after the user has gone through it once: after the user grants permission to allow communication between the RP and the IdP, there's no privacy or security benefit for enforcing another explicit user confirmation for something that they have already previously acknowledged.

With auto-reauthn, the browser changes its behavior depending on the option you specify for the mediation when calling navigator.credentials.get().

  const cred = await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://idp.example/fedcm.json',
        clientId: '1234',
      }],
    },
    mediation: 'optional', // this is the default
  });

  // `isAutoSelected` is `true` if auto-reauthn was performed.
  const isAutoSelected = cred.isAutoSelected;

The mediation is a property in the Credential Management API, it behaves in the same way as it does for PasswordCredential and FederatedCredential and it's partially supported by PublicKeyCredential as well. The property accepts the following four values:

  • 'optional'(default): Auto-reauthn if possible, requires a mediation if not. We recommend choosing this option on the sign-in page.
  • 'required': Always requires a mediation to proceed, for example, clicking the "Continue" button on the UI. Choose this option if your users are expected to grant permission explicitly every time they need to be authenticated.
  • 'silent': Auto-reauthn if possible, silently fail without requiring a mediation if not. We recommend choosing this option on the pages other than the dedicated sign-in page but where you want to keep users signed in—for example, an item page on a shipping website or an article page on a news website.
  • 'conditional': Used for WebAuthn and not available for FedCM at the moment.

With this call, auto-reauthn happens under the following conditions:

  • FedCM is available to use. For example, the user has not disabled FedCM either globally or for the RP in the settings.
  • The user used only one account with FedCM API to sign into the website on this browser.
  • The user is signed into the IdP with that account.
  • The auto-reauthn didn't happen within the last 10 minutes.
  • The RP hasn't called navigator.credentials.preventSilentAccess() after the previous sign in.

When these conditions are met, an attempt to automatically reauthenticate the user starts as soon as the FedCM navigator.credentials.get() is invoked.

When mediation: optional, auto-reauthn may be unavailable due to reasons that only the browser knows; the RP can check whether auto-reauthn is performed by examining the isAutoSelected property.

This is helpful to evaluate the API performance and improve UX accordingly. Also, when it's unavailable, the user may be prompted to sign in with explicit user mediation, which is a flow with mediation: required.

A user auto-reauthenticating through FedCM.

Enforce mediation with preventSilentAccess()

Auto-reauthenticating users immediately after they sign out wouldn't make for a very good user experience. That's why FedCM has a 10-minute quiet period after an auto-reauthn to prevent this behavior. This means that auto-reauthn happens at most once in every 10-minutes unless the user signs back in within 10-minutes. The RP should call navigator.credentials.preventSilentAccess() to explicitly request the browser to disable auto-reauthn when a user signs out of the RP explicitly, for example, by clicking a sign-out button.

  function signout() {
    navigator.credentials.preventSilentAccess();
    location.href = '/signout';
  }

Users can opt-out of auto-reauthn in settings

Users can opt-out from auto-reauth from the settings menu:

  • On desktop Chrome, go to chrome://password-manager/settings > Sign in automatically.
  • On Android Chrome, open Settings > Password Manager > Tap on a cog at the top right corner > Auto sign-in.

By disabling the toggle, the user can opt-out from auto-reauthn behavior all together. This setting is stored and synchronized across devices, if the user is signed into a Google Account on the Chrome instance and synchronization is enabled.

Disconnect the IdP from the RP

If a user has previously signed into the RP using the IdP through FedCM, the relationship is memorized by the browser locally as the list of connected accounts. The RP may initiate a disconnection by invoking the IdentityCredential.disconnect() function. This function can be called from a top-level RP frame. The RP needs to pass a configURL, the clientId it uses under the IdP, and an accountHint for the IdP to be disconnected. An account hint can be an arbitrary string as long as the disconnect endpoint can identify the account, for example an email address or user ID which does not necessarily match the account ID that the account list endpoint has provided:

  // Disconnect an IdP account 'account456' from the RP 'https://idp.com/'. This is invoked on the RP domain.
  IdentityCredential.disconnect({
    configURL: 'https://idp.com/config.json',
    clientId: 'rp123',
    accountHint: 'account456'
  });

IdentityCredential.disconnect() returns a Promise. This promise may throw an exception for the following reasons:

  • The user hasn't signed in to the RP using the IdP through FedCM.
  • The API is invoked from within an iframe without FedCM permissions policy.
  • The configURL is invalid or missing the disconnect endpoint.
  • Content Security Policy (CSP) check fails.
  • There is a pending disconnect request.
  • The user has disabled FedCM in the browser settings.

When the IdP's disconnect endpoint returns a response, the RP and the IdP are disconnected on the browser and the promise is resolved. The ID of the disconnected accounts are specified in the response from the disconnect endpoint.