@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 21.7 kB
Source Map (JSON)
{"version":3,"file":"auth.cjs","sources":["../../../src/utils/auth.ts"],"sourcesContent":["import type {AuthMethod, Organization, Session, User, XID} from '../types';\n\n// Token utilities\nexport interface DecodedJWT {\n header: {\n alg: string;\n typ: string;\n kid?: string;\n };\n payload: {\n sub: XID;\n aud: string;\n iss: string;\n exp: number;\n iat: number;\n nbf?: number;\n jti?: XID;\n user_type?: string;\n organization_id?: XID;\n permissions?: string[];\n roles?: string[];\n mfa_verified?: boolean;\n device_id?: string;\n client_id?: string;\n };\n signature: string;\n}\n\nexport const decodeJWT = (token: string): DecodedJWT | null => {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n\n const header = JSON.parse(atob(parts[0]));\n const payload = JSON.parse(atob(parts[1]));\n const signature = parts[2];\n\n return { header, payload, signature };\n } catch {\n return null;\n }\n};\n\nexport const isTokenExpired = (token: string): boolean => {\n const decoded = decodeJWT(token);\n if (!decoded) return true;\n\n const now = Math.floor(Date.now() / 1000);\n return decoded.payload.exp < now;\n};\n\nexport const getTokenExpiration = (token: string): Date | null => {\n const decoded = decodeJWT(token);\n if (!decoded) return null;\n\n return new Date(decoded.payload.exp * 1000);\n};\n\nexport const getTokenTimeToExpiry = (token: string): number => {\n const decoded = decodeJWT(token);\n if (!decoded) return 0;\n\n const now = Math.floor(Date.now() / 1000);\n return Math.max(0, decoded.payload.exp - now);\n};\n\n// Session utilities\nexport const isSessionActive = (session: Session): boolean => {\n const now = new Date();\n const expiresAt = new Date(session.expiresAt);\n return session.isActive && expiresAt > now;\n};\n\nexport const getSessionTimeRemaining = (session: Session): number => {\n const now = new Date();\n const expiresAt = new Date(session.expiresAt);\n return Math.max(0, expiresAt.getTime() - now.getTime());\n};\n\nexport const formatSessionDuration = (session: Session): string => {\n const remaining = getSessionTimeRemaining(session);\n if (remaining === 0) return 'Expired';\n\n const hours = Math.floor(remaining / (1000 * 60 * 60));\n const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));\n\n if (hours > 0) {\n return `${hours}h ${minutes}m`;\n }\n return `${minutes}m`;\n};\n\n// User utilities\nexport const getUserDisplayName = (user: User): string => {\n if (user.fullName) return user.fullName;\n if (user.firstName && user.lastName) return `${user.firstName} ${user.lastName}`;\n if (user.firstName) return user.firstName;\n if (user.username) return user.username;\n if (user.emailAddress) return user.emailAddress;\n return 'Unknown User';\n};\n\nexport const getUserInitials = (user: User): string => {\n const displayName = getUserDisplayName(user);\n return displayName\n .split(' ')\n .map(name => name.charAt(0).toUpperCase())\n .slice(0, 2)\n .join('');\n};\n\nexport const isUserVerified = (user: User): boolean => {\n return user.emailVerified && (user.phoneNumber ? user.phoneVerified : true);\n};\n\nexport const getUserVerificationStatus = (user: User): {\n isVerified: boolean;\n emailVerified: boolean;\n phoneVerified: boolean;\n missingVerifications: string[];\n} => {\n const missingVerifications: string[] = [];\n\n if (!user.emailVerified) missingVerifications.push('email');\n if (user.phoneNumber && !user.phoneVerified) missingVerifications.push('phone');\n\n return {\n isVerified: missingVerifications.length === 0,\n emailVerified: user.emailVerified,\n phoneVerified: user.phoneVerified,\n missingVerifications,\n };\n};\n\nexport const canUserAccessOrganization = (\n user: User,\n organizationId: XID\n): boolean => {\n if (!user.organizations) return false;\n return user.organizations.some(org => org.id === organizationId);\n};\n\nexport const getUserRoleInOrganization = (\n user: User,\n organizationId: XID\n): string | null => {\n if (!user.organizations) return null;\n\n const org = user.organizations.find(org => org.id === organizationId);\n return org?.userRole || null;\n};\n\nexport const getUserPermissionsInOrganization = (\n user: User,\n organizationId: XID\n): string[] => {\n if (!user.organizations) return [];\n\n const org = user.organizations.find(org => org.id === organizationId);\n return org?.userPermissions || [];\n};\n\nexport const hasPermission = (\n user: User,\n permission: string,\n organizationId?: XID\n): boolean => {\n if (!user.permissions) return false;\n\n return user.permissions.some(perm => {\n if (perm.permissionName !== permission) return false;\n\n if (organizationId) {\n return perm.contextType === 'organization' && perm.contextId === organizationId;\n }\n\n return perm.contextType === 'system';\n });\n};\n\nexport const hasRole = (\n user: User,\n role: string,\n organizationId?: XID\n): boolean => {\n if (!user.roles) return false;\n\n return user.roles.some(userRole => {\n if (userRole.roleName !== role) return false;\n\n if (organizationId) {\n return userRole.contextType === 'organization' && userRole.contextId === organizationId;\n }\n\n return userRole.contextType === 'system';\n });\n};\n\n// Organization utilities\nexport const isUserOrganizationOwner = (\n user: User,\n organization: Organization\n): boolean => {\n return organization.ownerId === user.id;\n};\n\nexport const canUserManageOrganization = (\n user: User,\n organization: Organization\n): boolean => {\n return (\n isUserOrganizationOwner(user, organization) ||\n hasRole(user, 'admin', organization.id) ||\n hasPermission(user, 'organization:manage', organization.id)\n );\n};\n\nexport const canUserInviteMembers = (\n user: User,\n organization: Organization\n): boolean => {\n return (\n isUserOrganizationOwner(user, organization) ||\n hasRole(user, 'admin', organization.id) ||\n hasPermission(user, 'members:invite', organization.id)\n );\n};\n\n// Authentication flow utilities\nexport const getRequiredAuthMethods = (methods: AuthMethod[]): AuthMethod[] => {\n const priority: Record<AuthMethod, number> = {\n 'passkey': 1,\n 'oauth': 2,\n 'email': 3,\n 'phone': 4,\n 'username': 5,\n 'mfa': 6,\n 'magic_link': 7,\n };\n\n return methods.sort((a, b) => priority[a] - priority[b]);\n};\n\nexport const isAuthMethodAvailable = (\n method: AuthMethod,\n enabledMethods: AuthMethod[]\n): boolean => {\n return enabledMethods.includes(method);\n};\n\nexport const getAuthMethodDisplayName = (method: AuthMethod): string => {\n const displayNames: Record<AuthMethod, string> = {\n 'email': 'Email',\n 'phone': 'Phone',\n 'username': 'Username',\n 'oauth': 'Social Login',\n 'passkey': 'Passkey',\n 'magic_link': 'Magic Link',\n 'mfa': 'Multi-Factor Authentication',\n };\n\n return displayNames[method] || method;\n};\n\n// Password utilities\nexport const validatePasswordStrength = (password: string): {\n score: number;\n feedback: string[];\n isValid: boolean;\n} => {\n const feedback: string[] = [];\n let score = 0;\n\n // Length check\n if (password.length >= 8) {\n score += 1;\n } else {\n feedback.push('Password must be at least 8 characters long');\n }\n\n // Character variety checks\n if (/[a-z]/.test(password)) score += 1;\n else feedback.push('Include lowercase letters');\n\n if (/[A-Z]/.test(password)) score += 1;\n else feedback.push('Include uppercase letters');\n\n if (/\\d/.test(password)) score += 1;\n else feedback.push('Include numbers');\n\n if (/[^A-Za-z0-9]/.test(password)) score += 1;\n else feedback.push('Include special characters');\n\n // Additional complexity checks\n if (password.length >= 12) score += 1;\n if (/(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/.test(password)) score += 1;\n\n return {\n score: Math.min(score, 5),\n feedback,\n isValid: score >= 4,\n };\n};\n\nexport const getPasswordStrengthLabel = (score: number): string => {\n const labels = ['Very Weak', 'Weak', 'Fair', 'Good', 'Strong'];\n return labels[Math.min(score, 4)] || 'Very Weak';\n};\n\nexport const isCommonPassword = (password: string): boolean => {\n const commonPasswords = [\n 'password', '123456', '123456789', 'qwerty', 'abc123',\n 'password123', 'admin', 'letmein', 'welcome', 'monkey',\n '1234567890', 'dragon', 'master', 'login', 'welcome123'\n ];\n\n return commonPasswords.includes(password.toLowerCase());\n};\n\n\nexport const formatBackupCode = (code: string): string => {\n return code.replace(/(.{4})/g, '$1-').replace(/-$/, '');\n};\n\nexport const validateTOTPCode = (code: string): boolean => {\n return /^\\d{6}$/.test(code);\n};\n\nexport const validateSMSCode = (code: string): boolean => {\n return /^\\d{4,8}$/.test(code);\n};\n\nexport const validateBackupCode = (code: string): boolean => {\n return /^[A-Z0-9]{8}$/.test(code.replace(/-/g, ''));\n};\n\n// Device and location utilities\nexport const getDeviceInfo = (): {\n deviceType: 'desktop' | 'mobile' | 'tablet' | 'unknown';\n browser?: string;\n operatingSystem?: string;\n userAgent: string;\n} => {\n const userAgent = navigator.userAgent;\n\n // Device type detection\n let deviceType: 'desktop' | 'mobile' | 'tablet' | 'unknown' = 'unknown';\n if (/Mobile|Android|iPhone|iPad/.test(userAgent)) {\n deviceType = /iPad/.test(userAgent) ? 'tablet' : 'mobile';\n } else if (/Windows|Mac|Linux/.test(userAgent)) {\n deviceType = 'desktop';\n }\n\n // Browser detection\n let browser: string | undefined;\n if (userAgent.includes('Chrome')) browser = 'Chrome';\n else if (userAgent.includes('Firefox')) browser = 'Firefox';\n else if (userAgent.includes('Safari')) browser = 'Safari';\n else if (userAgent.includes('Edge')) browser = 'Edge';\n\n // OS detection\n let operatingSystem: string | undefined;\n if (userAgent.includes('Windows')) operatingSystem = 'Windows';\n else if (userAgent.includes('Mac')) operatingSystem = 'macOS';\n else if (userAgent.includes('Linux')) operatingSystem = 'Linux';\n else if (userAgent.includes('Android')) operatingSystem = 'Android';\n else if (userAgent.includes('iOS')) operatingSystem = 'iOS';\n\n return {\n deviceType,\n browser,\n operatingSystem,\n userAgent,\n };\n};\n\n// Passkey utilities\nexport const isPasskeySupported = (): boolean => {\n return !!(\n window.PublicKeyCredential &&\n window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&\n window.PublicKeyCredential.isConditionalMediationAvailable\n );\n};\n\nexport const getPasskeySupport = async (): Promise<{\n supported: boolean;\n conditionalUI: boolean;\n platformAuthenticator: boolean;\n}> => {\n if (!window.PublicKeyCredential) {\n return {\n supported: false,\n conditionalUI: false,\n platformAuthenticator: false,\n };\n }\n\n const [conditionalUI, platformAuthenticator] = await Promise.all([\n window.PublicKeyCredential.isConditionalMediationAvailable?.() || Promise.resolve(false),\n window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable?.() || Promise.resolve(false),\n ]);\n\n return {\n supported: true,\n conditionalUI,\n platformAuthenticator,\n };\n};\n\n// URL and redirect utilities\nexport const buildRedirectUrl = (\n baseUrl: string,\n params: Record<string, string | undefined>\n): string => {\n const url = new URL(baseUrl);\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n\n return url.toString();\n};\n\nexport const parseAuthCallback = (url: string): {\n code?: string;\n state?: string;\n error?: string;\n error_description?: string;\n} => {\n const urlObj = new URL(url);\n\n return {\n code: urlObj.searchParams.get('code') || undefined,\n state: urlObj.searchParams.get('state') || undefined,\n error: urlObj.searchParams.get('error') || undefined,\n error_description: urlObj.searchParams.get('error_description') || undefined,\n };\n};\n\nexport const generateRandomState = (): string => {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n};\n\n// Security utilities\nexport const isSecureContext = (): boolean => {\n return window.isSecureContext;\n};\n\nexport const requireSecureContext = (): void => {\n if (!isSecureContext()) {\n throw new Error('This operation requires a secure context (HTTPS)');\n }\n};\n\nexport const sanitizeRedirectUrl = (url: string, allowedDomains: string[]): string | null => {\n try {\n const urlObj = new URL(url);\n\n if (allowedDomains.some(domain => urlObj.hostname === domain || urlObj.hostname.endsWith(`.${domain}`))) {\n return url;\n }\n\n return null;\n } catch {\n return null;\n }\n};\n\n// Event utilities\nexport const createAuthEvent = (\n type: string,\n data: Record<string, any> = {}\n): CustomEvent => {\n return new CustomEvent(`frank-auth:${type}`, {\n detail: {\n timestamp: new Date().toISOString(),\n ...data,\n },\n });\n};\n\nexport const dispatchAuthEvent = (\n type: string,\n data: Record<string, any> = {}\n): void => {\n const event = createAuthEvent(type, data);\n window.dispatchEvent(event);\n};"],"names":["decodeJWT","token","parts","header","payload","signature","isTokenExpired","decoded","now","getTokenExpiration","getTokenTimeToExpiry","isSessionActive","session","expiresAt","getSessionTimeRemaining","formatSessionDuration","remaining","hours","minutes","getUserDisplayName","user","getUserInitials","name","isUserVerified","getUserVerificationStatus","missingVerifications","canUserAccessOrganization","organizationId","org","getUserRoleInOrganization","getUserPermissionsInOrganization","hasPermission","permission","perm","hasRole","role","userRole","isUserOrganizationOwner","organization","canUserManageOrganization","canUserInviteMembers","getRequiredAuthMethods","methods","priority","a","b","isAuthMethodAvailable","method","enabledMethods","getAuthMethodDisplayName","validatePasswordStrength","password","feedback","score","getPasswordStrengthLabel","isCommonPassword","formatBackupCode","code","validateTOTPCode","validateSMSCode","validateBackupCode","getDeviceInfo","userAgent","deviceType","browser","operatingSystem","isPasskeySupported","getPasskeySupport","conditionalUI","platformAuthenticator","buildRedirectUrl","baseUrl","params","url","key","value","parseAuthCallback","urlObj","generateRandomState","array","byte","isSecureContext","requireSecureContext","sanitizeRedirectUrl","allowedDomains","domain","createAuthEvent","type","data","dispatchAuthEvent","event"],"mappings":"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"}