import { useState } from 'react'; import { Button } from '#/components/atoms/button.jsx'; import { Heading } from '#/components/atoms/heading.jsx'; import { Panel } from '#/components/atoms/panel.jsx'; import { ErrorNotice } from '#/components/molecules/error-notice.jsx'; import { TextField } from '#/components/molecules/text-field.jsx'; import { b64, unb64 } from '#/lib/base64.js'; export function SignInPage({ hostname, account = null, error = '', passkeys }) { const [err, setErr] = useState(error); const [busy, setBusy] = useState(false); const passkeySignIn = async () => { setErr(''); setBusy(true); try { if (!window.PublicKeyCredential) { throw new Error('This browser has no passkey support.'); } const start = await fetch('/account/passkey/challenge'); const { challenge, rpId, error: startError } = await start.json(); if (!start.ok) { throw new Error(startError || 'Could not start a passkey sign-in.'); } const assertion = await navigator.credentials.get({ publicKey: { challenge: unb64(challenge), rpId, userVerification: 'preferred', timeout: 120000, }, }); const response = await fetch('/account/passkey/sign-in', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credentialId: b64(assertion.rawId), authenticatorData: b64(assertion.response.authenticatorData), clientDataJSON: b64(assertion.response.clientDataJSON), signature: b64(assertion.response.signature), }), }); const result = await response.json(); if (!response.ok) throw new Error(result.error || 'That passkey was refused.'); window.location.href = '/account'; } catch (caught) { setErr( caught?.name === 'NotAllowedError' ? 'Cancelled.' : String(caught?.message || caught), ); setBusy(false); } }; return (
Sign in to the account hosted by{' '} {hostname}.