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).
What it is
Section titled “What it is”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 newCredentialinstance based on the provided options — ornullif noCredentialobject can be created.get()resolves with theCredentialinstance that matches the provided parameters. If a single credential cannot be unambiguously obtained, it resolves withnull.store()stores a set of credentials for a user, inside a providedCredentialinstance, and returns that instance in aPromise.
The API supports four credential types, each a subclass of Credential, per MDN:
- Password —
PasswordCredential - Federated identity —
IdentityCredential(and the deprecatedFederatedCredential) - 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.
Where it is supported
Section titled “Where it is supported”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.
How to use it
Section titled “How to use it”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.
Choosing how much the user is involved
Section titled “Choosing how much the user is involved”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 withnull. 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.”
Giving the request a deadline
Section titled “Giving the request a deadline”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.
How to detect it at runtime
Section titled “How to detect it at runtime”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' });}Practical checklist
Section titled “Practical checklist”- 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 navigatoras proof that passwords work: BCD recordsPasswordCredentialas unsupported in Firefox and Safari, while both shipCredentialsContainer. Feature-test the credential type, not just the container. - Treat a
nullresolution as a normal outcome, not an error:get()resolves withnullwhen a single credential cannot be unambiguously obtained,mediation: 'silent'fulfills withnullwhen consent is required, andcreate()resolves withnullwhen noCredentialobject can be created. - MDN lists
FederatedCredentialas deprecated among the four credential types, and says thefederatedoption is superseded by theidentityoption 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()andpreventSilentAccess()at Chrome 60 against Chrome 51 for the container, andpreventSilentAccess()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 withNotSupportedError, so the check passes and the call fails anyway. Wrap the call, catchNotSupportedError, 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 aPublicKeyCredentialit generally has no effect, since such authenticators typically require user interaction. - If you are reading older code or older articles,
preventSilentAccess()was calledrequireUserMediation()in earlier versions of the spec. - Handle
get()’s rejections distinctly:NotAllowedErrorcovers the user cancelling the request, the call being blocked by theidentity-credentials-get,publickey-credentials-getorotp-credentialspermissions policies, and an opaque calling origin;SecurityErrormeans the calling domain is not a valid domain;AbortErrorandTimeoutErrorcome from thesignaloption. - For the
PublicKeyCredential(WebAuthn/passkey) type specifically, follow the dedicated WebAuthn reference below instead of treating it as a generic credential.
Where to go next
Section titled “Where to go next”- WebAuthn and passkeys — the
PublicKeyCredentialtype in depth, the one credential type this page defers. - Web capabilities index — the other browser capabilities a PWA can build on.
- Payment Request API — the kind of sensitive
operation MDN gives as its example for
mediation: "required". - Web app manifest id: a stable PWA identity — the identity of the installed app itself, as opposed to the user’s.