UNPKG

accounts

Version:

Tempo Accounts SDK

104 lines 4.14 kB
import { Bytes } from 'ox'; import { Authentication, Registration } from 'webauthx/server'; import * as Storage from './Storage.js'; /** Creates a {@link WebAuthnCeremony} from a custom implementation. */ export function from(ceremony) { return ceremony; } /** * Creates a pure client-side ceremony for development and prototyping. * * Generates challenges and verifies responses locally using `webauthx/server`. * Stores credentials in memory. No external server needed. * * @example * ```ts * import { WebAuthnCeremony } from 'accounts' * * const ceremony = WebAuthnCeremony.local() * ``` */ export function local(options = {}) { const rpId = options.rpId ?? (typeof location !== 'undefined' ? location.hostname : 'localhost'); const storage = options.storage ?? (typeof window !== 'undefined' ? Storage.idb() : Storage.memory()); const storageKey = 'credentials'; return from({ async getRegistrationOptions(parameters) { const { excludeCredentialIds, name, userId } = parameters; const { options } = Registration.getOptions({ excludeCredentialIds: excludeCredentialIds, name, rp: { id: rpId, name: rpId }, user: userId ? { id: Bytes.fromString(userId), name } : undefined, }); return { options }; }, async verifyRegistration(credential) { const publicKey = credential.publicKey; const credentials = (await storage.getItem(storageKey)) ?? {}; credentials[credential.id] = publicKey; await storage.setItem(storageKey, credentials); return { credentialId: credential.id, publicKey }; }, async getAuthenticationOptions(parameters = {}) { const { allowCredentialIds, challenge, credentialId } = parameters; const { options } = Authentication.getOptions({ challenge, credentialId: allowCredentialIds ?? credentialId, rpId, }); return { options }; }, async verifyAuthentication(response) { const credentials = (await storage.getItem(storageKey)) ?? {}; const publicKey = credentials[response.id]; if (!publicKey) throw new Error(`Unknown credential: ${response.id}`); return { credentialId: response.id, publicKey }; }, }); } /** * Creates a server-backed ceremony that delegates to a remote {@link Handler.webAuthn} endpoint. * * All challenge generation, verification, and credential storage happen server-side. * The client uses `fetch()` to communicate with 4 POST endpoints derived from the base URL. * * @example * ```ts * import { WebAuthnCeremony } from 'accounts' * * const ceremony = WebAuthnCeremony.server({ url: 'https://example.com/webauthn' }) * ``` */ export function server(options) { const { url } = options; async function request(path, body) { const response = await fetch(`${url}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); const json = await response.json(); if (!response.ok) throw new Error(json.error ?? 'Request failed'); return json; } return from({ async getRegistrationOptions(parameters) { const { excludeCredentialIds, name, userId } = parameters; return request('/register/options', { excludeCredentialIds, name, userId }); }, async verifyRegistration(credential) { return request('/register', credential); }, async getAuthenticationOptions(parameters = {}) { const { allowCredentialIds, challenge, credentialId, mediation } = parameters; return request('/login/options', { allowCredentialIds, challenge, credentialId, mediation }); }, async verifyAuthentication(response) { return request('/login', response); }, }); } //# sourceMappingURL=WebAuthnCeremony.js.map