@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 21.4 kB
Source Map (JSON)
{"version":3,"file":"use-session.cjs","sources":["../../../src/hooks/use-session.ts"],"sourcesContent":["/**\n * @frank-auth/react - useSession Hook\n *\n * Session management hook that provides access to session operations,\n * multi-session handling, and session security features.\n */\n\nimport {useCallback, useEffect, useMemo, useState} from 'react';\n\nimport type {Session, SessionInfo} from '@frank-auth/client';\n\nimport {useAuth} from './use-auth';\nimport {useConfig} from '../provider/config-provider';\n\nimport type {AuthError} from '../provider/types';\n\n// ============================================================================\n// Session Hook Interface\n// ============================================================================\n\nexport interface UseSessionReturn {\n // Session state\n session: Session | null;\n sessions: SessionInfo[];\n isLoaded: boolean;\n isLoading: boolean;\n error: AuthError | null;\n\n // Session management\n createSession: (token: string) => Promise<Session>;\n setActiveSession: (sessionId: string) => Promise<void>;\n refreshSession: () => Promise<Session | null>;\n revokeSession: (sessionId: string) => Promise<void>;\n revokeAllSessions: (exceptCurrent?: boolean) => Promise<void>;\n endSession: () => Promise<void>;\n\n // Session information\n sessionId: string | null;\n sessionToken: string | null;\n expiresAt: Date | null;\n lastActiveAt: Date | null;\n\n // Session status\n isActive: boolean;\n isExpired: boolean;\n isExpiring: boolean; // Expires within 5 minutes\n timeUntilExpiry: number | null; // Minutes until expiry\n\n // Device information\n deviceInfo: DeviceInfo | null;\n\n // Security features\n isCurrentDevice: boolean;\n isTrustedDevice: boolean;\n\n // Multi-session support\n hasMultipleSessions: boolean;\n sessionCount: number;\n otherSessions: SessionInfo[];\n}\n\nexport interface DeviceInfo {\n userAgent: string;\n browser: string;\n os: string;\n device: string;\n ipAddress: string;\n location?: {\n city?: string;\n country?: string;\n region?: string;\n };\n}\n\n// ============================================================================\n// Main useSession Hook\n// ============================================================================\n\n/**\n * Session management hook providing access to all session functionality\n *\n * @example Basic session management\n * ```tsx\n * import { useSession } from '@frank-auth/react';\n *\n * function SessionManager() {\n * const {\n * session,\n * sessions,\n * revokeSession,\n * revokeAllSessions,\n * isExpiring\n * } = useSession();\n *\n * if (isExpiring) {\n * return (\n * <div className=\"session-warning\">\n * <p>Your session is about to expire</p>\n * <button onClick={refreshSession}>Extend Session</button>\n * </div>\n * );\n * }\n *\n * return (\n * <div>\n * <h3>Active Sessions ({sessions.length})</h3>\n * {sessions.map((session) => (\n * <div key={session.id}>\n * <p>{session.deviceInfo?.browser} on {session.deviceInfo?.os}</p>\n * <p>Last active: {session.lastActiveAt}</p>\n * <button onClick={() => revokeSession(session.id)}>\n * Revoke Session\n * </button>\n * </div>\n * ))}\n * <button onClick={() => revokeAllSessions(true)}>\n * Revoke All Other Sessions\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @example Session expiry warning\n * ```tsx\n * function SessionExpiryWarning() {\n * const { isExpiring, timeUntilExpiry, refreshSession } = useSession();\n *\n * if (!isExpiring || !timeUntilExpiry) return null;\n *\n * return (\n * <div className=\"alert alert-warning\">\n * <p>Session expires in {timeUntilExpiry} minutes</p>\n * <button onClick={refreshSession}>\n * Extend Session\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSession(): UseSessionReturn {\n const {session, sdk, createSession: authCreateSession, reload, userType} = useAuth();\n const {apiUrl, publishableKey} = useConfig();\n\n const [sessions, setSessions] = useState<SessionInfo[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Error handler\n const handleError = useCallback((err: any) => {\n const authError: AuthError = {\n code: err.code || 'UNKNOWN_ERROR',\n message: err.message || 'An unknown error occurred',\n details: err.details,\n field: err.field,\n };\n setError(authError);\n throw authError;\n }, []);\n\n // Load sessions on mount and session change\n const loadSessions = useCallback(async () => {\n try {\n setIsLoading(true);\n setError(null);\n\n const sessionsData = await sdk.session.listSessions({fields: []});\n setSessions(sessionsData.data as any);\n } catch (err) {\n console.error('Failed to load sessions:', err);\n setError({\n code: 'SESSIONS_LOAD_FAILED',\n message: 'Failed to load sessions',\n });\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session]);\n\n useEffect(() => {\n loadSessions();\n }, [loadSessions]);\n\n // Session management methods\n const createSession = useCallback(async (token: string): Promise<Session> => {\n return authCreateSession(token);\n }, [authCreateSession]);\n\n const setActiveSession = useCallback(async (sessionId: string): Promise<void> => {\n try {\n setIsLoading(true);\n setError(null);\n\n sdk.session.activeSession = sessionId;\n await reload(); // Refresh auth state\n await loadSessions(); // Refresh sessions list\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session, reload, loadSessions, handleError]);\n\n const refreshSession = useCallback(async (): Promise<Session | null> => {\n try {\n setIsLoading(true);\n setError(null);\n\n const refreshedSession = await sdk.session.refreshSession();\n await reload(); // Refresh auth state\n\n return refreshedSession;\n } catch (err) {\n handleError(err);\n return null;\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session, reload, handleError]);\n\n const revokeSession = useCallback(async (sessionId: string): Promise<void> => {\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.session.revokeSession(sessionId);\n\n // If we revoked the current session, reload auth state\n if (sessionId === session?.id) {\n await reload();\n } else {\n // Just refresh the sessions list\n await loadSessions();\n }\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session, session?.id, reload, loadSessions, handleError]);\n\n const revokeAllSessions = useCallback(async (exceptCurrent = false): Promise<void> => {\n try {\n setIsLoading(true);\n setError(null);\n\n await sdk.session.revokeAllSessions({\n exceptCurrent,\n });\n\n if (!exceptCurrent) {\n // All sessions revoked, user is signed out\n await reload();\n } else {\n // Only other sessions revoked, refresh sessions list\n await loadSessions();\n }\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session, reload, loadSessions, handleError]);\n\n const endSession = useCallback(async (): Promise<void> => {\n try {\n setIsLoading(true);\n setError(null);\n\n // await frankSession.endSession();\n await reload(); // This will sign out the user\n } catch (err) {\n handleError(err);\n } finally {\n setIsLoading(false);\n }\n }, [sdk.session, reload, handleError]);\n\n // Session information\n const sessionId = useMemo(() => session?.id || null, [session]);\n const sessionToken = useMemo(() => session?.accessToken || null, [session]);\n const expiresAt = useMemo(() =>\n session?.expiresAt ? new Date(session.expiresAt) : null,\n [session]\n );\n const lastActiveAt = useMemo(() =>\n session?.lastActiveAt ? new Date(session.lastActiveAt) : null,\n [session]\n );\n\n // Session status\n const isActive = useMemo(() => !!session && !session.expired, [session]);\n const isExpired = useMemo(() => {\n if (!expiresAt) return false;\n return expiresAt.getTime() <= Date.now();\n }, [expiresAt]);\n\n const isExpiring = useMemo(() => {\n if (!expiresAt) return false;\n const fiveMinutesFromNow = Date.now() + (5 * 60 * 1000);\n return expiresAt.getTime() <= fiveMinutesFromNow && !isExpired;\n }, [expiresAt, isExpired]);\n\n const timeUntilExpiry = useMemo(() => {\n if (!expiresAt) return null;\n const msUntilExpiry = expiresAt.getTime() - Date.now();\n return Math.max(0, Math.floor(msUntilExpiry / 60000)); // Convert to minutes\n }, [expiresAt]);\n\n // Device information\n const deviceInfo = useMemo((): DeviceInfo | null => {\n if (!session?.deviceInfo) return null;\n\n return {\n userAgent: session.deviceInfo.userAgent || '',\n browser: session.deviceInfo.browser || 'Unknown',\n os: session.deviceInfo.os || 'Unknown',\n device: session.deviceInfo.device || 'Unknown',\n ipAddress: session.deviceInfo.ipAddress || '',\n location: session.deviceInfo.location,\n };\n }, [session]);\n\n // Security features\n const isCurrentDevice = useMemo(() => {\n if (!session || !deviceInfo) return false;\n\n // Check if this is the current device by comparing user agent\n return typeof navigator !== 'undefined' &&\n deviceInfo.userAgent === navigator.userAgent;\n }, [session, deviceInfo]);\n\n const isTrustedDevice = useMemo(() =>\n session?.trustedDevice || false,\n [session]\n );\n\n // Multi-session support\n const hasMultipleSessions = useMemo(() => sessions.length > 1, [sessions]);\n const sessionCount = useMemo(() => sessions.length, [sessions]);\n const otherSessions = useMemo(() =>\n sessions.filter(s => s.id !== sessionId),\n [sessions, sessionId]\n );\n\n return {\n // Session state\n session,\n sessions,\n isLoaded: !!session,\n isLoading,\n error,\n\n // Session management\n createSession,\n setActiveSession,\n refreshSession,\n revokeSession,\n revokeAllSessions,\n endSession,\n\n // Session information\n sessionId,\n sessionToken,\n expiresAt,\n lastActiveAt,\n\n // Session status\n isActive,\n isExpired,\n isExpiring,\n timeUntilExpiry,\n\n // Device information\n deviceInfo,\n\n // Security features\n isCurrentDevice,\n isTrustedDevice,\n\n // Multi-session support\n hasMultipleSessions,\n sessionCount,\n otherSessions,\n };\n}\n\n// ============================================================================\n// Specialized Session Hooks\n// ============================================================================\n\n/**\n * Hook for session status monitoring\n */\nexport function useSessionStatus() {\n const {\n isActive,\n isExpired,\n isExpiring,\n timeUntilExpiry,\n expiresAt,\n refreshSession,\n } = useSession();\n\n return {\n isActive,\n isExpired,\n isExpiring,\n timeUntilExpiry,\n expiresAt,\n refreshSession,\n status: isExpired ? 'expired' : isExpiring ? 'expiring' : isActive ? 'active' : 'inactive',\n };\n}\n\n/**\n * Hook for multi-session management\n */\nexport function useMultiSession() {\n const {\n sessions,\n sessionCount,\n otherSessions,\n hasMultipleSessions,\n revokeSession,\n revokeAllSessions,\n setActiveSession,\n isLoading,\n error,\n } = useSession();\n\n return {\n sessions,\n sessionCount,\n otherSessions,\n hasMultipleSessions,\n revokeSession,\n revokeAllSessions,\n setActiveSession,\n isLoading,\n error,\n revokeAllOthers: () => revokeAllSessions(true),\n };\n}\n\n/**\n * Hook for device and security information\n */\nexport function useSessionSecurity() {\n const {\n deviceInfo,\n isCurrentDevice,\n isTrustedDevice,\n sessionId,\n lastActiveAt,\n } = useSession();\n\n return {\n deviceInfo,\n isCurrentDevice,\n isTrustedDevice,\n sessionId,\n lastActiveAt,\n isSecure: isTrustedDevice && isCurrentDevice,\n };\n}\n\n// ============================================================================\n// Session Expiry Hook with Auto-refresh\n// ============================================================================\n\n/**\n * Hook that automatically handles session expiry and refresh\n */\nexport function useSessionExpiry(options: {\n autoRefresh?: boolean;\n refreshThreshold?: number; // Minutes before expiry to refresh\n onExpiry?: () => void;\n onExpiring?: () => void;\n} = {}) {\n const {\n autoRefresh = false,\n refreshThreshold = 5,\n onExpiry,\n onExpiring,\n } = options;\n\n const {\n isExpired,\n isExpiring,\n timeUntilExpiry,\n refreshSession,\n } = useSession();\n\n // Auto-refresh when approaching expiry\n useEffect(() => {\n if (autoRefresh && isExpiring && timeUntilExpiry && timeUntilExpiry <= refreshThreshold) {\n refreshSession().catch(console.error);\n }\n }, [autoRefresh, isExpiring, timeUntilExpiry, refreshThreshold, refreshSession]);\n\n // Handle expiry callback\n useEffect(() => {\n if (isExpired) {\n onExpiry?.();\n }\n }, [isExpired, onExpiry]);\n\n // Handle expiring callback\n useEffect(() => {\n if (isExpiring) {\n onExpiring?.();\n }\n }, [isExpiring, onExpiring]);\n\n return {\n isExpired,\n isExpiring,\n timeUntilExpiry,\n refreshSession,\n autoRefresh,\n };\n}\n"],"names":["useSession","session","sdk","authCreateSession","reload","userType","useAuth","apiUrl","publishableKey","useConfig","sessions","setSessions","useState","isLoading","setIsLoading","error","setError","handleError","useCallback","err","authError","loadSessions","sessionsData","useEffect","createSession","token","setActiveSession","sessionId","refreshSession","refreshedSession","revokeSession","revokeAllSessions","exceptCurrent","endSession","useMemo","sessionToken","expiresAt","lastActiveAt","isActive","isExpired","isExpiring","fiveMinutesFromNow","timeUntilExpiry","msUntilExpiry","deviceInfo","isCurrentDevice","isTrustedDevice","hasMultipleSessions","sessionCount","otherSessions","useSessionStatus","useMultiSession","useSessionSecurity","useSessionExpiry","options","autoRefresh","refreshThreshold","onExpiry","onExpiring"],"mappings":"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"}