Federated Credential Management API developer guide

Learn how to use FedCM for privacy-preserving identity federation.

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

To learn more about FedCM use cases, user flows, and API roadmap check out the introduction to FedCM API.

FedCM development environment

You need a secure context (HTTPS or localhost) both on the IdP and RP in Chrome to use the FedCM.

Debug code on Chrome on Android

Set up and run a server locally to debug your FedCM code. You can access this server in Chrome on an Android device connected using a USB cable with port forwarding.

You can use DevTools on desktop to debug Chrome on Android by following the instructions at Remote debug Android devices.

Block third-party cookies on Chrome

Simulate third-party cookie phase-out by configuring Chrome to block them
Simulate third-party cookie phase-out by configuring Chrome to block them

You can test how FedCM works without third-party cookies on Chrome before it's actually enforced.

To block third-party cookies, use Incognito mode, or choose "Block third-party cookies" in your desktop settings at chrome://settings/cookies or on mobile by navigating to Settings > Site settings > Cookies.

Using the FedCM API

You integrate with FedCM by creating a well-known file, config file and endpoints for accounts list, assertion issuance and optionally client metadata.

From there, FedCM exposes JavaScript APIs that RPs can use to sign in with the IdP.

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.

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"]
}

The JSON file must contain the provider_urls property with an array of IdP config file URLs that can be specified as a path part of configURL in navigator.credentials.get by RPs. The number of URL strings in the array is limited to one, but this may change with your feedback in the future.

Create an IdP config file and endpoints

The IdP config file provides a list of required endpoints for the browser. IdPs will host this config file 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: '******',
      loginHint: 'demo1@example.com'
    }]
  }
});
const { token } = credential;

Specify a full URL of the IdP config file location as a configURL. When navigator.credentials.get() is called on the RP, the browser fetches 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

The browser expects a JSON response from the IdP which includes the following properties:

Property Description
accounts_endpoint (required) URL for the accounts list endpoint.
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) Branding option which sets the icon object, displayed in the sign-in dialog. The icon object is an array with 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 25.

RP could modify the string in the FedCM dialog UI via identity.context value for navigator.credentials.get() to accommodate predefined authentication contexts. Optional property can be one of "signin" (default), "signup", "use" or "continue".

How branding is applied to the FedCM dialog
How branding is applied to the FedCM dialog

Here's an example response body from the IdP:

{
  "accounts_endpoint": "/accounts.php",
  "client_metadata_endpoint": "/client_metadata.php",
  "id_assertion_endpoint": "/assertion.php",
  "disconnect_endpoint": "/disconnect.php",
  "login_url": "/login",
  "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

Accounts list endpoint

The IdP's accounts list endpoint returns a list of accounts that the user is currently 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, 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.php 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.

Example response body:

{
 "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"],
   "login_hints": ["demo1", "demo1@example.com"],
  }, {
   "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", "demo2@example.com"],
  }]
}

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

The returned accounts list is consumed by the browser and will not be available to the RP.

By passing login_hints in the accounts list, the RP can invoke navigator.credentials.get() with the loginHint property to selectively show the specified account.

Client metadata endpoint

The IdP's client metadata endpoint returns the relying party's metadata such as the RP's privacy policy and terms of service. 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.php?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.

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",
}

The returned client metadata is consumed by the browser and will not 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 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.
nonce (optional) The request nonce, provided by the RP.
disclosure_text_shown Results in a string of "true" or "false" (rather than a boolean). The result is "false" if the disclosure text was not shown. This happens when the RP's client ID was included in the approved_clients property list of the response from the accounts list endpoint or if the browser has observed a sign-up moment in the past in the absence of approved_clients.
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.

Example HTTP header:

POST /assertion.php HTTP/1.1
Host: accounts.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

Upon receiving the request, the server should:

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

How the token is issued is up to the IdP, but in general, it's signed with information such as the account ID, client ID, issuer origin, 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 (required) A token is a string that contains claims about the authentication.
{
  "token": "***********"
}

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

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 pass 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 native UI. For example, links for next steps, customer service contact information and so on. 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"
  }
}

Disconnect endpoint

By invoking IdentityCredential.disconnect(), the browser sends a cross-origin POST request with cookies 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.php 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 determine 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 "*".

Login URL

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
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
A user signs into an RP after signing in to the IdP using FedCM.

Inform the browser about the user's login status on the identity provider

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, and unknown. The default state is unknown.

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

Set-Login: logged-in

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

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

These calls record the user's login status as logged-in. When the user's login status is set to logged-in, the RP calling FedCM makes requests to the IdP's accounts list endpoint and displays available accounts to the user in the FedCM dialog.

To signal that the user is signed out from all their accounts, send Set-Login: logged-out HTTP header in a top-level navigation or a same-origin 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")

These calls record the user's login status as 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 list endpoint.

The unknown status is set before the IdP sends a signal using the Login Status API. Unknown was introduced for a better transition, because a user may have already signed into the IdP when this API was shipped. The IdP may not have a chance to signal this to the browser by the time FedCM is first invoked. In this case, Chrome makes a request to the IdP's accounts list endpoint and update the status based on the response from the accounts list 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 list 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.

Sign in to the relying party with the identity provider

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
}

To request allowing users to sign in to the IdP from the RP, do the following, for example:

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

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.

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 will not display a disclosure text "To continue with ...." if the user has already signed up to the RP.

The sign-up state is determined based on whether the following conditions are fulfilled or not:

  • If approved_clients includes the RP's clientId.
  • If the browser remembers that the user has already signed up to the RP.
A user signs into an RP using FedCM

When the RP calls navigator.credentials.get(), the following activities take place:

  1. The browser sends requests and fetches several documents:
    1. The well-known file and an IdP config file which declare endpoints.
    2. An accounts list.
    3. Optional: URLs for the RP's privacy policy and terms of service, retrieved from the client metadata endpoint.
  2. The browser displays the list of accounts that the user can use to sign-in, as well as the terms of service and privacy policy if available.
  3. Once the user chooses an account to sign in with, a request to the ID assertion endpoint is sent to the IdP to retrieve a token.
  4. The RP can validate the token to authenticate the user.
login API call
login API call

RPs are expected to support browsers which don't support FedCM, therefore users should be able to use an existing, non-FedCM sign-in process. Until third-party cookies are phased out completely, this should remain non-problematic.

Once the token is validated by the RP server, the RP may register the user or let them sign-in and start a new session.

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, the server is temporarily unavailable. If the IdP returns an "error" response, the RP can catch it, as well as Chrome notifies the user by showing a 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 consent

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 metidion: required.

A user auto-reauthenticating through FedCM.

Enforce mediation with preventSilentAccess()

Auto-reauthenticating users immediately after they sign out would not 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.

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.