FedCm
Federated sign-in the browser mediates itself: it fetches the provider's config, shows the account chooser and returns a token - with no third-party cookies, no popup, and no redirect away from your page.
@inject Bit.Butil.FedCm fedCmMDN reference
/.well-known/web-identity and the config file named by ConfigUrl, with
accounts, client-metadata and assertion endpoints behind it, and it calls
SetLoginStatus as its own session changes - the browser will not even look for
accounts at a provider it believes is logged out. Against a provider that has done none of that,
Get returns null.
FedCmCredential.Token is a bearer credential, usually a signed JWT. Post it to your
backend and validate the signature, the issuer, the audience and the nonce you generated there.
Reading it in the browser proves nothing.
Whether the runtime exposes window.IdentityCredential, and whether navigator.login is there for a provider to report its session with. During prerender/SSR these return false rather than throwing, so defer them to OnAfterRenderAsync.
@inject Bit.Butil.FedCm fedCm
var supported = await fedCm.IsSupported();
var canReportLogin = await fedCm.IsLoginStatusSupported();Opens the browser's own account dialog for the given provider and resolves with the provider's token, or null when the user dismissed it or no account was found. The nonce belongs to your server: generate it there, and check it comes back in the token.
Bit.Butil.FedCm fedCm
HttpClient http
<button @onclick="SignIn">Sign in</button>
{
private async Task SignIn()
{
// The nonce belongs to the server: it is what proves the token came back from a sign-in
// this backend started, rather than being replayed from another one.
var nonce = await http.GetStringAsync("/api/auth/nonce");
var credential = await fedCm.Get(new FedCmOptions
{
Providers =
[
new FedCmProvider
{
ConfigUrl = "https://idp.example/fedcm.json",
ClientId = "your-client-id",
Nonce = nonce,
Fields = ["name", "email", "picture"]
}
],
Context = "signin",
Mediation = CredentialMediation.Optional
});
// null when the user dismissed the dialog, or no account was found.
if (credential is null) return;
// Reading the token in the browser proves nothing - the backend validates the signature,
// the issuer, the audience and the nonce it generated.
await http.PostAsJsonAsync("/api/auth/fedcm", new { credential.Token });
}
}{
"//": "Served by the identity provider at the root of its eTLD+1, not by your app. Without it the",
"//2": "browser refuses to fetch the config below, and Get returns null with nothing to debug.",
"provider_urls": ["https://idp.example/fedcm.json"]
}{
"//": "The config file ConfigUrl points at. Every URL here is same-origin with the config itself.",
"accounts_endpoint": "/fedcm/accounts",
"client_metadata_endpoint": "/fedcm/client-metadata",
"id_assertion_endpoint": "/fedcm/assertion",
"login_url": "/login",
"branding": {
"background_color": "#0f6cbd",
"color": "#ffffff",
"icons": [{ "url": "https://idp.example/icon.png", "size": 40 }]
}
}Severs the link between this relying party and one account at the provider, so the next sign-in needs fresh consent. The provider's disconnect endpoint does the work; the browser only forwards the request.
var disconnected = await fedCm.Disconnect(
configUrl: "https://idp.example/fedcm.json",
clientId: "your-client-id",
accountHint: "[email protected]");Only an identity provider's own pages call this, and they call it on every session change. Set LoggedIn after a sign-in and LoggedOut after a sign-out: the browser skips the accounts endpoint entirely for a provider it believes is logged out.
// On the identity provider's own origin:
await fedCm.SetLoginStatus(FedCmLoginStatus.LoggedIn);
// ...and on sign-out:
await fedCm.SetLoginStatus(FedCmLoginStatus.LoggedOut);API reference
ValueTask<bool> IsSupported()ValueTask<FedCmCredential?> Get(FedCmOptions options)ValueTask<bool> Disconnect(string configUrl, string clientId, string accountHint)ValueTask<bool> IsLoginStatusSupported()ValueTask<bool> SetLoginStatus(FedCmLoginStatus status)FedCmProvider[] Providers, string? Context, CredentialMediation Mediationstring ConfigUrl, string ClientId, string? Nonce, string? LoginHint, string? DomainHint, string[]? Fields, object? Parametersstring? Id, string Token, bool IsAutoSelected, string? ConfigUrlenum { LoggedIn, LoggedOut }