# Credential Management API

> navigator.credentials lets a site create, store, and retrieve sign-in credentials: the four credential types, the mediation modes, and the browser gaps.

**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

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/capabilities/webauthn-passkeys/) reference.

## 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

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

```js
// 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`.

```js
// 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();
}
```

```js
// 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

`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."

### 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:

```js
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

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:

```js
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

- 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.

## Where to go next

- [WebAuthn and passkeys](/reference/capabilities/webauthn-passkeys/) — the
  `PublicKeyCredential` type in depth, the one credential type this page defers.
- [Web capabilities index](/reference/capabilities/) — the other browser capabilities a PWA
  can build on.
- [Payment Request API](/reference/capabilities/payment-request/) — the kind of sensitive
  operation MDN gives as its example for `mediation: "required"`.
- [Web app manifest id: a stable PWA identity](/reference/manifest/id/) — the identity of the
  installed app itself, as opposed to the user's.