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
- Serve the frontend over HTTPS (or use http://localhost / http://127.0.0.1 during development) so WebAuthn APIs are exposed.
- Before showing passkey UI, feature-detect and hide passkey buttons when window.PublicKeyCredential is missing rather than letting login() throw.
- In tests, polyfill/stub window.PublicKeyCredential and navigator.credentials in the test setup file.
- 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
- Always develop/test passkey flows on HTTPS or localhost
- Feature-detect before showing passkey buttons, not only before the API call
- Stub window.PublicKeyCredential/navigator.credentials in unit test setup files
- Keep a password/OTP fallback route for users on old browsers
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
- Passkey sign-in was cancelled
- Passkey creation was cancelled
- No response body
- HTTP error! status: ${response.status}
- HTTP error! status: ${response.status}
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/f3e6538490cf9ea8.
Report an issue: GitHub.