loke.dev
Header image for 3 Things I Wish I Knew Before Migrating to the Federated Credential Management (FedCM) API

3 Things I Wish I Knew Before Migrating to the Federated Credential Management (FedCM) API

Third-party cookie deprecation is breaking 'Sign in with Google' buttons everywhere, but the native FedCM replacement is far from a drop-in fix.

· 4 min read

If you’ve been relying on the standard Google Identity Services (GIS) library to pop a little iframe for "Sign in with Google," your world is about to get significantly more complicated. Chrome is actively sunsetting the third-party cookies that made those silent, cross-site identity handshakes possible. The replacement is the Federated Credential Management (FedCM) API, and while it's designed to protect privacy, it completely changes the relationship between your site, the Identity Provider (IdP), and the browser.

I recently spent a week migrating a production app to FedCM. Here are the three things that bit me the hardest—and how you can avoid the same headaches.

1. You No Longer Control the "When" and "Where" of the UI

With traditional OAuth or the old Google "One Tap" library, you had a fair amount of control over the UI. You could trigger a popup on a button click or embed a specific iframe. With FedCM, the browser is the gatekeeper. When you call the API, the browser takes over and renders a native prompt at the top-right (on desktop) or as a bottom sheet (on mobile).

You can’t CSS your way out of this. If the browser decides the user has dismissed the prompt too many times, it might quiet the request entirely.

Here is what a basic FedCM request looks like in JavaScript:

try {
  const credential = await navigator.credentials.get({
    identity: {
      providers: [{
        configURL: 'https://accounts.google.com/gsi/fedcm.json',
        clientId: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
        nonce: 'random_string_for_security',
      }]
    },
    // This 'mediation' flag is crucial. 'optional' is the default.
    mediation: 'required' 
  });

  if (credential) {
    // Send this token to your backend to verify
    const token = credential.token;
    await handleSignIn(token);
  }
} catch (e) {
  console.error("FedCM failed:", e.name, e.message);
}

The Gotcha: You can’t just call this whenever you feel like it. Browsers are becoming very strict about "User Activation." If you try to trigger a FedCM prompt without a direct user gesture (like a click), and it’s not a "re-authentication" scenario, it might just fail silently or throw a NotAllowedError.

2. The .well-known Configuration is Non-Negotiable

FedCM isn't just a client-side change. It's a handshake that requires the Identity Provider (Google, Facebook, etc.) to have a specific file structure that the browser can verify. But it's not just on them—if you are building your own IdP or using a wrapper, you need to understand the manifest.

The browser looks for a manifest file at the IdP’s origin. For example, if you're using Google, the browser hits https://accounts.google.com/.well-known/web-identity.

If you're testing your own implementation, your manifest (the configURL) must return a JSON structure like this:

{
  "accounts_endpoint": "/auth/accounts",
  "client_metadata_endpoint": "/auth/metadata",
  "id_assertion_endpoint": "/auth/id_assertion",
  "login_url": "/login",
  "branding": {
    "background_color": "#ffffff",
    "color": "#1a73e8"
  }
}

The Gotcha: I wasted four hours because I forgot that FedCM requests ignore redirects for security reasons. If your configURL redirects to a CDN or a versioned path, the browser will likely kill the request. Ensure your endpoints are direct and return the correct application/json content type. Also, the browser sends a specific header—Sec-Fetch-Dest: webidentity—which your server *must* check for to prevent CSRF attacks.

3. Auto-Re-authentication is a UX Dream and a Logic Nightmare

One of the "magic" features of FedCM is auto-re-authentication. If a user has already logged into your site using their Google account, FedCM can automatically log them back in on their next visit without them clicking a single button.

It sounds great, but it introduces a race condition. If your app starts loading its own session state while the browser is simultaneously trying to auto-log the user in via FedCM, you can end up with weird "double-login" flashes or overwritten session cookies.

To handle auto-re-auth properly, you need to check the isAutoSelected property:

const credential = await navigator.credentials.get({
  identity: {
    providers: [{
      configURL: 'https://accounts.google.com/gsi/fedcm.json',
      clientId: 'YOUR_CLIENT_ID',
    }]
  },
  mediation: 'optional' // Allows the browser to pick the user automatically
});

if (credential) {
  if (credential.isAutoSelected) {
     console.log("User was logged in silently. No UI was shown!");
  }
  // Proceed with backend verification
}

The Gotcha: If the user explicitly logs out of your app, you must prevent auto-re-authentication from immediately logging them back in (which would be a frustrating loop). You do this by calling navigator.credentials.preventSilentAccess().

async function userLogout() {
  // 1. Tell the browser to stop auto-logging in for this site
  await navigator.credentials.preventSilentAccess();
  
  // 2. Clear your own local session/cookies
  await myAuthService.logout();
  
  // 3. Redirect to home
  window.location.href = "/";
}

Summary for the Transition

FedCM isn't just "One Tap 2.0." It’s a security-first API that puts the browser in the driver's seat.

If you're migrating:
1. Test for `NotAllowedError`: Your UI needs to fall back to a "hard" login button if the browser blocks the FedCM prompt.
2. Audit your headers: Ensure your backend is ready for the Sec-Fetch-Dest: webidentity header.
3. Manage the logout state: Don't let preventSilentAccess() be an afterthought, or your users will get stuck in a login loop that feels like a bug.

It's a bit of a learning curve, but once it's running, it's actually a much smoother experience for the user—and significantly more private than the "cookie-leakage" world we're leaving behind.