Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thoughts on FedCM full vs FedCM lite #665

Open
cbiesinger opened this issue Oct 24, 2024 · 0 comments
Open

Thoughts on FedCM full vs FedCM lite #665

cbiesinger opened this issue Oct 24, 2024 · 0 comments

Comments

@cbiesinger
Copy link
Collaborator

@bvandersloot-mozilla @samuelgoto @ekovac

There is a perception that FedCM lite and FedCM full are different APIs and that therefore it would be preferable to only have one of them.

However, in my mind, it really is just one API working in two modes, one that queries endpoints when FedCM is called and one that pre-provides accounts, removing the requirement for an IDP to support new endpoints in favor of a JS-only API.

In other words, the difference is primarily on the IDP side.

I am proposing some changes to bring the two modes more in alignment with each other. The changes listed below have individual benefits and also make FedCM lite more obviously a different mode of FedCM rather than an entirely new API.

Make id_assertion_endpoint optional in FedCM full

Lightweight FedCM allows for third-party cookie access without a (further) prompt. There is also a proposal for FedCM “full” to allow the same.

Therefore, we could just allow not specifying a id_assertion_endpoint in the configURL and return an IdentityCredential to the caller immediately upon the user choosing an account. We could indicate this situation by setting token to null in the credential (see also), which is also what the FedCM lite proposal does.

Note that the current proposal for FedCM lite includes a token query URL, which will seamlessly integrate with the current API from an RP perspective.

Allow specifying terms of service and privacy policy in the n.c.get call

FedCM full currently uses the client metadata endpoint to provide the terms of service and privacy policy and RP icon URL. This choice was made because many IdPs already have this data and this way an IDP can update its SDK with minimal, if any, changes required from RPs, and because IdPs may prefer having the chance to validate the URLs that show up in the signin prompt.

However, for other situations, even with FedCM full, it can be useful if the RP specifies its terms of service and privacy policy URLs themselves. For example, this comes up with the IDP registration API.

We could allow specifying these URLs in the API call as proposed here, either in all cases, or all cases except for FedCM full when a client metadata endpoint is provided.

Who should win if both IdP and RP specify these URLs?

If these URLs are specified in the n.c.get() call but the IdP also has a clientMetadata endpoint that specifies these URLs, who should win? Should the IdP win because they may have done more verification on these URLs, or the RP who may have specified more up-to-date URLs in the function call?

This question should be addressed but this can be sorted out separately. We can start by having the IdP win to match current behavior.

effectiveQueryURL

effectiveQueryURL lets an IdP limit origins that can use FedCM with this IdP. See also some discussion here. This functionality does not currently exist in FedCM full and is somewhat orthogonal to the light vs full discussion. However, it would be easy to provide similar functionality in the clientMetadata endpoint of FedCM full.

Option 1: Rely purely on the already-provided origin (preferred option)

We already send the origin to the client metadata endpoint. We could add an optional boolean disallowed = false to the response that an IdP could set to true to fail requests from this origin.

Option 2: Provide allowed URLs or URL prefixes in the client metadata response

It is not clear if this level of granularity is needed but it would be easy to add an allowedPaths / allowedPathPrefixes field to the client metadata response to allow for this.

Relationship to the IdP registration API

Exact details are being discussed on Github, but FedCM lite and FedCM full can easily integrate into the registration API. The discussion is centering around whether this should happen with a separate call to IdentityProvider.register (optionally providing a type) or whether a type should be provided as part of storing the accounts. Either way allows showing “FedCM lite” IdPs in the same account chooser as “FedCM Full IdPs”.

Relationship to the Multi-IdP API

The array passed to n.c.get easily allows selecting FedCM lite providers by origin, or can be extended using FedCM lite providers with a matching effectiveQueryURL.

Note that because FedCM Lite does not require network requests, their accounts are available faster than FedCM full accounts. If a future change makes it so that the multi-IDP UI updates dynamically as accounts arrive, this could give FedCM lite providers a (potentially undeserved) advantage and should be considered if/when such a change is made. (This applies less if the user agent uses an IDP chooser before showing the accounts)

Active mode vs passive mode

User agents supporting passive mode will just reject the promise if no credentials were stored/IdPs registered in the FedCM lite case. Active mode is covered (assumed) in the explainer.

Switching between FedCM Full and FedCM lite

As currently specified, the user agent will use FedCM Full if a configURL is specified in the navigator.credentials.get call and FedCM lite otherwise (using origin or effectiveQueryURL).

This does not allow an IDP to change between modes without requiring RP changes. It is not clear if this will end up being a problem, but it could be solved fairly easily as follows:

Switch from FedCM Full to FedCM Lite

If an IDP already supports FedCM full but would like to switch to FedCM lite (perhaps because the avoided network fetch is worth the potentially outdated accounts), this currently requires changing all RPs to change how they call FedCM.

To avoid this, we can add a string account_source = push|pull to the config URL (where push is FedCM lite and pull is FedCM full), defaulting to pull. If the account source is push, the accounts endpoint and the client metadata endpoint will not be fetched, and if there is no stored credential the user agent will either fail the request (passive mode) or show the login popup (active mode).

Another option would be to allow omitting the accounts endpoint from the config file and have that imply push.

Switching from FedCM Lite to FedCM full

If an IDP wants to switch to the pull method for providing accounts (and implements the required endpoints and well-known file), we can let them do so as follows without requiring RP changes:

The IdP can store a configURL instead of the account details. If provided, the user agent will use FedCM full for this IdP with the stored configURL.

However, the next section provides a better way to handle both cases.

Unifying configURL and origin

There is not actually a need for configURL and origin to be different parameters. We can make the client side completely identical by just having a single url parameter with semantics as follows:

  • If there are stored accounts for the origin of url, use those accounts (FedCM lite)
  • Fetch the URL. If it is a valid config file, proceed with FedCM full.
  • Otherwise, proceed with opening the loginURL provided in the API call to let the user log in to the IDP (assuming active mode)
  • If no loginURL was provided or passive mode is used, return an error.
  • Alternatively, we can let IDPs provide (only) a login URL in this file to avoid the RP having to specify it.

This has the nice property that the RP code is identical no matter what mode the IdP uses. Additionally, this provides a path to switch from lite to full or back using the provided URL.

Conclusion

With the above proposals, the RP side is identical for FedCM lite vs full; the real differences are on the IdP side. Both modes allow returning a dynamic token to the RP, or allowing the RP to request third-party cookie access. They differ in whether the accounts are fetched by the user agent upon calling the FedCM API (a pull model) or stored by the IDP ahead of time (a push model), and there is value in supporting both ways.

Code snippet:

let token = await navigator.credentials.get({
  identity: {
    providers: [{
      url: "https://idp.example/fedcm"
    }],
    rp: {
      privacyPolicy: "https://rp.example/privacy",
      termsOfService: "https://rp.example/tos",
      icons: [{url: "https://rp.example/favicon.ico"}]
    }
  }
});

// Alternatively, relying on storage access instead of a tokenURL:
await navigator.credentials.get({
  identity: {
    providers: {
      configURL: "https://idp.example/fedcm"
    },
    clientData: {
      privacyPolicy: "https://rp.example/privacy",
      termsOfService: "https://rp.example/tos",
      icons: [{url: "https://rp.example/favicon.ico"}]
    }
  }
});
await navigator.requestStorageAccessFor("https://idp.example//");
let token = (await fetch("https://idp.example/login?rp=rp.example")).text();

Supporting both modes allows IDPs to evaluate the tradeoffs:

  • No new endpoints required for the push mode
  • Harder or impossible to update account details in the push model (name, picture) when a user change those, especially if that happens on another device
    ** Expiration times can help with not showing outdated details but don’t allow showing updated details
  • Personal user information gets persisted in the browser profile for the push mode
  • Push mode requires JavaScript calls whenever a user logs in or out and potentially when the user changes their account details; pull mode works more “passively”
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant