UNPKG

@better-auth-ui/core

Version:

Authentication components and data utilities for [Better Auth](https://better-auth.com), available for React and Solid.

202 lines (194 loc) 6.19 kB
import type { AuthClient } from "../lib/auth-client" import { type AuthLocale, defaultAuthLocale, localizeAuthPlugins } from "../lib/auth-locale" import type { AuthPlugin } from "../lib/auth-plugin" import { type BasePaths, basePaths } from "../lib/base-paths" import type { DeepPartial } from "../lib/deep-partial" import { type Localization, localization } from "../lib/localization" import { deepmerge, resizeAvatar } from "../lib/utils" import { type ViewPaths, viewPaths } from "../lib/view-paths" import type { AdditionalField, AdditionalFields } from "./additional-fields-config" import type { AvatarConfig } from "./avatar-config" import type { EmailAndPasswordConfig } from "./email-and-password-config" import type { AuthSocialProvider } from "./social-provider-config" export type SocialSignInMode = "redirect" | "popup" /** * Core authentication configuration interface. * * Defines the base structure for authentication settings including paths, * providers, navigation functions, and feature flags. */ export interface AuthConfig<TAuthClient extends AuthClient = AuthClient> { /** * The Better Auth client instance used for authentication operations. */ authClient: TAuthClient /** * Additional user fields rendered on sign-up and the user profile. * @remarks `AdditionalFields` */ additionalFields?: AdditionalFields /** * Avatar upload, optimization, and deletion configuration. * @remarks `AvatarConfig` * @default { enabled: true, resize: resizeAvatar, size: 256, extension: "png" } */ avatar: AvatarConfig /** * Base paths for different application sections * @remarks `BasePaths` */ basePaths: BasePaths /** * Base URL for API endpoints (optional) * @default "" */ baseURL: string /** * Email and password authentication configuration * @remarks `EmailAndPasswordConfig` */ emailAndPassword: EmailAndPasswordConfig /** * Localization strings for UI components * @remarks `Localization` */ localization: Localization /** * Language metadata and translated messages for all auth components. * @remarks `AuthLocale` * @default `defaultAuthLocale` */ locale: AuthLocale /** * Registered auth plugins. UI packages widen the element type via the * `AuthPluginRegister` module-augmentation slot. * @remarks `AuthPlugin[]` * @default [] */ plugins: AuthPlugin[] /** * Default redirect path after successful authentication * @default "/" */ redirectTo: string /** * Allow users to link multiple accounts from the same social provider. * When false, providers already linked to the account are hidden from the available-to-link list. * @default true */ multipleAccountsPerProvider?: boolean /** * Allow the UI to unlink the final sign-in account. * * Enable this only when Better Auth's * `account.accountLinking.allowUnlinkingAll` option is also enabled. * @default false */ allowUnlinkingAllAccounts?: boolean /** * List of enabled social authentication providers * Built-in providers can use their string ID. Custom and Generic OAuth * providers use `{ id, label, icon }`. * @remarks `AuthSocialProvider[]` */ socialProviders?: AuthSocialProvider[] /** * How social sign-in opens the provider flow. * Popup mode requires Better Auth's experimental `oauthPopupClient` action. * @default "redirect" */ socialSignInMode: SocialSignInMode /** * View path mappings for different authentication views * @remarks `ViewPaths` */ viewPaths: ViewPaths /** * Function to navigate to a new path * @param options - Navigation options with href and optional replace flag * @default window.location.href = href (or window.location.replace if replace: true) * @example * // TanStack Router * navigate={navigate} * // Next.js * navigate={({href, replace}) => replace ? router.replace(href) : router.push(href)} */ navigate: (options: { to: string; replace?: boolean }) => void } export const defaultAuthConfig: Omit<AuthConfig, "authClient"> = { allowUnlinkingAllAccounts: false, avatar: { enabled: true, resize: resizeAvatar, size: 256, extension: "png" }, basePaths, baseURL: "", emailAndPassword: { enabled: true, forgotPassword: true, name: true, rememberMe: false, strengthMeter: true, minPasswordLength: 8, maxPasswordLength: 128 }, plugins: [], redirectTo: "/", socialSignInMode: "redirect", viewPaths, localization, locale: defaultAuthLocale, navigate: ({ to, replace }) => { if (replace) { window.location.replace(to) } else { window.location.href = to } } } export type AuthConfigOptions<TAuthClient extends AuthClient = AuthClient> = DeepPartial<Omit<AuthConfig<TAuthClient>, "authClient" | "locale">> & { authClient: TAuthClient /** Imported locale bundle. Defaults to English (`en-US`). */ locale?: AuthLocale } /** Resolves defaults, locale messages, plugin messages, and consumer overrides. */ export function resolveAuthConfig<TAuthClient extends AuthClient = AuthClient>( options: AuthConfigOptions<TAuthClient> ): AuthConfig<TAuthClient> { const locale = options.locale ?? defaultAuthLocale const resolvedLocalization = deepmerge( deepmerge(localization, locale.localization), options.localization ?? {} ) const plugins = localizeAuthPlugins(options.plugins ?? [], locale) const mergedConfig = deepmerge<Omit<AuthConfig<TAuthClient>, "authClient">>( defaultAuthConfig as Omit<AuthConfig<TAuthClient>, "authClient">, { ...options, locale, localization: resolvedLocalization, plugins } ) const fieldsByName = new Map<string, AdditionalField>() for (const plugin of plugins) { for (const field of plugin.additionalFields ?? []) { fieldsByName.set(field.name, field) } } for (const field of mergedConfig.additionalFields ?? []) { fieldsByName.set(field.name, field) } mergedConfig.additionalFields = Array.from(fieldsByName.values()) return { ...mergedConfig, authClient: options.authClient } }