@better-auth-ui/core
Version:
Authentication components and data utilities for [Better Auth](https://better-auth.com), available for React and Solid.
114 lines (106 loc) • 3.32 kB
text/typescript
import { createAuthPlugin } from "../../lib/create-auth-plugin"
// Side-effect import so this file participates in declaration merging on the
// same module instance that external consumers reach via `@better-auth-ui/core`.
import type {} from "../../lib/view-paths"
import {
parseTwoFactorMethods,
type TwoFactorMethod
} from "./two-factor-challenge"
import {
type TwoFactorLocalization,
twoFactorLocalization
} from "./two-factor-localization"
declare module "../../lib/view-paths" {
/** Widens `AuthViewPaths` with the two-factor path when this plugin is imported. */
interface AuthViewPaths {
/** @default "two-factor" */
twoFactor?: string
}
}
export type TwoFactorPluginOptions = {
/**
* Override the plugin's default localization strings.
* @remarks `TwoFactorLocalization`
*/
localization?: Partial<TwoFactorLocalization>
/**
* URL segment for the two-factor challenge view.
* @remarks `string`
* @default "two-factor"
*/
path?: string
/**
* Number of characters rendered by the TOTP and OTP inputs.
*
* Keep this in sync with Better Auth's `twoFactor({ totpOptions: { digits } })`
* and `twoFactor({ otpOptions: { digits } })` server options.
*
* @remarks `number`
* @default 6
*/
codeLength?: number
/**
* Offer backup-code recovery on the challenge view and expose backup-code
* management in settings.
*
* Turn this off when the server sets `backupCodeOptions: { enabled: false }`.
*
* @remarks `boolean`
* @default true
*/
backupCodes?: boolean
/**
* Show the "Trust this device" checkbox on the challenge view.
*
* @remarks `boolean`
* @default true
*/
trustDevice?: boolean
/**
* Manage two-factor without asking for a password.
*
* Mirrors Better Auth's `twoFactor({ allowPasswordless })` server option,
* which only skips the password for accounts that have no credential
* account (e.g. passkey-only users) — set both or neither.
*
* @remarks `boolean`
* @default false
*/
allowPasswordless?: boolean
/**
* Methods users can choose while enrolling in two-factor authentication.
*
* Add `"otp"` only when the server configures `otpOptions.sendOTP`.
*
* @remarks `TwoFactorMethod[]`
* @default ["totp"]
*/
enrollmentMethods?: TwoFactorMethod[]
}
const DEFAULT_CODE_LENGTH = 6
function resolveCodeLength(value?: number) {
if (value === undefined || !Number.isFinite(value)) {
return DEFAULT_CODE_LENGTH
}
return Math.max(1, Math.floor(value))
}
function resolveEnrollmentMethods(methods?: TwoFactorMethod[]) {
const resolved = parseTwoFactorMethods(methods)
return resolved.length ? resolved : (["totp"] satisfies TwoFactorMethod[])
}
export const twoFactorPlugin = createAuthPlugin(
"twoFactor",
(options: TwoFactorPluginOptions = {}) => ({
localization: { ...twoFactorLocalization, ...options.localization },
codeLength: resolveCodeLength(options.codeLength),
backupCodes: options.backupCodes ?? true,
trustDevice: options.trustDevice ?? true,
allowPasswordless: options.allowPasswordless ?? false,
enrollmentMethods: resolveEnrollmentMethods(options.enrollmentMethods),
viewPaths: {
auth: {
twoFactor: options.path ?? "two-factor"
}
}
})
)