Wei-Shaw/sub2api · error

Passkeys are not supported by this browser

Error message

Passkeys are not supported by this browser

What it means

requirePasskeySupport() in frontend/src/api/passkey.ts:21 throws when window.PublicKeyCredential or navigator.credentials is undefined. These APIs (WebAuthn) are only exposed in secure contexts (HTTPS or localhost) and in browsers that implement WebAuthn (all modern ones since ~2018). The check runs before every login()/register() call, so any environment lacking WebAuthn fails fast before a network round trip.

Source

Thrown at frontend/src/api/passkey.ts:21

export interface PasskeyCredentialSummary {
  id: number
  name: string
  created_at: string
  last_used_at?: string
  backup: boolean
}

interface CeremonyOptionsResponse {
  session_token: string
  options: {
    publicKey: Record<string, unknown>
  }
}

function requirePasskeySupport(): void {
  if (!window.PublicKeyCredential || !navigator.credentials) {
    throw new Error('Passkeys are not supported by this browser')
  }
}

function base64URLToBuffer(value: string): ArrayBuffer {
  const normalized = value.replace(/-/g, '+').replace(/_/g, '/')
  const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4)
  const binary = atob(padded)
  const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0))
  return bytes.buffer
}

function bufferToBase64URL(value: ArrayBuffer | null): string | null {
  if (value === null) return null
  const bytes = new Uint8Array(value)
  let binary = ''
  for (const byte of bytes) binary += String.fromCharCode(byte)
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
}

View on GitHub (pinned to 073e92d171)

Solutions

  1. Serve the frontend over HTTPS (or use http://localhost / http://127.0.0.1 during development) so WebAuthn APIs are exposed.
  2. Before showing passkey UI, feature-detect and hide passkey buttons when window.PublicKeyCredential is missing rather than letting login() throw.
  3. In tests, polyfill/stub window.PublicKeyCredential and navigator.credentials in the test setup file.
  4. If users are on old browsers, offer a password/OTP fallback sign-in path.

Example fix

// before
function requirePasskeySupport(): void {
  if (!window.PublicKeyCredential || !navigator.credentials) {
    throw new Error('Passkeys are not supported by this browser')
  }
}

// after
export function isPasskeySupported(): boolean {
  return typeof window.PublicKeyCredential === 'function' && !!navigator.credentials;
}
function requirePasskeySupport(): void {
  if (!isPasskeySupported()) {
    throw new Error('Passkeys are not supported by this browser')
  }
}
// template: <button v-if="isPasskeySupported()">Sign in with passkey</button>
Defensive patterns

Strategy: validation

Validate before calling

export function isPasskeySupported(): boolean {
  return typeof window.PublicKeyCredential === 'function' && !!navigator.credentials && window.isSecureContext;
}
// call before rendering passkey UI:
if (!isPasskeySupported()) hidePasskeyButtons();

Type guard

function isPasskeySupported(): boolean {
  return typeof window.PublicKeyCredential === 'function'
    && !!navigator.credentials
    && window.isSecureContext === true;
}

Try / catch

try { await login(); } catch (e) {
  if (e instanceof Error && e.message === 'Passkeys are not supported by this browser') {
    showPasswordFallback(); return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling login() or register() from passkey.ts while: the page is served over plain HTTP on a non-localhost host; the browser is pre-2018 (IE11, old Safari < 16.4 on Windows, legacy Android WebView); WebAuthn is disabled by enterprise policy; or the code runs in a non-browser/jsdom test environment where the globals were never installed.

Common situations: Dev servers accessed over http://192.168.x.x from a phone (not a secure context); older in-app browsers; unit tests in Vitest/Jest that render components calling passkey login without stubbing PublicKeyCredential; captive-portal/proxied environments that strip HTTPS.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/f3e6538490cf9ea8. Report an issue: GitHub.