Friday, October 6, 2023
FedCM and BYOIDP
Posted by jan
Federated Credential Management
The Federated Credential Management API (or FedCM API) provides a standard mechanism for identity providers (IdPs) to make identity federation services available on the web in a privacy-preserving way, without the need for third-party cookies and redirects. This includes a JavaScript API that enables the use of federated authentication for activities such as signing in or signing up on a website.
An end-user using a web app with FedCM will not have to be redirected to their IdP in order to receive its access token, but can use a new native browser UI to log in. The need for <iframe>
, redirects, and third-party cookies to enable identity federation becomes obsolete, a win in user-privacy and user-experience.
Goal
FedCM currently supports logging in a user-agent when the IdP is known. This means the RP needs to define a list of supported IdPs that it then passes to the FedCM API. The goal for this demo is to provide a bring your own identity provider functionality, so that when querying FedCM with the get
call, it will find all registered (previously logged in to) IdPs and return the list to the user. This way the RP does not need to provide a list of IdPs it wants to support but can simply allow this wildcard approach.
Demo
Clone the demo repositories.
git clone git@github.com:Liquid-Surf/fedcm-rp-node.git
git clone git@github.com:Liquid-Surf/fedcm-idp-typescript.git
Follow the instructions to run the RP and IdP. For this demo it is needed to run multiple IdPs.
Mapping the IdPs in your host file allows the FedCM demo to run multiple IdPs on localhost.
sudo tee -a /etc/hosts > /dev/null <<EOT
127.0.0.1 idp-1.localhost
127.0.0.1 idp-2.localhost
127.0.0.1 idp-3.localhost
EOT
Now we want to inject our own version of the navigator.credentials.get
function that supports querying for registered IdPs. For this we use Tampermonkey (a browser extension should also work) and json-server
to keep track of registered IdPs.
npm install -g json-server
echo '{ "providers": { "urls": [] } }' >> db.json
json-server --watch db.json
The IdPs will post their hostname to this db.json
file when a user logs in.
This script will expose our updated get method and expose it on _navigator
. This will be called instead of the real get
in the RP and injected using Tampermonkey.
(async function () {
'use strict';
// Fetch the registered IdPs from the fake FedCM Store/JSON server
const response = await fetch("http://localhost:3000/providers");
const providerUrls = await res.json();
// Extract the URL data and create registered providers
const providersFromFakeFedCMStore = idpUrls.urls.map(url => ({
nonce: "not-a-nonce",
configURL: `http://${url}:8080/fedcm.json`,
clientId: "yourClientID"
}));
function getCredentials(identity) {
const providersFromRP = identity.providers;
const providers = identity.registered ? providersFromFakeFedCMStore.concat(providersFromRP) : providersFromRP;
return navigator.credentials.get({
identity: { providers }
});
}
window._navigator = {
credentials: { get: getCredentials }
};
})();
Flow
- Register and sign into the IdP on
idp-1.localhost:8080
- Register and sign into the IdP on
idp-2.localhost:8080
- Go to the RP at
localhost:7080
- Press Login
- This should prompt you now with the option to continue with both IdPs, even though the RP only knew about
idp-1
and queried the fake FedCM storage foridp-2
.