Implement Passkeys

Implement AuthOS WebAuthn passkey registration and login

AuthOS release 0.8.2 TypeScript SDK 0.8.0 Latest-only documentation
Updated Jul 15, 2026 Edit this page
On this page

Complete the passwordless prerequisites and choose passkeys only after defining a tested fallback and account-recovery path.

Passkeys (WebAuthn) Authentication

Passkeys use origin-bound WebAuthn credentials with platform or hardware authenticators, which are designed to reduce phishing risk. This Beta implementation has not yet published conformance or abuse-case results.

Browser Compatibility Check

Before implementing passkeys, check if the user’s browser supports WebAuthn:

import { SsoClient } from '@drmhse/sso-sdk';

const sso = new SsoClient({
  baseURL: 'https://sso.example.com'
});

// Check if WebAuthn is supported
if (!sso.passkeys.isSupported()) {
  console.log('Passkeys not supported - show password login instead');
  return;
}

// Check if platform authenticator is available (Touch ID, Face ID, etc.)
const hasPlatformAuth = await sso.passkeys.isPlatformAuthenticatorAvailable();
if (hasPlatformAuth) {
  console.log('Biometric authentication available');
}

Registering a Passkey

Users must register a passkey while authenticated. This is typically done from account settings after the user has logged in with another method.

async function registerPasskey() {
  // User must be authenticated first
  const accessToken = localStorage.getItem('sso_access_token');
  if (!accessToken) {
    throw new Error('User must be logged in to register a passkey');
  }

  sso.setAuthToken(accessToken);

  try {
    // Register the passkey with a friendly name
    const passkeyId = await sso.passkeys.register('My MacBook Pro');

    console.log('Passkey registered successfully:', passkeyId);
    // Show success message to user
    return passkeyId;
  } catch (error) {
    console.error('Passkey registration failed:', error);
    // Handle errors: user cancelled, authenticator not available, etc.
    throw error;
  }
}

Logging In with a Passkey

Once registered, users can authenticate using their passkey:

async function loginWithPasskey(email: string) {
  try {
    // Initiate passkey authentication
    const result = await sso.passkeys.login(email);

    // Store the token
    localStorage.setItem('sso_access_token', result.token);
    sso.setAuthToken(result.token);

    console.log('Logged in as:', result.user_id);

    // Redirect to dashboard
    window.location.href = '/dashboard';
  } catch (error) {
    if (error instanceof Error) {
      if (error.message.includes('no passkeys registered')) {
        // User has no passkeys - offer alternative login
        console.error('No passkeys registered for this account');
      } else {
        console.error('Passkey login failed:', error.message);
      }
    }
    throw error;
  }
}

Complete React Passkey Implementation

import { useState } from 'react';
import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';

const sso = new SsoClient({
  baseURL: process.env.REACT_APP_SSO_URL!
});

export function PasskeyLogin() {
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [isSupported, setIsSupported] = useState(true);

  // Check browser support on mount
  useEffect(() => {
    setIsSupported(sso.passkeys.isSupported());
  }, []);

  const handlePasskeyLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    setIsLoading(true);

    try {
      const result = await sso.passkeys.login(email);

      // Store token and redirect
      localStorage.setItem('sso_access_token', result.token);
      sso.setAuthToken(result.token);
      window.location.href = '/dashboard';
    } catch (err) {
      setIsLoading(false);

      if (err instanceof SsoApiError) {
        if (err.statusCode === 404) {
          setError('No passkeys registered for this account. Please use another login method.');
        } else {
          setError(err.message);
        }
      } else if (err instanceof Error) {
        if (err.name === 'NotAllowedError') {
          setError('Authentication cancelled or timed out');
        } else {
          setError('Passkey authentication failed');
        }
      }
    }
  };

  if (!isSupported) {
    return (
      <div className="alert alert-warning">
        Your browser does not support passkeys. Please use a modern browser or try password login.
      </div>
    );
  }

  return (
    <div className="passkey-login">
      <h2>Sign in with Passkey</h2>
      <p>Use Touch ID, Face ID, or your security key to sign in securely</p>

      <form onSubmit={handlePasskeyLogin}>
        <div className="form-group">
          <label>Email Address</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="you@example.com"
            required
          />
        </div>

        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Authenticating...' : 'Sign in with Passkey'}
        </button>
      </form>

      {error && <div className="alert alert-error">{error}</div>}

      <div className="alternative-methods">
        <a href="/login/password">Sign in with password instead</a>
      </div>
    </div>
  );
}

export function PasskeyRegistration() {
  const [deviceName, setDeviceName] = useState('');
  const [error, setError] = useState('');
  const [success, setSuccess] = useState(false);
  const [isLoading, setIsLoading] = useState(false);

  const handleRegister = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    setSuccess(false);
    setIsLoading(true);

    try {
      // User must already be authenticated
      const passkeyId = await sso.passkeys.register(deviceName || undefined);

      setSuccess(true);
      setDeviceName('');
      console.log('Registered passkey:', passkeyId);
    } catch (err) {
      setIsLoading(false);

      if (err instanceof Error) {
        if (err.name === 'NotAllowedError') {
          setError('Registration cancelled or not allowed');
        } else if (err.name === 'InvalidStateError') {
          setError('A passkey is already registered on this device');
        } else {
          setError(err.message);
        }
      }
    }
  };

  if (!sso.passkeys.isSupported()) {
    return (
      <div className="alert alert-warning">
        Your browser does not support passkeys
      </div>
    );
  }

  return (
    <div className="passkey-registration">
      <h3>Register a Passkey</h3>
      <p>Add a passkey to sign in faster and more securely</p>

      {success ? (
        <div className="alert alert-success">
          Passkey registered successfully! You can now use it to sign in.
        </div>
      ) : (
        <form onSubmit={handleRegister}>
          <div className="form-group">
            <label>Device Name (Optional)</label>
            <input
              type="text"
              value={deviceName}
              onChange={(e) => setDeviceName(e.target.value)}
              placeholder="e.g., My MacBook Pro"
            />
            <small>Give this passkey a name to identify which device it's on</small>
          </div>

          <button type="submit" disabled={isLoading}>
            {isLoading ? 'Registering...' : 'Register Passkey'}
          </button>
        </form>
      )}

      {error && <div className="alert alert-error">{error}</div>}
    </div>
  );
}

Best Practices

Passkey Security

  1. Always use HTTPS: WebAuthn requires a secure context
  2. Prevent account lockout: Always offer alternative authentication methods
  3. User-friendly device names: Encourage users to name their passkeys
  4. Handle errors gracefully: Users may cancel authentication or not have a compatible device
  5. Support multiple passkeys: Allow users to register passkeys on multiple devices

Troubleshooting

Passkey Issues

“WebAuthn not supported”

  • Ensure the site is accessed via HTTPS (localhost is exempt)
  • Check browser compatibility (Chrome 67+, Safari 14+, Firefox 60+)
  • Verify user has a compatible authenticator

“Registration failed”

  • User may have cancelled the operation
  • Authenticator might already have a credential for this site
  • Check for conflicting browser extensions

“Authentication failed”

  • User may have no registered passkeys
  • Passkey might have been removed from the authenticator
  • Network or server errors

Next steps