Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 3x 3x 3x 3x 3x 3x | import { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { api } from '../api/client'
import {
prepareAuthenticationOptions,
serializeAuthenticationCredential,
} from '../lib/webauthn'
import { useAuth } from '../contexts/AuthContext'
export default function PasskeyLogin() {
const navigate = useNavigate()
const { refreshSession } = useAuth()
const [error, setError] = useState('')
const [submitting, setSubmitting] = useState(false)
const webauthnSupported = typeof window !== 'undefined' && !!window.PublicKeyCredential
async function handleLogin() {
setError('')
setSubmitting(true)
try {
const options = await api.passkey.loginOptions()
if (options && 'no_credentials' in options) {
setError('No accounts exist yet. Create an account first.')
return
}
const publicKeyOptions = prepareAuthenticationOptions(options)
const credential = await navigator.credentials.get({
publicKey: publicKeyOptions,
})
if (!credential) {
setError('Sign in was cancelled.')
return
}
await api.passkey.loginVerify(
serializeAuthenticationCredential(credential as PublicKeyCredential)
)
await refreshSession()
navigate('/home')
} catch (err) {
if (err instanceof DOMException && err.name === 'NotAllowedError') {
setError('Sign in was cancelled.')
} else {
setError(err instanceof Error ? err.message : 'Sign in failed')
}
} finally {
setSubmitting(false)
}
}
return (
<div className="flex min-h-screen items-center justify-center bg-background p-4">
<div className="w-full max-w-sm space-y-6">
<div className="text-center">
<h1 className="text-3xl font-bold text-foreground">Cookie</h1>
<p className="mt-1 text-sm text-muted-foreground">Sign in with your passkey</p>
</div>
{error && (
<div role="alert" className="rounded-md bg-red-50 p-3 text-sm text-red-800 dark:bg-red-900/20 dark:text-red-300">
{error}
</div>
)}
<div className="space-y-4">
{webauthnSupported ? (
<button
onClick={handleLogin}
disabled={submitting}
className="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm hover:bg-primary/90 disabled:opacity-50"
>
{submitting ? 'Signing in...' : 'Sign In'}
</button>
) : (
<p className="text-center text-sm text-muted-foreground">
Your browser does not support passkeys. Please use a modern browser or pair this device
using a code from another device.
</p>
)}
</div>
<div className="text-center text-sm text-muted-foreground">
<p>
Don't have an account?{' '}
<Link to="/register" className="font-medium text-primary hover:underline">
Create Account
</Link>
</p>
</div>
<div className="text-center">
<a href="/privacy/" className="text-xs text-muted-foreground hover:text-foreground hover:underline">
Privacy Policy
</a>
</div>
</div>
</div>
)
}
|