· deepdives  · 8 min read

Demystifying the Credential Management API: A Comprehensive Guide for Modern Web Applications

A practical, in-depth guide to the Credential Management API: what it is, how it works, code examples, integration patterns with WebAuthn and federated sign-in, browser support, pitfalls and best practices for modern web apps.

A practical, in-depth guide to the Credential Management API: what it is, how it works, code examples, integration patterns with WebAuthn and federated sign-in, browser support, pitfalls and best practices for modern web apps.

Why the Credential Management API matters

Authentication is one of the most user-facing pieces of any web application. Friction and insecurity at sign-in drive abandonment and increase support costs. The Credential Management API gives web apps a programmatic, privacy-respecting bridge to the browser’s credential storage and autofill systems - enabling smoother sign-in flows, tighter integration with federated providers, and a cleaner migration path toward passkeys (WebAuthn).

In this guide you’ll learn: what the API provides, practical examples (Password and Federated credentials), how it relates to WebAuthn (PublicKeyCredential), feature detection and fallbacks, common pitfalls, and best practices for security and UX.

Spec landscape and relationship to WebAuthn

  • The Credential Management API (Level 1) standardizes how a site can store and retrieve credentials via the navigator.credentials object. It focuses on credential types such as PasswordCredential and FederatedCredential.
  • The Web Authentication (WebAuthn) API covers public-key credential creation and assertion (passkeys), exposed as PublicKeyCredential via navigator.credentials.create() and navigator.credentials.get(). WebAuthn is a separate spec but works alongside the Credential Management API.

Think of the Credential Management API as the way to integrate with browser-managed username/password and federated accounts, while WebAuthn is the native passkey mechanism for cryptographic authentication. You can combine them: offer password/federated as fallbacks and use WebAuthn for strong authentication.

References: W3C Credential Management spec and WebAuthn spec for deeper details:

Basic concepts and API surface

  • navigator.credentials.get(options) - request stored credentials from the browser. Options may request password, federated, or publicKey credentials. The mediation option affects whether the browser is allowed to show a UI or attempt a silent retrieval.
  • navigator.credentials.store(credential) - store a credential (example: after a successful sign-in you can store a PasswordCredential or FederatedCredential).
  • new PasswordCredential(form) - construct a password credential from a form element or object.
  • new FederatedCredential({ id, provider, name, iconURL }) - represents a federated account.
  • navigator.credentials.create({ publicKey: ... }) and navigator.credentials.get({ publicKey: ... }) - WebAuthn flows for public-key credentials (passkeys).

Important: APIs require secure contexts (HTTPS) and often need user activation or explicit user consent depending on the browser and mediation mode.

Practical example - password sign in (classic flow + credential storage)

The typical pattern is:

  1. Detect support and try to silently retrieve an existing credential.
  2. If a credential is returned, log the user in (server-side validation needed).
  3. On explicit user sign-in, store the credential so the browser can offer it next time.

Feature detection and silent retrieval example:

async function tryAutoSignIn() {
  if (!('credentials' in navigator)) return null;

  try {
    // Try to get a stored password credential without prompting the user
    const cred = await navigator.credentials.get({
      password: true,
      mediation: 'silent', // 'silent' tries to avoid UI
    });

    if (cred) {
      // `cred.id` is typically the username/email, `cred.password` is available
      // for a PasswordCredential. Send to server for validation or create a session.
      const resp = await fetch('/auth/credential-signin', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id: cred.id, password: cred.password }),
      });
      // handle response
    }
  } catch (err) {
    // browsers vary in errors - safely ignore and fall back to form
    console.debug('Credential get failed', err);
  }
}

Store the credential after a successful sign-in (e.g., after your server returns a session token):

async function storePasswordCredential(formElement) {
  if (!('credentials' in navigator)) return;

  try {
    const cred = new PasswordCredential(formElement);
    await navigator.credentials.store(cred);
  } catch (err) {
    console.debug('Credential store failed', err);
  }
}

Notes:

  • Create PasswordCredential using a form element (id/password), or with an object specifying id, password, name.
  • Always store credentials after an explicit, user-driven sign-in action.
  • When using SPAs, ensure you await navigator.credentials.store before redirecting away or re-rendering in a way that cancels the promise.

Federated (social) sign-in

FederatedCredential represents a federated account (Google, Facebook, etc.). It lets browsers show a unified account selection UI and helps credential managers tie federated accounts to sites.

Example: storing a federated credential after an OAuth sign-in:

async function storeFederatedCredential(
  email,
  providerOrigin,
  displayName,
  icon
) {
  if (
    !('credentials' in navigator) ||
    typeof FederatedCredential === 'undefined'
  )
    return;

  try {
    const fed = new FederatedCredential({
      id: email,
      provider: providerOrigin, // e.g. 'https://accounts.google.com'
      name: displayName,
      iconURL: icon,
    });
    await navigator.credentials.store(fed);
  } catch (err) {
    console.debug('Failed to store federated credential', err);
  }
}

Caveats:

  • The provider URL must match expected federated providers and the browser may require certain policies for the provider origin.
  • Browser support for federated credentials is more limited than for passwords. Use feature detection.

WebAuthn (PublicKeyCredential) integration

WebAuthn is the recommended path forward for passwordless strong authentication. Example registration and authentication use the WebAuthn API and are conceptually separate from the PasswordCredential API.

Registration (simplified):

// server returns `publicKey` options for credential creation
const credential = await navigator.credentials.create({
  publicKey: publicKeyOptions,
});
// send credential to server for verification and account linking

Authentication (simplified):

// server returns `publicKey` options for assertion
const assertion = await navigator.credentials.get({ publicKey: getOptions });
// send assertion to server to verify and create session

How they interact:

  • WebAuthn operations are done via navigator.credentials.create / get with publicKey options. These return PublicKeyCredential objects.
  • You can use Credential Management to coordinate flows (e.g., attempt WebAuthn first, fallback to PasswordCredential).
  • Browsers often surface passkeys within their own UI; you do not manually store passkeys using navigator.credentials.store - the WebAuthn flow manages the key material.

Mediation modes and UX differences

mediation option influences how credentials are fetched:

  • silent - attempt to retrieve credentials without showing UI. Good for auto sign-in when you want zero interaction, but may return null if the browser requires user selection.
  • optional - the browser may prompt the user if needed - a middle ground.
  • required - require the browser to show a credential selection UI; if the browser can’t satisfy the request it fails.

Choose the mode that best matches your UX expectations. For automatic, low-friction login on trusted devices, try silent first and fall back to a prompt.

Browser support and progressive enhancement

Support varies across browsers and over time. Always feature-detect and provide robust fallbacks:

if ('credentials' in navigator) {
  // safe to use Credential Management API
}

Fallbacks:

  • If not supported, fall back to classic HTML form sign-in and use conventional browser autofill (username/password attributes, proper autocomplete attributes).
  • For federated sign-ins, use the provider SDK + redirect or popup flows as the canonical fallback.

Because implementations differ, test in the browsers your users use most. Don’t rely on mediation: 'silent' to always succeed.

Common pitfalls and how to avoid them

  1. Storing before authentication completes

    • Problem: calling navigator.credentials.store before the server confirms authentication can result in inconsistent state.
    • Fix: store only after server-side success and after a user-triggered sign-in action.
  2. Ignoring promises and redirects

    • Problem: starting a navigation before navigator.credentials.store resolves may cancel the store.
    • Fix: await navigator.credentials.store(cred) or defer navigation until done.
  3. Confusing WebAuthn with PasswordCredential

    • Problem: storing passkeys manually with navigator.credentials.store is not how WebAuthn works.
    • Fix: use WebAuthn flows for public-key credentials and let the browser manage the keys.
  4. Assuming consistent behavior across browsers

    • Problem: differences in mediation, provider handling, and federated credential policies.
    • Fix: robust UX that gracefully falls back and logs analytics events for credential failures.
  5. Sending plaintext passwords unnecessarily

    • Problem: developers may accidentally expose passwords in logs or telemetry when using PasswordCredential.password.
    • Fix: treat the credential like any secret - send it over TLS to your authentication endpoint and avoid logging.

Security and privacy considerations

  • Always use HTTPS; the APIs are only available in secure contexts.
  • Prefer server-side verification; never trust client-only assertions.
  • Keep CSRF protections and same-site cookie policies in place. Auto-signed-in sessions should still follow secure session management practices.
  • Be cautious with mediation: 'silent' - silent auto sign-in should only be used where it’s appropriate and expected by users (e.g., personal devices).
  • Do not send more data than necessary to the authentication endpoint, and do not leak credential fields in logs or analytics.

Testing and debugging tips

  • Test across Chrome/Chromium, Firefox, and Safari (mobile and desktop) if your audience is diverse. Behavior differs.
  • Use browser devtools to inspect stored credentials and check console messages. Some browsers log credential API activity.
  • Add telemetry for credential flows (attempt, success, failure) so you can measure impact on conversions.
  • Feature detect and progressively enhance; never break the classic form flow.
  • Always store credentials only after a successful authentication event.
  • Use mediation: 'silent' for a first-pass attempt to auto-sign-in, then fall back to optional or explicit UI.
  • Integrate WebAuthn for strong passwordless options and provide password/federated fallbacks.
  • Avoid logging secrets and ensure all endpoints are served over HTTPS.
  • Test across platforms, and observe how browser password managers interact with your site.

Example integration flow (summary)

  1. Page loads → run navigator.credentials.get({ password: true, mediation: 'silent' }).
  2. If credential returned → send to server to validate and create session.
  3. If no credential → display normal login UI.
  4. On user sign-in success → await navigator.credentials.store(new PasswordCredential(form)) (or new FederatedCredential(...) for social logins).
  5. Offer WebAuthn registration as an alternative: call navigator.credentials.create({ publicKey }) and link the public key in the user account.

Further reading and references

Back to Blog

Related Posts

View All Posts »