Credentials
The password and federated half of navigator.credentials: hand a sign-in to the browser's password manager, and get it back - silently, if you ask - on the next visit.
@inject Bit.Butil.Credentials credentialsMDN reference
navigator.credentials; this page is the password store.
PasswordCredential and FederatedCredential are implemented by Chromium.
Firefox and Safari expose navigator.credentials for passkeys but not for these, so
IsPasswordSupported - not IsSupported - is the check worth branching on.
Whether the container exists at all, and whether the two credential types this service stores are implemented. During prerender/SSR these return false rather than throwing, so defer them to OnAfterRenderAsync.
@inject Bit.Butil.Credentials credentials
var container = await credentials.IsSupported();
var passwords = await credentials.IsPasswordSupported();
var federated = await credentials.IsFederatedSupported();Offers a username/password pair to the password manager, which decides for itself whether to prompt. Call it after the server has confirmed the sign-in - storing a credential the server rejected teaches the password manager a wrong answer.
var stored = await credentials.StorePassword(
id: "[email protected]",
password: thePassword,
name: "Ada Lovelace",
iconUrl: "https://example.com/avatar.png");Records that this account signs in through an identity provider, so the account chooser can offer "continue with ..." next time. No secret is stored - just the account and the provider's origin.
var stored = await credentials.StoreFederated(
id: "[email protected]",
provider: "https://accounts.google.com",
name: "Ada Lovelace");Asks the browser for a stored credential. With Silent mediation nothing is shown at all - either a credential comes back and you can sign the user in, or nothing does and the visit continues undisturbed. Required always shows the chooser, which is what to use right after a sign-out.
var credential = await credentials.Get(new CredentialRequestOptions
{
Password = true,
FederatedProviders = ["https://accounts.google.com"],
Mediation = CredentialMediation.Silent
});
if (credential is { Type: "password" })
{
// POST credential.Id / credential.Password to the sign-in endpoint.
}Turns off silent access until the user next signs in deliberately. Without it, the next visit signs them straight back in - which is the bug behind "I clicked sign out and it logged me back in".
await credentials.PreventSilentAccess();API reference
ValueTask<bool> IsSupported()ValueTask<bool> IsPasswordSupported()ValueTask<bool> IsFederatedSupported()ValueTask<bool> StorePassword(string id, string password, string? name = null, string? iconUrl = null)ValueTask<bool> StoreFederated(string id, string provider, string? name = null, string? iconUrl = null, string? protocol = null)ValueTask<CredentialInfo?> Get(CredentialRequestOptions? options = null)ValueTask PreventSilentAccess()bool Password, string[]? FederatedProviders, string[]? FederatedProtocols, CredentialMediation Mediation (asking for neither throws ArgumentException)enum { Optional, Silent, Required, Conditional }string Type, string Id, string? Name, string? IconUrl, string? Password, string? Provider, string? Protocol