/** * Configure the UI that's used by the Code provider. * * ```ts {1,7-12} * import { CodeUI } from "@openauthjs/openauth/ui/code" * import { CodeProvider } from "@openauthjs/openauth/provider/code" * * export default issuer({ * providers: { * code: CodeAdapter( * CodeUI({ * copy: { * code_info: "We'll send a pin code to your email" * }, * sendCode: (claims, code) => console.log(claims.email, code) * }) * ) * }, * // ... * }) * ``` * * @packageDocumentation */ /** @jsxImportSource hono/jsx */ import { UnknownStateError } from '../error.js'; import { CodeProviderOptions } from '../provider/code.js'; import { Layout } from './base.js'; import { FormAlert } from './form.js'; const DEFAULT_COPY = { /** * Copy for the email input. */ email_placeholder: 'Email', /** * Error message when the email is invalid. */ email_invalid: 'Email address is not valid', /** * Copy for the continue button. */ button_continue: 'Continue', /** * Copy informing that the pin code will be emailed. */ code_info: "We'll send a pin code to your email.", /** * Copy for the pin code input. */ code_placeholder: 'Code', /** * Error message when the code is invalid. */ code_invalid: 'Invalid code', /** * Copy for when the code was sent. */ code_sent: 'Code sent to ', /** * Copy for when the code was resent. */ code_resent: 'Code resent to ', /** * Copy for the link to resend the code. */ code_didnt_get: "Didn't get code?", /** * Copy for the resend button. */ code_resend: 'Resend', /** * Error message when too many codes have been asked for, or too many * guesses made. Deliberately one message for both: which of the two it was * is a fact about somebody else's mailbox. */ rate_limited: 'Too many attempts. Wait a moment and start again.', /** * The consent line under the action, split around its two links so the * sentence stays one translatable run rather than being glued together * from fragments in the markup. */ terms_before: 'By continuing, you acknowledge that you have read and understood, and agree to Nestri\u2019s ', terms_label: 'Terms & Conditions', terms_url: 'https://nestri.io/terms', terms_between: ' and ', privacy_label: 'Privacy Policy', privacy_url: 'https://nestri.io/privacy', terms_after: '.' }; export type CodeUICopy = typeof DEFAULT_COPY; /** * Configure the password UI. */ export interface CodeUIOptions { /** * Callback to send the pin code to the user. * * The `claims` object contains the email or phone number of the user. You can send the code * using this. * * @example * ```ts * async (claims, code) => { * // Send the code via the claim * } * ``` */ sendCode: (claims: Record, code: string) => Promise; /** * Custom copy for the UI. */ copy?: Partial; /** * The mode to use for the input. * @default "email" */ mode?: 'email' | 'phone'; } /** * Creates a UI for the Code provider flow. * @param props - Configure the UI. */ export function CodeUI(props: CodeUIOptions): CodeProviderOptions { const copy = { ...DEFAULT_COPY, ...props.copy }; const mode = props.mode ?? 'email'; return { sendCode: props.sendCode, length: 6, request: async (_req, state, _form, error): Promise => { if (state.type === 'start') { const jsx = (
{error?.type === 'invalid_claim' && } {error?.type === 'rate_limit' && }

{copy.terms_before} {copy.terms_label} {copy.terms_between} {copy.privacy_label} {copy.terms_after}

); return new Response(jsx.toString(), { headers: { 'Content-Type': 'text/html' } }); } if (state.type === 'code') { const jsx = (
{error?.type === 'invalid_code' && } {error?.type === 'rate_limit' && } {state.type === 'code' && ( )}
{Object.entries(state.claims).map(([key, value]) => ( ))}
{copy.code_didnt_get}
); return new Response(jsx.toString(), { headers: { 'Content-Type': 'text/html' } }); } throw new UnknownStateError(); } }; }