Skip to content

Credential Management API

Published Updated

In one line: per MDN, the Credential Management API “enables a website to create, store, and retrieve credentials” through the CredentialsContainer interface, exposed on navigator.credentials, and is available only in secure contexts (HTTPS).

MDN describes a credential as “an item which enables a system to make an authentication decision” — evidence a user presents to prove they are who they claim to be. The central interface, CredentialsContainer, provides three main functions: create() (create a new credential), store() (store a new credential locally), and get() (retrieve a credential to log a user in).

The CredentialsContainer reference lists a fourth instance method alongside those three, and it is the one most easily missed: preventSilentAccess() “sets a flag that specifies whether automatic log in is allowed for future visits to the current origin, then returns an empty Promise”. All four methods are secure-context only.

Read the three main methods by what they resolve to, because the shapes differ:

  • create() resolves with a new Credential instance based on the provided options — or null if no Credential object can be created.
  • get() resolves with the Credential instance that matches the provided parameters. If a single credential cannot be unambiguously obtained, it resolves with null.
  • store() stores a set of credentials for a user, inside a provided Credential instance, and returns that instance in a Promise.

The API supports four credential types, each a subclass of Credential, per MDN:

  • Password — PasswordCredential
  • Federated identity — IdentityCredential (and the deprecated FederatedCredential)
  • One-time password (OTP) — OTPCredential
  • Web Authentication — PublicKeyCredential

This page covers the shared CredentialsContainer surface only; for the WebAuthn/passkey credential type specifically, see the dedicated WebAuthn and passkeys reference.

Per MDN’s browser-compat-data for CredentialsContainer, the interface itself is supported starting Chrome 51, Edge 18, Firefox 60, and Safari 13. Individual methods can land later than the interface itself, so check the specific method you need against the linked compat data rather than assuming the whole surface ships together. BCD records, for the same interface:

Method Chrome Edge Firefox Safari
CredentialsContainer 51 18 60 13
get() 51 18 60 13
store() 51 mirror 60 13
create() 60 18 60 13
preventSilentAccess() 60 18 60 17 (13 partial)

store()’s Edge cell reads mirror because that is literally what BCD records there: the other three methods carry an explicit Edge entry of 18, while store() has no Edge figure of its own and mirrors the corresponding Chromium data instead.

preventSilentAccess()’s Safari cell is the one that bites, because the failure mode is not “missing”. BCD carries two Safari entries for that method: full support from 17, and a partial_implementation range covering Safari 13 through 16 whose note reads “this method exists, but always rejected with a NotSupportedError exception.” So on those versions a plain typeof navigator.credentials.preventSilentAccess === 'function' check passes and the call still fails — the method is present and permanently broken. Detect it by catching NotSupportedError from the call, not by probing for the property.

The gap that most often breaks a real implementation is one level down, in the credential type rather than the container. MDN marks PasswordCredential as limited availability: “this feature is not Baseline because it does not work in some of the most widely-used browsers.” BCD is specific about which — it records PasswordCredential from Chrome 51, and records it as not supported in Firefox and in Safari, each with an open implementation bug rather than a version number. So 'credentials' in navigator being true tells you nothing about whether you can construct a PasswordCredential: those two engines ship the container without that type.

Three moments matter: storing after a successful sign-in, retrieving on a later visit, and clearing the automatic-login flag on sign-out.

// 1. Store a password credential after a successful sign-in.
async function rememberPasswordCredential({ id, password, name, iconURL }) {
if (!('PasswordCredential' in window)) return;
const credential = new PasswordCredential({ id, password, name, iconURL });
await navigator.credentials.store(credential);
}

PasswordCredential exposes password, name (a human-readable string that provides a public name for display in a credential chooser) and iconURL (a URL pointing to an image for an icon) as read-only properties, alongside the id and type it inherits from Credential.

// 2. Try to sign the user in silently when the page loads.
const credential = await navigator.credentials.get({
password: true,
mediation: 'silent',
});
if (credential) {
await signInWith(credential);
} else {
// No credential could be handed over without asking — show the Login button
// instead of a dialog the user did not ask for.
showLoginButton();
}
// 3. On sign-out, stop the browser signing them straight back in.
async function signOut() {
await serverSignOut();
if (!navigator.credentials?.preventSilentAccess) return;
try {
await navigator.credentials.preventSilentAccess();
} catch (err) {
// Safari 13–16 ship the method but always reject with NotSupportedError.
// The property check above cannot see that, so absorb this one rejection
// and fall back to clearing your own session state instead.
if (err.name !== 'NotSupportedError') throw err;
forgetLocalSession();
}
}

The try/catch handles Safari 13–16’s NotSupportedError, because the existence check cannot distinguish a working method from BCD’s partial implementation. Note that only NotSupportedError is swallowed — anything else still propagates, so a real failure is not hidden.

MDN gives exactly that reason for the call: “you might call this, after a user signs out of a website to ensure that they aren’t automatically signed in on the next site visit.” It resolves to undefined.

get() takes a mediation option — a string indicating how the user is involved in retrieving the credential. The default is "optional". The four values, per MDN:

  • "silent" — the user will not be asked to authenticate; the user agent will automatically reauthenticate and log them in if possible, and if consent is required the promise fulfills with null. Intended for signing a user in automatically on arrival if possible, but without presenting a confusing login dialog box if not.
  • "optional" — if credentials can be handed over without user mediation, they will be, enabling automatic reauthentication; if user mediation is required, the user agent will ask the user to authenticate. Intended for situations where you have reasonable confidence the user won’t be surprised to see a login dialog — for example after they click “Login/Signup”.
  • "required" — the user will always be asked to authenticate. Intended for forcing authentication, such as reauthenticating for a sensitive operation (MDN’s example: confirming a credit card payment) or when switching users.
  • "conditional" — discovered credentials are presented to the user in a non-modal dialog box along with an indication of the origin requesting them. In practice this means autofilling available credentials.

The other get() options worth knowing: password is the boolean that asks the browser for a stored password as a PasswordCredential; federated takes { protocols, providers } — but MDN notes FederatedCredential “is now superseded, and developers should prefer to use the identity option, if it is available.”

get() accepts a signal — an AbortSignal that lets an ongoing request be aborted. An aborted operation may complete normally (generally if the abort arrived after the operation had finished) or reject with the signal’s reason, which is an AbortError DOMException by default. Pairing it with AbortSignal.timeout() turns a prompt nobody answers into a catchable error:

try {
const credential = await navigator.credentials.get({
password: true,
signal: AbortSignal.timeout(10_000),
});
// …
} catch (err) {
if (err.name === 'TimeoutError') return showLoginButton();
if (err.name === 'AbortError') return; // the request was cancelled
throw err;
}

A request automatically aborted due to a timeout set with AbortSignal.timeout() rejects with TimeoutError, not AbortError — branch on both if you use a deadline.

Feature-test navigator.credentials before calling into it, since the API is undefined in browsers or contexts (like non-secure origins) that don’t expose it. Because the container can be present while the credential type you need is not, test both:

async function getStoredCredential() {
if (!('credentials' in navigator) || !('PasswordCredential' in window)) {
// Either the Credential Management API isn't available here (unsupported
// browser or a non-secure context), or this engine ships the container
// without password credentials — fall back to a manual sign-in form.
return null;
}
return navigator.credentials.get({ password: true, mediation: 'silent' });
}
  • Per MDN, the API is available only in secure contexts (HTTPS) — confirm the page is served securely before relying on it.
  • Do not treat 'credentials' in navigator as proof that passwords work: BCD records PasswordCredential as unsupported in Firefox and Safari, while both ship CredentialsContainer. Feature-test the credential type, not just the container.
  • Treat a null resolution as a normal outcome, not an error: get() resolves with null when a single credential cannot be unambiguously obtained, mediation: 'silent' fulfills with null when consent is required, and create() resolves with null when no Credential object can be created.
  • MDN lists FederatedCredential as deprecated among the four credential types, and says the federated option is superseded by the identity option where that is available — check the current MDN status before building new flows against it.
  • Method-level support can lag the interface: BCD puts create() and preventSilentAccess() at Chrome 60 against Chrome 51 for the container, and preventSilentAccess() at Safari 17 against Safari 13. Confirm the method you need individually.
  • Do not feature-detect preventSilentAccess() by checking the property. BCD records Safari 13–16 as a partial implementation where the method exists but always rejects with NotSupportedError, so the check passes and the call fails anyway. Wrap the call, catch NotSupportedError, fall back to clearing your own session, and re-throw everything else.
  • Call preventSilentAccess() on sign-out, not on sign-in — it is the flag that stops the next visit logging the user straight back in. Note that with a PublicKeyCredential it generally has no effect, since such authenticators typically require user interaction.
  • If you are reading older code or older articles, preventSilentAccess() was called requireUserMediation() in earlier versions of the spec.
  • Handle get()’s rejections distinctly: NotAllowedError covers the user cancelling the request, the call being blocked by the identity-credentials-get, publickey-credentials-get or otp-credentials permissions policies, and an opaque calling origin; SecurityError means the calling domain is not a valid domain; AbortError and TimeoutError come from the signal option.
  • For the PublicKeyCredential (WebAuthn/passkey) type specifically, follow the dedicated WebAuthn reference below instead of treating it as a generic credential.