UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

792 lines (697 loc) 19.7 kB
import type {JSONObject, XID} from './index'; import type {AuthMethod, OAuthProvider} from './auth'; import type {ComponentType, ReactNode} from 'react'; export type SizeT = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; export type RadiusT = 'none' | 'sm' | 'md' | 'lg' | 'xl'; export type ShadowT = 'none' | 'sm' | 'md' | 'lg' | 'xl'; // Main configuration interface export interface FrankAuthConfig { // Core settings publishableKey: string; apiUrl?: string; environment?: 'development' | 'staging' | 'production'; // Organization context organizationId?: XID; allowOrganizationSwitching?: boolean; // Authentication configuration authentication: AuthenticationConfig; // UI appearance configuration appearance: AppearanceConfig; // Localization configuration localization: LocalizationConfig; // Component configuration components: ComponentConfig; // Feature flags features: FeatureConfig; // Routing configuration routing: RoutingConfig; // Session configuration session: SessionConfig; // Custom configuration custom?: JSONObject; } // Authentication configuration export interface AuthenticationConfig { // Enabled authentication methods methods: AuthMethod[]; // Primary authentication method primaryMethod: AuthMethod; // Sign-in configuration signIn: SignInConfig; // Sign-up configuration signUp: SignUpConfig; // OAuth configuration oauth: OAuthConfig; // MFA configuration mfa: MFAConfig; // Passkey configuration passkeys: PasskeyConfig; // Password configuration password: PasswordConfig; // Verification configuration verification: VerificationConfig; } // Sign-in configuration export interface SignInConfig { enabled: boolean; allowedMethods: AuthMethod[]; defaultMethod: AuthMethod; showAlternativeMethods: boolean; allowRememberMe: boolean; requireEmailVerification: boolean; allowPasswordReset: boolean; redirectUrl?: string; afterSignInUrl?: string; maxAttempts?: number; lockoutDuration?: number; } // Sign-up configuration export interface SignUpConfig { enabled: boolean; allowedMethods: AuthMethod[]; defaultMethod: AuthMethod; requireEmailVerification: boolean; requirePhoneVerification: boolean; allowedFields: string[]; requiredFields: string[]; captchaEnabled: boolean; termsOfServiceUrl?: string; privacyPolicyUrl?: string; redirectUrl?: string; afterSignUpUrl?: string; } // OAuth configuration export interface OAuthConfig { enabled: boolean; providers: OAuthProviderConfig[]; allowAccountLinking: boolean; popupMode: boolean; redirectMode: 'popup' | 'redirect'; } export interface OAuthProviderConfig { provider: OAuthProvider; enabled: boolean; clientId?: string; scopes?: string[]; buttonText?: string; buttonIcon?: string; buttonStyle?: 'full' | 'icon' | 'minimal'; customParameters?: Record<string, string>; redirectUrl?: string; } // MFA configuration export interface MFAConfig { enabled: boolean; required: boolean; allowedMethods: ('totp' | 'sms' | 'email' | 'backup_codes')[]; defaultMethod: 'totp' | 'sms' | 'email'; backupCodesEnabled: boolean; gracePeriod?: number; rememberDevice: boolean; rememberDeviceDuration?: number; } // Passkey configuration export interface PasskeyConfig { enabled: boolean; allowRegistration: boolean; allowAuthentication: boolean; requireUserVerification: boolean; rpName: string; rpId?: string; timeout?: number; attestation?: 'none' | 'indirect' | 'direct'; } // Password configuration export interface PasswordConfig { enabled: boolean; minLength: number; maxLength: number; requireUppercase: boolean; requireLowercase: boolean; requireNumbers: boolean; requireSymbols: boolean; allowCommonPasswords: boolean; maxAge?: number; historyCount?: number; showStrengthIndicator: boolean; allowPasswordManager: boolean; } // Verification configuration export interface VerificationConfig { email: { enabled: boolean; required: boolean; allowResend: boolean; resendCooldown: number; expirationTime: number; template?: string; }; phone: { enabled: boolean; required: boolean; allowResend: boolean; resendCooldown: number; expirationTime: number; allowedCountries?: string[]; blockedCountries?: string[]; }; } // Appearance configuration export interface AppearanceConfig { // Theme configuration theme: ThemeConfig; // Layout configuration layout: LayoutConfig; // Branding configuration branding: BrandingConfig; // Component styling elements: ElementConfig; // Color scheme colorScheme: 'light' | 'dark' | 'system'; // Custom CSS customCSS?: string; } // Theme configuration export interface ThemeConfig { primary: string; secondary: string; accent: string; background: string; foreground: string; muted: string; border: string; input: string; ring: string; destructive: string; warning: string; success: string; info: string; } // Layout configuration export interface LayoutConfig { containerMaxWidth: string sidebarWidth: string headerHeight: string footerHeight: string contentPadding: string // Card layout card: { width: 'sm' | 'md' | 'lg' | 'xl' | 'full'; padding: 'sm' | 'md' | 'lg'; rounded: 'none' | 'sm' | 'md' | 'lg' | 'xl'; shadow: 'none' | 'sm' | 'md' | 'lg' | 'xl'; border: boolean; }; // Modal layout modal: { size: 'sm' | 'md' | 'lg' | 'xl' | 'full'; overlay: boolean; closeOnOverlayClick: boolean; showCloseButton: boolean; }; // Form layout form: { spacing: 'sm' | 'md' | 'lg'; labelPosition: 'top' | 'left' | 'floating'; buttonSize: 'sm' | 'md' | 'lg'; buttonWidth: 'auto' | 'full'; }; } // Branding configuration export interface BrandingConfig { logo?: string; logoUrl?: string; favicon?: string; applicationName?: string; tagline?: string; supportEmail?: string; supportUrl?: string; termsOfServiceUrl?: string; privacyPolicyUrl?: string; showPoweredBy: boolean; } // Element styling configuration export interface ElementConfig { // Global styles fontFamily?: string; fontSize?: 'xs' | 'sm' | 'base' | 'lg' | 'xl'; // Button styles button?: { primary?: string; secondary?: string; outline?: string; ghost?: string; destructive?: string; }; // Input styles input?: { base?: string; focus?: string; error?: string; disabled?: string; }; // Card styles card?: { base?: string; header?: string; body?: string; footer?: string; }; // Form styles form?: { label?: string; error?: string; helper?: string; }; // Link styles link?: { base?: string; hover?: string; visited?: string; }; } // Localization configuration export interface LocalizationConfig { // Default locale defaultLocale: string; // Available locales availableLocales: string[]; // Allow locale switching allowLocaleSwitching: boolean; // Custom translations customTranslations?: Record<string, Record<string, string>>; // Date/time formatting dateFormat: string; timeFormat: string; timezone?: string; // Number formatting numberFormat: { locale: string; currency?: string; }; } // Component configuration export interface ComponentConfig { // Global component props globalProps?: Record<string, any>; // Component overrides overrides?: ComponentOverrides; // Component slots slots?: ComponentSlots; // Custom components customComponents?: CustomComponents; } // Component overrides export interface ComponentOverrides { // Authentication components SignIn?: ComponentOverride; SignUp?: ComponentOverride; UserButton?: ComponentOverride; UserProfile?: ComponentOverride; // Organization components OrganizationSwitcher?: ComponentOverride; OrganizationProfile?: ComponentOverride; // Form components PasswordField?: ComponentOverride; EmailField?: ComponentOverride; PhoneField?: ComponentOverride; // Layout components AuthLayout?: ComponentOverride; ModalLayout?: ComponentOverride; CardLayout?: ComponentOverride; } export interface ComponentOverride { props?: Record<string, any>; className?: string; style?: Record<string, any>; replace?: ComponentType<any>; wrapper?: ComponentType<any>; } // Component slots export interface ComponentSlots { beforeSignIn?: ReactNode; afterSignIn?: ReactNode; beforeSignUp?: ReactNode; afterSignUp?: ReactNode; header?: ReactNode; footer?: ReactNode; sidebar?: ReactNode; loading?: ReactNode; error?: ReactNode; } // Custom components export interface CustomComponents { LoadingSpinner?: ComponentType<any>; ErrorBoundary?: ComponentType<any>; SuccessMessage?: ComponentType<any>; ErrorMessage?: ComponentType<any>; Avatar?: ComponentType<any>; Button?: ComponentType<any>; Input?: ComponentType<any>; Card?: ComponentType<any>; Modal?: ComponentType<any>; } // Feature configuration export interface FeatureConfig { // Authentication features signIn: boolean; signUp: boolean; userProfile: boolean; passwordReset: boolean; emailVerification: boolean; phoneVerification: boolean; // Organization features organizations: boolean; organizationSwitcher: boolean; organizationProfile: boolean; memberManagement: boolean; roleManagement: boolean; // Security features mfa: boolean; passkeys: boolean; sessionManagement: boolean; deviceManagement: boolean; auditLog: boolean; // Advanced features impersonation: boolean; bulkOperations: boolean; apiKeys: boolean; webhooks: boolean; // UI features darkMode: boolean; customBranding: boolean; localization: boolean; accessibility: boolean; } // Routing configuration export interface RoutingConfig { // Base paths basePath: string; signInPath: string; signUpPath: string; userProfilePath: string; organizationProfilePath: string; // Redirect URLs afterSignInUrl: string; afterSignUpUrl: string; afterSignOutUrl: string; // Protected routes protectedRoutes: string[]; publicRoutes: string[]; // Custom redirects customRedirects?: Record<string, string>; } // Session configuration export interface SessionConfig { // Session duration duration: number; // Inactivity timeout inactivityTimeout: number; // Refresh token rotation refreshTokenRotation: boolean; // Session storage storage: 'cookie' | 'localStorage' | 'sessionStorage'; // Cookie configuration cookie: { name: string; domain?: string; secure: boolean; sameSite: 'strict' | 'lax' | 'none'; httpOnly: true; }; // Session monitoring monitoring: { enabled: boolean; trackActivity: boolean; trackLocation: boolean; trackDevice: boolean; }; } // Server-side configuration export interface ServerConfig { // Organization-specific configuration organizationConfig?: OrganizationSpecificConfig; // Feature flags featureFlags?: Record<string, boolean>; // Branding overrides brandingOverrides?: BrandingConfig; // Authentication policies authenticationPolicies?: AuthenticationPolicies; // UI customization uiCustomization?: UICustomization; } // Organization-specific configuration export interface OrganizationSpecificConfig { organizationId: XID; customDomain?: string; customBranding?: BrandingConfig; authenticationMethods?: AuthMethod[]; mfaRequired?: boolean; allowedEmailDomains?: string[]; customCSS?: string; customComponents?: CustomComponents; featureOverrides?: Partial<FeatureConfig>; } // Authentication policies export interface AuthenticationPolicies { passwordPolicy: { minLength: number; maxLength: number; requireUppercase: boolean; requireLowercase: boolean; requireNumbers: boolean; requireSymbols: boolean; preventReuse: number; maxAge?: number; }; sessionPolicy: { maxDuration: number; inactivityTimeout: number; maxConcurrentSessions: number; requireReauth: boolean; }; mfaPolicy: { required: boolean; allowedMethods: string[]; gracePeriod?: number; }; lockoutPolicy: { enabled: boolean; maxAttempts: number; lockoutDuration: number; }; } // UI customization export interface UICustomization { // Custom CSS customCSS?: string; // Custom components customComponents?: Record<string, ComponentType<any>>; // Custom themes customThemes?: Record<string, ThemeConfig>; // Custom layouts customLayouts?: Record<string, ComponentType<any>>; // Custom translations customTranslations?: Record<string, Record<string, string>>; } // Configuration validation export interface ConfigValidation { valid: boolean; errors: string[]; warnings: string[]; } // Configuration defaults export const DEFAULT_CONFIG: Partial<FrankAuthConfig> = { authentication: { methods: ['email', 'oauth'], primaryMethod: 'email', signIn: { enabled: true, allowedMethods: ['email', 'oauth'], defaultMethod: 'email', showAlternativeMethods: true, allowRememberMe: true, requireEmailVerification: false, allowPasswordReset: true, }, signUp: { enabled: true, allowedMethods: ['email'], defaultMethod: 'email', requireEmailVerification: true, requirePhoneVerification: false, allowedFields: ['firstName', 'lastName', 'email', 'password'], requiredFields: ['email', 'password'], captchaEnabled: false, }, oauth: { enabled: true, providers: [], allowAccountLinking: true, popupMode: true, redirectMode: 'popup', }, mfa: { enabled: true, required: false, allowedMethods: ['totp', 'sms'], defaultMethod: 'totp', backupCodesEnabled: true, rememberDevice: true, rememberDeviceDuration: 30 * 24 * 60 * 60 * 1000, // 30 days }, passkeys: { enabled: true, allowRegistration: true, allowAuthentication: true, requireUserVerification: true, rpName: 'Frank Auth', timeout: 60000, attestation: 'none', }, password: { enabled: true, minLength: 8, maxLength: 128, requireUppercase: true, requireLowercase: true, requireNumbers: true, requireSymbols: false, allowCommonPasswords: false, showStrengthIndicator: true, allowPasswordManager: true, }, verification: { email: { enabled: true, required: false, allowResend: true, resendCooldown: 60, expirationTime: 24 * 60 * 60 * 1000, // 24 hours }, phone: { enabled: true, required: false, allowResend: true, resendCooldown: 60, expirationTime: 10 * 60 * 1000, // 10 minutes }, }, }, appearance: { elements: {}, colorScheme: 'system', theme: { primary: 'hsl(262.1 83.3% 57.8%)', secondary: 'hsl(220 14.3% 95.9%)', accent: 'hsl(220 14.3% 95.9%)', background: 'hsl(0 0% 100%)', foreground: 'hsl(224 71.4% 4.1%)', muted: 'hsl(220 14.3% 95.9%)', border: 'hsl(220 13% 91%)', input: 'hsl(220 13% 91%)', ring: 'hsl(262.1 83.3% 57.8%)', destructive: 'hsl(0 84.2% 60.2%)', warning: 'hsl(38 92% 50%)', success: 'hsl(142 76% 36%)', info: 'hsl(204 94% 94%)', }, layout: { card: { width: 'md', padding: 'md', rounded: 'md', shadow: 'md', border: true, }, modal: { size: 'md', overlay: true, closeOnOverlayClick: true, showCloseButton: true, }, form: { spacing: 'md', labelPosition: 'top', buttonSize: 'md', buttonWidth: 'full', }, }, branding: { applicationName: 'Frank Auth', showPoweredBy: true, }, }, localization: { defaultLocale: 'en', availableLocales: ['en'], allowLocaleSwitching: false, dateFormat: 'MM/dd/yyyy', timeFormat: 'HH:mm', numberFormat: { locale: 'en-US', }, }, components: { globalProps: {}, overrides: {}, slots: {}, customComponents: {}, }, features: { signIn: true, signUp: true, userProfile: true, passwordReset: true, emailVerification: true, phoneVerification: false, organizations: true, organizationSwitcher: true, organizationProfile: true, memberManagement: true, roleManagement: true, mfa: true, passkeys: true, sessionManagement: true, deviceManagement: true, auditLog: false, impersonation: false, bulkOperations: false, apiKeys: false, webhooks: false, darkMode: true, customBranding: true, localization: true, accessibility: true, }, routing: { basePath: '/auth', signInPath: '/sign-in', signUpPath: '/sign-up', userProfilePath: '/user', organizationProfilePath: '/organization', afterSignInUrl: '/dashboard', afterSignUpUrl: '/welcome', afterSignOutUrl: '/', protectedRoutes: ['/dashboard', '/settings', '/profile'], publicRoutes: ['/sign-in', '/sign-up', '/forgot-password'], }, session: { duration: 24 * 60 * 60 * 1000, // 24 hours inactivityTimeout: 30 * 60 * 1000, // 30 minutes refreshTokenRotation: true, storage: 'cookie', cookie: { name: 'frank_session', secure: true, sameSite: 'lax', httpOnly: true, }, monitoring: { enabled: true, trackActivity: true, trackLocation: false, trackDevice: true, }, }, };