Passkey Setup Guide
Passkey Setup Guide
Section titled “Passkey Setup Guide”This guide covers advanced passkey authentication setup for your application.
Prerequisites
Section titled “Prerequisites”- HTTPS-enabled domain (or localhost for development)
- Modern browser with WebAuthn support
- Orbital API credentials
Installation
Section titled “Installation”Install the WebAuthn browser library:
npm install @simplewebauthn/browserBasic Implementation
Section titled “Basic Implementation”1. Registration Flow
Section titled “1. Registration Flow”Create a registration form:
<form id="register-form"> <input type="email" id="email" placeholder="Email" required /> <input type="text" id="displayName" placeholder="Display Name" required /> <button type="submit">Register with Passkey</button></form>Handle registration:
import { startRegistration } from '@simplewebauthn/browser';
document.getElementById('register-form')?.addEventListener('submit', async (e) => { e.preventDefault();
const email = (document.getElementById('email') as HTMLInputElement).value; const displayName = (document.getElementById('displayName') as HTMLInputElement).value;
try { const invitationCode = 'abc123xyz';
// Step 1: Begin registration with invitation const beginResp = await fetch(`/api/auth/passkey/begin?invite=${invitationCode}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, });
if (!beginResp.ok) { throw new Error('Failed to begin registration'); }
const { publicKey, sessionId } = await beginResp.json();
// Step 2: Create credential const credential = await startRegistration(publicKey);
// Step 3: Complete registration const completeResp = await fetch('/api/auth/passkey/complete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'SessionID': sessionId, }, body: JSON.stringify(credential), });
if (!completeResp.ok) { throw new Error('Registration failed'); }
alert('Registration successful! You can now log in.'); } catch (error) { console.error('Registration error:', error); alert('Registration failed. Please try again.'); }});2. Login Flow
Section titled “2. Login Flow”Create a login button:
<button id="login-btn">Sign in with Passkey</button>Handle authentication:
import { startAuthentication } from '@simplewebauthn/browser';
document.getElementById('login-btn')?.addEventListener('click', async () => { try { // Step 1: Begin authentication const beginResp = await fetch('/api/auth/passkey/begin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), });
if (!beginResp.ok) { throw new Error('Failed to begin login'); }
const { publicKey, sessionId } = await beginResp.json();
// Step 2: Get credential const credential = await startAuthentication(publicKey);
// Step 3: Complete authentication const completeResp = await fetch('/api/auth/passkey/complete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'SessionID': sessionId, }, body: JSON.stringify(credential), credentials: 'include', });
if (!completeResp.ok) { throw new Error('Authentication failed'); }
// Auth token is set as httpOnly cookie window.location.href = '/dashboard'; } catch (error) { console.error('Login error:', error); alert('Login failed. Please try again.'); }});Advanced Features
Section titled “Advanced Features”Conditional UI
Section titled “Conditional UI”Show/hide UI based on passkey availability:
async function checkPasskeySupport() { if (!window.PublicKeyCredential) { return { supported: false, available: false }; }
const supported = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
return { supported: true, available: supported, };}
// Use itconst { supported, available } = await checkPasskeySupport();
if (!available) { // Show fallback authentication document.getElementById('passkey-login')?.style.display = 'none'; document.getElementById('password-login')?.style.display = 'block';}Autofill UI (Conditional Mediation)
Section titled “Autofill UI (Conditional Mediation)”Allow passkeys in form autofill:
async function conditionalLogin() { if (!window.PublicKeyCredential?.isConditionalMediationAvailable) { return; }
const available = await PublicKeyCredential.isConditionalMediationAvailable();
if (available) { const beginResp = await fetch('/api/auth/passkey/begin', { method: 'POST', body: JSON.stringify({}), });
const { publicKey, sessionId } = await beginResp.json();
// This will show in the autofill UI const credential = await startAuthentication(publicKey, true);
await fetch('/api/auth/passkey/complete', { method: 'POST', headers: { 'SessionID': sessionId }, body: JSON.stringify(credential), }); }}
// Call on page loadconditionalLogin();Multiple Passkeys
Section titled “Multiple Passkeys”Allow users to register multiple passkeys:
async function listUserPasskeys() { const resp = await fetch('/api/auth/passkeys', { credentials: 'include', });
const passkeys = await resp.json(); return passkeys;}
async function deletePasskey(credentialId: string) { await fetch(`/api/auth/passkeys/${credentialId}`, { method: 'DELETE', credentials: 'include', });}Error handling (quick reference)
Section titled “Error handling (quick reference)”Typical issues to check:
NotAllowedError– user cancelled the prompt or it timed outInvalidStateError– credential already registered or reusedNotSupportedError– browser does not support WebAuthnSecurityError– non‑HTTPS origin or RP ID mismatch
Testing Locally
Section titled “Testing Locally”For local development:
- Use
localhost(not127.0.0.1) - Configure RP ID correctly:
// In your API configuration{ rpId: 'localhost', rpName: 'Orbital (Dev)', origin: 'http://localhost:3000',}Next steps
Section titled “Next steps”- Authentication – overview and bootstrap
- Passkey Authentication Flow – endpoint‑level reference
- API Reference – full auth endpoints