download
raw
5.7 kB
/**
* 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 { CodeProviderOptions } from "../provider/code.js"
import { UnknownStateError } from "../error.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",
}
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<string, string>, code: string) => Promise<void>
/**
* Custom copy for the UI.
*/
copy?: Partial<CodeUICopy>
/**
* 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<Response> => {
if (state.type === "start") {
const jsx = (
<Layout>
<form data-component="form" method="post">
{error?.type === "invalid_claim" && (
<FormAlert message={copy.email_invalid} />
)}
<input type="hidden" name="action" value="request" />
<input
data-component="input"
autofocus
type={mode === "email" ? "email" : "tel"}
name={mode === "email" ? "email" : "phone"}
inputmode={mode === "email" ? "email" : "numeric"}
required
placeholder={copy.email_placeholder}
/>
<button data-component="button">{copy.button_continue}</button>
</form>
<p data-component="form-footer">{copy.code_info}</p>
</Layout>
)
return new Response(jsx.toString(), {
headers: {
"Content-Type": "text/html",
},
})
}
if (state.type === "code") {
const jsx = (
<Layout>
<form data-component="form" class="form" method="post">
{error?.type === "invalid_code" && (
<FormAlert message={copy.code_invalid} />
)}
{state.type === "code" && (
<FormAlert
message={
(state.resend ? copy.code_resent : copy.code_sent) +
state.claims.email
}
color="success"
/>
)}
<input type="hidden" name="action" value="verify" />
<input
data-component="input"
autofocus
minLength={6}
maxLength={6}
type="text"
name="code"
required
inputmode="numeric"
autocomplete="one-time-code"
placeholder={copy.code_placeholder}
/>
<button data-component="button">{copy.button_continue}</button>
</form>
<form method="post">
{Object.entries(state.claims).map(([key, value]) => (
<input
key={key}
type="hidden"
name={key}
value={value}
className="hidden"
/>
))}
<input type="hidden" name="action" value="request" />
<div data-component="form-footer">
<span>
{copy.code_didnt_get}{" "}
<button data-component="link">{copy.code_resend}</button>
</span>
</div>
</form>
</Layout>
)
return new Response(jsx.toString(), {
headers: {
"Content-Type": "text/html",
},
})
}
throw new UnknownStateError()
},
}
}

Xet Storage Details

Size:
5.7 kB
·
Xet hash:
df192826f2b5610560cf77228fba5d408c3a3ddeb08e68727f3f8ca708451116

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.