@rationalbloks/frontblok-auth 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -111,17 +111,10 @@ export declare class BaseApi {
111
111
  }>;
112
112
  getCurrentUser(): Promise<User>;
113
113
  /**
114
- * Authenticate with Google OAuth.
115
- *
116
- * SECURITY: Requires a nonce for CSRF protection.
117
- * Use generateOAuthNonce() before initiating Google Sign-In, then pass
118
- * the same nonce here to prevent CSRF attacks.
119
- *
120
- * @param credential - The Google ID token (JWT from Google Sign-In)
121
- * @param nonce - The CSRF protection nonce (must match what was set in Google Sign-In)
122
- * @returns GoogleOAuthResponse with tokens and user data
114
+ * Internal: Authenticate with Google OAuth.
115
+ * Use googleLogin() instead - THE ONE WAY.
123
116
  */
124
- googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse>;
117
+ private _googleOAuthLogin;
125
118
  /**
126
119
  * THE ONE WAY: Simplified Google OAuth login.
127
120
  *
@@ -1,2 +1,2 @@
1
- export { BaseApi, createAuthApi, getStoredUser, getStoredToken, isAuthenticated, generateOAuthNonce, getOAuthNonce, clearOAuthNonce } from './client';
1
+ export { BaseApi, createAuthApi, getStoredUser, getStoredToken, isAuthenticated, generateOAuthNonce } from './client';
2
2
  export type { User, ApiKey, ApiKeyCreateResponse, AuthResponse, GoogleOAuthResponse, PasswordResetRequestResponse, PasswordResetResponse, EmailVerificationResponse, SetPasswordResponse } from './types';
package/dist/index.cjs CHANGED
@@ -364,17 +364,10 @@ class BaseApi {
364
364
  // GOOGLE OAUTH AUTHENTICATION
365
365
  // ========================================================================
366
366
  /**
367
- * Authenticate with Google OAuth.
368
- *
369
- * SECURITY: Requires a nonce for CSRF protection.
370
- * Use generateOAuthNonce() before initiating Google Sign-In, then pass
371
- * the same nonce here to prevent CSRF attacks.
372
- *
373
- * @param credential - The Google ID token (JWT from Google Sign-In)
374
- * @param nonce - The CSRF protection nonce (must match what was set in Google Sign-In)
375
- * @returns GoogleOAuthResponse with tokens and user data
367
+ * Internal: Authenticate with Google OAuth.
368
+ * Use googleLogin() instead - THE ONE WAY.
376
369
  */
377
- async googleOAuthLogin(credential, nonce) {
370
+ async _googleOAuthLogin(credential, nonce) {
378
371
  console.log("[API] Google OAuth login attempt");
379
372
  const data = await this.request("/api/auth/google/login", {
380
373
  method: "POST",
@@ -426,7 +419,7 @@ class BaseApi {
426
419
  if (!nonce) {
427
420
  throw new Error("Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.");
428
421
  }
429
- const result = await this.googleOAuthLogin(credential, nonce);
422
+ const result = await this._googleOAuthLogin(credential, nonce);
430
423
  clearOAuthNonce();
431
424
  console.log("[API] OAuth nonce cleared (replay protection)");
432
425
  return result;
@@ -749,23 +742,18 @@ function createAuthProvider(api) {
749
742
  return AuthProvider;
750
743
  }
751
744
  function createAppRoot(App, config = {}) {
752
- const { googleClientId } = config;
745
+ const { googleClientId = "" } = config;
753
746
  const root = client.createRoot(document.getElementById("root"));
754
- let appElement = /* @__PURE__ */ jsxRuntime.jsx(App, {});
755
- if (googleClientId) {
756
- appElement = /* @__PURE__ */ jsxRuntime.jsx(google.GoogleOAuthProvider, { clientId: googleClientId, children: appElement });
757
- }
747
+ const appElement = /* @__PURE__ */ jsxRuntime.jsx(google.GoogleOAuthProvider, { clientId: googleClientId, children: /* @__PURE__ */ jsxRuntime.jsx(App, {}) });
758
748
  root.render(
759
749
  /* @__PURE__ */ jsxRuntime.jsx(react.StrictMode, { children: appElement })
760
750
  );
761
751
  }
762
752
  exports.BaseApi = BaseApi;
763
- exports.clearOAuthNonce = clearOAuthNonce;
764
753
  exports.createAppRoot = createAppRoot;
765
754
  exports.createAuthApi = createAuthApi;
766
755
  exports.createAuthProvider = createAuthProvider;
767
756
  exports.generateOAuthNonce = generateOAuthNonce;
768
- exports.getOAuthNonce = getOAuthNonce;
769
757
  exports.getStoredToken = getStoredToken;
770
758
  exports.getStoredUser = getStoredUser;
771
759
  exports.isAuthenticated = isAuthenticated;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/api/client.ts","../src/auth/AuthContext.tsx","../src/auth/AppProvider.tsx"],"sourcesContent":["// ========================================================================\r\n// BASE API CLIENT\r\n// Universal HTTP client with JWT token management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// Copy as-is when creating new applications.\r\n//\r\n// Features:\r\n// - JWT token storage and automatic refresh\r\n// - Cross-tab synchronization via localStorage events\r\n// - Proactive token refresh before expiration\r\n// - Automatic 401 retry with token refresh\r\n// - Rate limiting (429) retry with exponential backoff\r\n// - Authentication methods (login, register, logout)\r\n// - API key management for external integrations\r\n//\r\n// Usage:\r\n// 1. Extend this class for your application's API\r\n// 2. Add your domain-specific CRUD methods in the subclass\r\n// 3. Call super() in constructor with your API base URL\r\n//\r\n// ========================================================================\r\n\r\nimport type { \r\n User, \r\n ApiKey, \r\n ApiKeyCreateResponse, \r\n AuthResponse,\r\n GoogleOAuthResponse,\r\n PasswordResetRequestResponse,\r\n PasswordResetResponse,\r\n EmailVerificationResponse,\r\n SetPasswordResponse\r\n} from './types';\r\n\r\n// ========================================================================\r\n// OAUTH UTILITIES (defined before class so they can be used within it)\r\n// ========================================================================\r\n\r\n/**\r\n * Generate a cryptographically secure nonce for OAuth CSRF protection.\r\n * \r\n * USAGE:\r\n * 1. Call generateOAuthNonce() in component useState (runs once on mount)\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. On success, call authApi.googleLogin(credential) - it handles the rest\r\n * \r\n * @example\r\n * ```typescript\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} onSuccess={...} />\r\n * // On success: await authApi.googleLogin(credential);\r\n * ```\r\n * \r\n * @returns The generated nonce string\r\n */\r\nexport const generateOAuthNonce = (): string => {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n const nonce = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\r\n sessionStorage.setItem('google_oauth_nonce', nonce);\r\n console.log('[OAuth] Generated nonce for CSRF protection');\r\n return nonce;\r\n};\r\n\r\n/**\r\n * Retrieve the stored OAuth nonce.\r\n * @returns The stored nonce or null if not found\r\n */\r\nexport const getOAuthNonce = (): string | null => {\r\n return sessionStorage.getItem('google_oauth_nonce');\r\n};\r\n\r\n/**\r\n * Clear the stored OAuth nonce (call after successful login).\r\n */\r\nexport const clearOAuthNonce = (): void => {\r\n sessionStorage.removeItem('google_oauth_nonce');\r\n console.log('[OAuth] Cleared nonce');\r\n};\r\n\r\n// ========================================================================\r\n// BASE API CLASS\r\n// ========================================================================\r\n\r\n/**\r\n * BaseApi - Universal HTTP client with authentication.\r\n * \r\n * Extend this class to add your application-specific API methods.\r\n * \r\n * @example\r\n * ```typescript\r\n * class MyAppApi extends BaseApi {\r\n * constructor() {\r\n * super(import.meta.env.VITE_API_URL || 'https://api.myapp.com');\r\n * }\r\n * \r\n * async getProducts() {\r\n * return this.request<Product[]>('/api/products/');\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class BaseApi {\r\n protected token: string | null = null;\r\n protected refreshToken: string | null = null;\r\n protected isRefreshing: boolean = false;\r\n protected refreshPromise: Promise<boolean> | null = null;\r\n protected proactiveRefreshTimer: ReturnType<typeof setTimeout> | null = null;\r\n protected refreshCheckInterval: ReturnType<typeof setInterval> | null = null;\r\n protected tokenRefreshPromise: Promise<void> | null = null;\r\n protected readonly apiBaseUrl: string;\r\n\r\n constructor(apiBaseUrl: string) {\r\n this.apiBaseUrl = apiBaseUrl;\r\n this.loadTokens();\r\n this.setupVisibilityHandler();\r\n this.setupStorageListener();\r\n this.startRefreshCheckInterval();\r\n \r\n console.log(`[API] Base URL: ${this.apiBaseUrl}`);\r\n console.log(`[API] Environment: ${import.meta.env.DEV ? 'Development' : 'Production'}`);\r\n }\r\n\r\n // ========================================================================\r\n // TOKEN MANAGEMENT\r\n // ========================================================================\r\n\r\n /**\r\n * Sync tokens across browser tabs when localStorage changes.\r\n * Prevents \"stale refresh token\" issue where Tab A rotates the token\r\n * but Tab B still has the old (now invalid) refresh token in memory.\r\n */\r\n private setupStorageListener(): void {\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('storage', (event) => {\r\n if (event.key === 'auth_token') {\r\n console.log('[API] Token updated in another tab, syncing...');\r\n this.token = event.newValue;\r\n } else if (event.key === 'refresh_token') {\r\n console.log('[API] Refresh token updated in another tab, syncing...');\r\n this.refreshToken = event.newValue;\r\n } else if (event.key === null) {\r\n // Storage was cleared (logout in another tab)\r\n console.log('[API] Storage cleared in another tab, syncing logout...');\r\n this.token = null;\r\n this.refreshToken = null;\r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Start interval-based token check (runs every 60 seconds).\r\n * More reliable than setTimeout which browsers throttle in background tabs.\r\n */\r\n private startRefreshCheckInterval(): void {\r\n if (this.refreshCheckInterval) {\r\n clearInterval(this.refreshCheckInterval);\r\n }\r\n \r\n this.refreshCheckInterval = setInterval(() => {\r\n if (this.token) {\r\n this.checkAndRefreshToken();\r\n }\r\n }, 60 * 1000);\r\n }\r\n\r\n /**\r\n * Handle tab visibility changes - check token when user returns to tab.\r\n */\r\n private setupVisibilityHandler(): void {\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'visible' && this.token) {\r\n console.log('[API] Tab became visible, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n \r\n window.addEventListener('focus', () => {\r\n if (this.token) {\r\n console.log('[API] Window focused, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Check token expiry and refresh if needed.\r\n */\r\n protected async checkAndRefreshToken(): Promise<void> {\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 3 * 60 * 1000; // 3 minutes buffer\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token expired, refreshing immediately...');\r\n await this.refreshAccessToken();\r\n } else if (timeUntilExpiry <= refreshBuffer) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry / 1000)}s, refreshing proactively...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n\r\n /**\r\n * Parse JWT to get expiration time.\r\n */\r\n protected getTokenExpiry(token: string): number | null {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]));\r\n return payload.exp ? payload.exp * 1000 : null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Schedule proactive refresh 5 minutes before token expires.\r\n */\r\n protected scheduleProactiveRefresh(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 5 * 60 * 1000;\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token already expired, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n if (timeUntilExpiry <= refreshBuffer) {\r\n console.log('[API] Token expiring soon, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n const refreshIn = timeUntilExpiry - refreshBuffer;\r\n console.log(`[API] Scheduling proactive token refresh in ${Math.round(refreshIn / 60000)} minutes`);\r\n \r\n this.proactiveRefreshTimer = setTimeout(async () => {\r\n console.log('[API] Proactive token refresh triggered');\r\n await this.refreshAccessToken();\r\n }, refreshIn);\r\n }\r\n\r\n /**\r\n * Load tokens from localStorage on initialization.\r\n */\r\n protected loadTokens(): void {\r\n const storedToken = localStorage.getItem('auth_token');\r\n const storedRefreshToken = localStorage.getItem('refresh_token');\r\n \r\n if (storedToken) {\r\n this.token = storedToken;\r\n console.log('[API] Access token loaded from localStorage');\r\n }\r\n if (storedRefreshToken) {\r\n this.refreshToken = storedRefreshToken;\r\n console.log('[API] Refresh token loaded from localStorage');\r\n }\r\n \r\n if (this.token) {\r\n this.tokenRefreshPromise = this.checkAndRefreshToken();\r\n this.scheduleProactiveRefresh();\r\n }\r\n }\r\n\r\n /**\r\n * Wait for any pending token refresh to complete before making API calls.\r\n */\r\n async ensureTokenReady(): Promise<void> {\r\n if (this.tokenRefreshPromise) {\r\n await this.tokenRefreshPromise;\r\n this.tokenRefreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the refresh token.\r\n */\r\n async refreshAccessToken(): Promise<boolean> {\r\n if (this.isRefreshing) {\r\n return this.refreshPromise || Promise.resolve(false);\r\n }\r\n\r\n // Sync from localStorage before refresh (another tab may have rotated)\r\n const latestRefreshToken = localStorage.getItem('refresh_token');\r\n if (latestRefreshToken && latestRefreshToken !== this.refreshToken) {\r\n console.log('[API] Syncing refresh token from localStorage');\r\n this.refreshToken = latestRefreshToken;\r\n }\r\n\r\n if (!this.refreshToken) {\r\n console.warn('[API] No refresh token available');\r\n return false;\r\n }\r\n\r\n this.isRefreshing = true;\r\n this.refreshPromise = (async () => {\r\n try {\r\n console.log('[API] Refreshing access token...');\r\n const response = await fetch(`${this.apiBaseUrl}/api/auth/refresh`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n\r\n if (!response.ok) {\r\n console.error('[API] Token refresh failed:', response.status);\r\n if (response.status === 401 || response.status === 403) {\r\n console.log('[API] Refresh token is invalid, logging out...');\r\n this.clearAuth();\r\n } else {\r\n console.warn(`[API] Refresh failed with ${response.status}, will retry on next interval`);\r\n }\r\n return false;\r\n }\r\n\r\n const data = await response.json();\r\n \r\n this.token = data.access_token;\r\n localStorage.setItem('auth_token', data.access_token);\r\n \r\n if (data.refresh_token) {\r\n this.refreshToken = data.refresh_token;\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n console.log('[API] Refresh token rotated');\r\n }\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Access token refreshed successfully');\r\n return true;\r\n } catch (error) {\r\n console.error('[API] Token refresh network error:', error);\r\n console.warn('[API] Will retry refresh on next interval');\r\n return false;\r\n } finally {\r\n this.isRefreshing = false;\r\n this.refreshPromise = null;\r\n }\r\n })();\r\n\r\n return this.refreshPromise;\r\n }\r\n\r\n // ========================================================================\r\n // HTTP REQUEST METHOD\r\n // ========================================================================\r\n\r\n /**\r\n * Make an authenticated HTTP request.\r\n * Handles token refresh, 401 retry, and rate limiting automatically.\r\n * \r\n * This method is public so apps can make custom API calls without extending BaseApi.\r\n * \r\n * @example\r\n * const authApi = createAuthApi(API_URL);\r\n * const data = await authApi.request<MyType>('/api/my-endpoint/', { method: 'POST', body: JSON.stringify(payload) });\r\n */\r\n async request<T>(endpoint: string, options: RequestInit = {}, isRetry = false, retryCount = 0): Promise<T> {\r\n await this.ensureTokenReady();\r\n \r\n // Proactive token refresh before request\r\n if (this.token && !isRetry && !endpoint.includes('/auth/')) {\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (expiry) {\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n if (timeUntilExpiry < 2 * 60 * 1000) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry/1000)}s, refreshing before request...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n }\r\n \r\n const url = `${this.apiBaseUrl}${endpoint}`;\r\n \r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...(options.headers as Record<string, string>),\r\n };\r\n\r\n if (this.token) {\r\n headers.Authorization = `Bearer ${this.token}`;\r\n }\r\n\r\n console.log(`[API Request] ${options.method || 'GET'} ${url}`);\r\n \r\n try {\r\n const response = await fetch(url, { ...options, headers });\r\n \r\n console.log(`[API Response] ${response.status} ${response.statusText}`);\r\n \r\n if (!response.ok) {\r\n const error = await response.text();\r\n console.error(`[API Error] ${response.status}: ${error}`);\r\n \r\n // Rate limiting retry\r\n if (response.status === 429 && retryCount < 3) {\r\n const backoffMs = Math.pow(2, retryCount) * 1000;\r\n console.log(`[API] Rate limited (429), retrying in ${backoffMs}ms (attempt ${retryCount + 1}/3)...`);\r\n await new Promise(resolve => setTimeout(resolve, backoffMs));\r\n return this.request<T>(endpoint, options, isRetry, retryCount + 1);\r\n }\r\n \r\n // 401 retry with token refresh\r\n if (response.status === 401 && !isRetry && !endpoint.includes('/auth/login') && !endpoint.includes('/auth/register') && !endpoint.includes('/auth/refresh')) {\r\n console.log('[API] Attempting token refresh...');\r\n const refreshed = await this.refreshAccessToken();\r\n if (refreshed) {\r\n return this.request<T>(endpoint, options, true, retryCount);\r\n }\r\n console.warn('[API] Token refresh failed, returning error to caller');\r\n }\r\n \r\n const apiError = new Error(`API Error: ${response.status} - ${error}`) as Error & { status: number };\r\n apiError.status = response.status;\r\n throw apiError;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n console.error('[API Connection Error]:', error);\r\n if (error instanceof TypeError && error.message.includes('Failed to fetch')) {\r\n throw new Error(`Cannot connect to API: ${this.apiBaseUrl}`);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // ========================================================================\r\n // AUTHENTICATION METHODS\r\n // ========================================================================\r\n\r\n async login(email: string, password: string): Promise<AuthResponse> {\r\n console.log('[API] Login attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ email, password }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Login successful');\r\n return data;\r\n }\r\n\r\n async register(email: string, password: string, firstName: string, lastName: string): Promise<AuthResponse> {\r\n console.log('[API] Registration attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/register', {\r\n method: 'POST',\r\n body: JSON.stringify({ \r\n email, \r\n password, \r\n first_name: firstName, \r\n last_name: lastName \r\n }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Registration successful');\r\n return data;\r\n }\r\n\r\n async getMe(): Promise<{ user: User }> {\r\n console.log('[API] Fetching current user');\r\n return this.request<{ user: User }>('/api/auth/me');\r\n }\r\n\r\n async getCurrentUser(): Promise<User> {\r\n console.log('[API] Fetching current user data');\r\n const response = await this.request<{ user: User }>('/api/auth/me');\r\n localStorage.setItem('user', JSON.stringify(response.user));\r\n return response.user;\r\n }\r\n\r\n // ========================================================================\r\n // GOOGLE OAUTH AUTHENTICATION\r\n // ========================================================================\r\n\r\n /**\r\n * Authenticate with Google OAuth.\r\n * \r\n * SECURITY: Requires a nonce for CSRF protection.\r\n * Use generateOAuthNonce() before initiating Google Sign-In, then pass\r\n * the same nonce here to prevent CSRF attacks.\r\n * \r\n * @param credential - The Google ID token (JWT from Google Sign-In)\r\n * @param nonce - The CSRF protection nonce (must match what was set in Google Sign-In)\r\n * @returns GoogleOAuthResponse with tokens and user data\r\n */\r\n async googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login attempt');\r\n \r\n const data = await this.request<GoogleOAuthResponse>('/api/auth/google/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ credential, nonce }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);\r\n return data;\r\n }\r\n\r\n /**\r\n * THE ONE WAY: Simplified Google OAuth login.\r\n * \r\n * This is the DEFAULT method for Google Sign-In. It handles the complete\r\n * OAuth flow in one call following the Chain of Events mantra:\r\n * \r\n * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())\r\n * 2. Send credential + nonce to backend for verification\r\n * 3. Store JWT tokens on success\r\n * 4. Clear nonce to prevent replay attacks\r\n * \r\n * If ANY step fails, the entire chain fails. No partial states.\r\n * \r\n * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)\r\n * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag\r\n * @throws Error if nonce is missing or authentication fails\r\n * \r\n * @example\r\n * ```typescript\r\n * // In component: generate nonce ONCE on mount\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // Pass nonce to GoogleLogin, then on success:\r\n * const handleSuccess = async (response: CredentialResponse) => {\r\n * const result = await authApi.googleLogin(response.credential!);\r\n * // Done! Tokens stored, nonce cleared, user authenticated.\r\n * };\r\n * ```\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login (THE ONE WAY)');\r\n \r\n // Step 1: Retrieve nonce - MUST exist or we fail immediately\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.');\r\n }\r\n \r\n // Step 2: Authenticate with backend (uses internal googleOAuthLogin)\r\n const result = await this.googleOAuthLogin(credential, nonce);\r\n \r\n // Step 3: Clear nonce AFTER success to prevent replay attacks\r\n clearOAuthNonce();\r\n console.log('[API] OAuth nonce cleared (replay protection)');\r\n \r\n return result;\r\n }\r\n\r\n // ========================================================================\r\n // PASSWORD RESET FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Request a password reset email (forgot password flow).\r\n * \r\n * @param email - The email address to send reset link to\r\n * @returns Message confirming email was sent (or would be sent)\r\n */\r\n async requestPasswordReset(email: string): Promise<PasswordResetRequestResponse> {\r\n console.log('[API] Requesting password reset for:', email);\r\n \r\n return this.request<PasswordResetRequestResponse>('/api/auth/request-password-reset', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n /**\r\n * Reset password using token from email.\r\n * \r\n * @param token - The password reset token from the email link\r\n * @param newPassword - The new password (min 8 characters)\r\n * @returns Success message\r\n */\r\n async resetPassword(token: string, newPassword: string): Promise<PasswordResetResponse> {\r\n console.log('[API] Resetting password with token');\r\n \r\n return this.request<PasswordResetResponse>('/api/auth/reset-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ token, new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // EMAIL VERIFICATION FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Verify email address using token from verification email.\r\n * \r\n * @param token - The verification token from the email link\r\n * @returns Success message\r\n */\r\n async verifyEmail(token: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Verifying email with token');\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/verify-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ token }),\r\n });\r\n }\r\n\r\n /**\r\n * Request a new verification email.\r\n * \r\n * @param email - The email address to send verification to\r\n * @returns Message confirming email was sent\r\n */\r\n async requestVerificationEmail(email: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Requesting verification email for:', email);\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/request-verification-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // SET PASSWORD (FOR OAUTH-ONLY ACCOUNTS)\r\n // ========================================================================\r\n\r\n /**\r\n * Set password for OAuth-only accounts.\r\n * \r\n * Allows users who registered via Google OAuth to set a password\r\n * so they can also log in with email/password.\r\n * \r\n * @param newPassword - The password to set (min 8 characters)\r\n * @returns Success message\r\n */\r\n async setPassword(newPassword: string): Promise<SetPasswordResponse> {\r\n console.log('[API] Setting password for OAuth account');\r\n \r\n return this.request<SetPasswordResponse>('/api/auth/set-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // ACCOUNT MANAGEMENT\r\n // ========================================================================\r\n\r\n async deleteAccount(password: string, confirmText: string): Promise<{ message: string; note: string }> {\r\n console.log('[API] Deleting account (DESTRUCTIVE)');\r\n \r\n const response = await this.request<{ message: string; note: string }>('/api/auth/me', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ \r\n password, \r\n confirm_text: confirmText \r\n }),\r\n });\r\n \r\n this.clearAuth();\r\n console.log('[API] Account deleted successfully');\r\n return response;\r\n }\r\n\r\n async logout(): Promise<void> {\r\n console.log('[API] Logging out');\r\n \r\n if (this.refreshToken) {\r\n try {\r\n await this.request('/api/auth/logout', {\r\n method: 'POST',\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n console.log('[API] Refresh token revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke refresh token on server:', error);\r\n }\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n async logoutAllDevices(): Promise<void> {\r\n console.log('[API] Logging out from all devices');\r\n \r\n try {\r\n await this.request('/api/auth/logout-all', { method: 'POST' });\r\n console.log('[API] All sessions revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke all sessions:', error);\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n protected clearAuth(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n \r\n this.token = null;\r\n this.refreshToken = null;\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('refresh_token');\r\n localStorage.removeItem('user_data');\r\n console.log('[API] Auth cache cleared');\r\n \r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n\r\n // ========================================================================\r\n // API KEY MANAGEMENT\r\n // ========================================================================\r\n\r\n async listApiKeys(): Promise<{ api_keys: ApiKey[]; total: number }> {\r\n console.log('[API] Listing user API keys');\r\n return this.request('/api/auth/api-keys', { method: 'GET' });\r\n }\r\n\r\n async createApiKey(data: {\r\n name: string;\r\n scopes?: string;\r\n expires_in_days?: number;\r\n rate_limit_per_minute?: number;\r\n }): Promise<ApiKeyCreateResponse> {\r\n console.log(`[API] Creating API key: ${data.name}`);\r\n return this.request('/api/auth/api-keys', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n name: data.name,\r\n scopes: data.scopes || 'read,write',\r\n expires_in_days: data.expires_in_days,\r\n rate_limit_per_minute: data.rate_limit_per_minute || 60\r\n }),\r\n });\r\n }\r\n\r\n async revokeApiKey(keyId: string): Promise<{ message: string; key_prefix: string; revoked_at: string }> {\r\n console.log(`[API] Revoking API key: ${keyId}`);\r\n return this.request(`/api/auth/api-keys/${keyId}`, { method: 'DELETE' });\r\n }\r\n}\r\n\r\n// ========================================================================\r\n// FACTORY FUNCTION (RECOMMENDED)\r\n// ========================================================================\r\n// Use this instead of extending BaseApi. Cleaner, no inheritance.\r\n//\r\n// Usage:\r\n// import { createAuthApi } from '@rationalbloks/frontblok-auth';\r\n// export const authApi = createAuthApi(import.meta.env.VITE_API_URL);\r\n//\r\n// // Then use directly:\r\n// authApi.login(email, password);\r\n// authApi.logout();\r\n// authApi.listApiKeys();\r\n\r\n/**\r\n * Creates an auth API client instance.\r\n * Preferred over class inheritance - simpler and cleaner.\r\n * \r\n * @param apiBaseUrl - The backend API base URL\r\n * @returns A BaseApi instance with all auth methods\r\n */\r\nexport function createAuthApi(apiBaseUrl: string): BaseApi {\r\n return new BaseApi(apiBaseUrl);\r\n}\r\n\r\n// ========================================================================\r\n// STORAGE UTILITIES\r\n// ========================================================================\r\n\r\nexport const getStoredUser = (): User | null => {\r\n const userData = localStorage.getItem('user_data');\r\n if (!userData) return null;\r\n \r\n try {\r\n return JSON.parse(userData);\r\n } catch (error) {\r\n console.error('[API] Invalid user_data in localStorage:', error);\r\n localStorage.removeItem('user_data');\r\n return null;\r\n }\r\n};\r\n\r\nexport const getStoredToken = (): string | null => {\r\n return localStorage.getItem('auth_token');\r\n};\r\n\r\nexport const isAuthenticated = (): boolean => {\r\n return !!getStoredToken();\r\n};\r\n","// ========================================================================\r\n// AUTH CONTEXT\r\n// Universal authentication state management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// It provides:\r\n// - Authentication state (user, isLoading, isAuthenticated, error)\r\n// - Authentication actions (login, register, logout, clearError, refreshUser)\r\n// - React Context pattern for app-wide auth state\r\n// - Token expiration handling via 'auth:cleared' event\r\n// ========================================================================\r\n\r\nimport React, { createContext, useContext, useState, useEffect } from 'react';\r\nimport { getStoredUser, getStoredToken } from '../api';\r\nimport type { User } from '../api';\r\nimport type { BaseApi } from '../api/client';\r\n\r\n// ========================================================================\r\n// TYPES\r\n// ========================================================================\r\n\r\nexport interface AuthState {\r\n user: User | null;\r\n isLoading: boolean;\r\n isAuthenticated: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport interface AuthActions {\r\n login: (email: string, password: string) => Promise<boolean>;\r\n register: (email: string, password: string, firstName: string, lastName: string) => Promise<boolean>;\r\n logout: () => void;\r\n clearError: () => void;\r\n refreshUser: () => Promise<void>;\r\n}\r\n\r\nexport type AuthContextType = AuthState & AuthActions;\r\n\r\n// ========================================================================\r\n// CONTEXT\r\n// ========================================================================\r\n\r\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\r\n\r\n/**\r\n * Hook to access authentication state and actions.\r\n * Must be used within an AuthProvider.\r\n */\r\nexport const useAuth = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useAuth must be used within AuthProvider');\r\n }\r\n return context;\r\n};\r\n\r\n// ========================================================================\r\n// PROVIDER FACTORY\r\n// ========================================================================\r\n\r\n/**\r\n * Creates an AuthProvider component that uses the specified API instance.\r\n * This allows the universal auth context to work with any API that extends BaseApi.\r\n * \r\n * @example\r\n * ```typescript\r\n * // In your app's auth setup:\r\n * import { createAuthProvider } from '@/core/auth';\r\n * import { myAppApi } from '@/services/myAppApi';\r\n * \r\n * export const MyAppAuthProvider = createAuthProvider(myAppApi);\r\n * ```\r\n */\r\nexport function createAuthProvider(api: BaseApi) {\r\n const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\r\n const [state, setState] = useState<AuthState>({\r\n user: getStoredUser(),\r\n isLoading: false,\r\n isAuthenticated: !!getStoredToken(),\r\n error: null,\r\n });\r\n\r\n // Listen for auth:cleared event (token expiration, session invalidation)\r\n useEffect(() => {\r\n const handleAuthCleared = () => {\r\n console.log('[Auth] Session expired or invalidated - clearing auth state');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n window.addEventListener('auth:cleared', handleAuthCleared);\r\n return () => window.removeEventListener('auth:cleared', handleAuthCleared);\r\n }, []);\r\n\r\n // Refresh user data from backend on mount if authenticated\r\n useEffect(() => {\r\n const refreshUserData = async () => {\r\n const token = getStoredToken();\r\n if (token) {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n isAuthenticated: true,\r\n }));\r\n } catch (error: unknown) {\r\n console.error('[Auth] Failed to refresh user data:', error);\r\n const httpError = error as { status?: number };\r\n if (httpError?.status === 401 || httpError?.status === 403) {\r\n console.warn('[Auth] Auth failed - clearing local state only');\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('user_data');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n } else {\r\n console.warn('[Auth] Keeping user logged in with cached data');\r\n }\r\n }\r\n }\r\n };\r\n\r\n refreshUserData();\r\n }, []);\r\n\r\n const login = async (email: string, password: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.login(email, password);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Login failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const register = async (email: string, password: string, firstName: string, lastName: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.register(email, password, firstName, lastName);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Registration failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const logout = () => {\r\n api.logout();\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n const clearError = () => {\r\n setState(prev => ({ ...prev, error: null }));\r\n };\r\n\r\n const refreshUser = async () => {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n }));\r\n } catch (error) {\r\n console.error('[Auth] Failed to refresh user:', error);\r\n }\r\n };\r\n\r\n return (\r\n <AuthContext.Provider\r\n value={{\r\n ...state,\r\n login,\r\n register,\r\n logout,\r\n clearError,\r\n refreshUser,\r\n }}\r\n >\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n };\r\n\r\n return AuthProvider;\r\n}\r\n","// ========================================================================\r\n// CORE AUTH - App Provider Factory\r\n// ========================================================================\r\n// Creates a wrapped app component with OAuth providers configured.\r\n// This is the universal entry point for React apps with authentication.\r\n//\r\n// Usage:\r\n// import { createAppRoot } from './core/auth';\r\n// import App from './App';\r\n// createAppRoot(App, { googleClientId: '...' });\r\n// ========================================================================\r\n\r\nimport React, { StrictMode } from 'react';\r\nimport { createRoot } from 'react-dom/client';\r\nimport { GoogleOAuthProvider } from '@react-oauth/google';\r\n\r\nexport interface AuthConfig {\r\n /** Google OAuth Client ID (optional - only needed if using Google Sign-In) */\r\n googleClientId?: string;\r\n}\r\n\r\n/**\r\n * Creates and renders the app root with authentication providers.\r\n * This is the universal way to bootstrap a React app with auth.\r\n */\r\nexport function createAppRoot(\r\n App: React.ComponentType,\r\n config: AuthConfig = {}\r\n): void {\r\n const { googleClientId } = config;\r\n\r\n const root = createRoot(document.getElementById('root')!);\r\n\r\n // Build the component tree with optional providers\r\n let appElement: React.ReactElement = <App />;\r\n\r\n // Wrap with Google OAuth if client ID is provided\r\n if (googleClientId) {\r\n appElement = (\r\n <GoogleOAuthProvider clientId={googleClientId}>\r\n {appElement}\r\n </GoogleOAuthProvider>\r\n );\r\n }\r\n\r\n // Render with StrictMode\r\n root.render(\r\n <StrictMode>\r\n {appElement}\r\n </StrictMode>\r\n );\r\n}\r\n"],"names":["createContext","useContext","useState","useEffect","jsx","createRoot","GoogleOAuthProvider","StrictMode"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/api/client.ts","../src/auth/AuthContext.tsx","../src/auth/AppProvider.tsx"],"sourcesContent":["// ========================================================================\r\n// BASE API CLIENT\r\n// Universal HTTP client with JWT token management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// Copy as-is when creating new applications.\r\n//\r\n// Features:\r\n// - JWT token storage and automatic refresh\r\n// - Cross-tab synchronization via localStorage events\r\n// - Proactive token refresh before expiration\r\n// - Automatic 401 retry with token refresh\r\n// - Rate limiting (429) retry with exponential backoff\r\n// - Authentication methods (login, register, logout)\r\n// - API key management for external integrations\r\n//\r\n// Usage:\r\n// 1. Extend this class for your application's API\r\n// 2. Add your domain-specific CRUD methods in the subclass\r\n// 3. Call super() in constructor with your API base URL\r\n//\r\n// ========================================================================\r\n\r\nimport type { \r\n User, \r\n ApiKey, \r\n ApiKeyCreateResponse, \r\n AuthResponse,\r\n GoogleOAuthResponse,\r\n PasswordResetRequestResponse,\r\n PasswordResetResponse,\r\n EmailVerificationResponse,\r\n SetPasswordResponse\r\n} from './types';\r\n\r\n// ========================================================================\r\n// OAUTH UTILITIES (defined before class so they can be used within it)\r\n// ========================================================================\r\n\r\n/**\r\n * Generate a cryptographically secure nonce for OAuth CSRF protection.\r\n * \r\n * USAGE:\r\n * 1. Call generateOAuthNonce() in component useState (runs once on mount)\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. On success, call authApi.googleLogin(credential) - it handles the rest\r\n * \r\n * @example\r\n * ```typescript\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} onSuccess={...} />\r\n * // On success: await authApi.googleLogin(credential);\r\n * ```\r\n * \r\n * @returns The generated nonce string\r\n */\r\nexport const generateOAuthNonce = (): string => {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n const nonce = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\r\n sessionStorage.setItem('google_oauth_nonce', nonce);\r\n console.log('[OAuth] Generated nonce for CSRF protection');\r\n return nonce;\r\n};\r\n\r\n/**\r\n * Retrieve the stored OAuth nonce.\r\n * @returns The stored nonce or null if not found\r\n */\r\nexport const getOAuthNonce = (): string | null => {\r\n return sessionStorage.getItem('google_oauth_nonce');\r\n};\r\n\r\n/**\r\n * Clear the stored OAuth nonce (call after successful login).\r\n */\r\nexport const clearOAuthNonce = (): void => {\r\n sessionStorage.removeItem('google_oauth_nonce');\r\n console.log('[OAuth] Cleared nonce');\r\n};\r\n\r\n// ========================================================================\r\n// BASE API CLASS\r\n// ========================================================================\r\n\r\n/**\r\n * BaseApi - Universal HTTP client with authentication.\r\n * \r\n * Extend this class to add your application-specific API methods.\r\n * \r\n * @example\r\n * ```typescript\r\n * class MyAppApi extends BaseApi {\r\n * constructor() {\r\n * super(import.meta.env.VITE_API_URL || 'https://api.myapp.com');\r\n * }\r\n * \r\n * async getProducts() {\r\n * return this.request<Product[]>('/api/products/');\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class BaseApi {\r\n protected token: string | null = null;\r\n protected refreshToken: string | null = null;\r\n protected isRefreshing: boolean = false;\r\n protected refreshPromise: Promise<boolean> | null = null;\r\n protected proactiveRefreshTimer: ReturnType<typeof setTimeout> | null = null;\r\n protected refreshCheckInterval: ReturnType<typeof setInterval> | null = null;\r\n protected tokenRefreshPromise: Promise<void> | null = null;\r\n protected readonly apiBaseUrl: string;\r\n\r\n constructor(apiBaseUrl: string) {\r\n this.apiBaseUrl = apiBaseUrl;\r\n this.loadTokens();\r\n this.setupVisibilityHandler();\r\n this.setupStorageListener();\r\n this.startRefreshCheckInterval();\r\n \r\n console.log(`[API] Base URL: ${this.apiBaseUrl}`);\r\n console.log(`[API] Environment: ${import.meta.env.DEV ? 'Development' : 'Production'}`);\r\n }\r\n\r\n // ========================================================================\r\n // TOKEN MANAGEMENT\r\n // ========================================================================\r\n\r\n /**\r\n * Sync tokens across browser tabs when localStorage changes.\r\n * Prevents \"stale refresh token\" issue where Tab A rotates the token\r\n * but Tab B still has the old (now invalid) refresh token in memory.\r\n */\r\n private setupStorageListener(): void {\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('storage', (event) => {\r\n if (event.key === 'auth_token') {\r\n console.log('[API] Token updated in another tab, syncing...');\r\n this.token = event.newValue;\r\n } else if (event.key === 'refresh_token') {\r\n console.log('[API] Refresh token updated in another tab, syncing...');\r\n this.refreshToken = event.newValue;\r\n } else if (event.key === null) {\r\n // Storage was cleared (logout in another tab)\r\n console.log('[API] Storage cleared in another tab, syncing logout...');\r\n this.token = null;\r\n this.refreshToken = null;\r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Start interval-based token check (runs every 60 seconds).\r\n * More reliable than setTimeout which browsers throttle in background tabs.\r\n */\r\n private startRefreshCheckInterval(): void {\r\n if (this.refreshCheckInterval) {\r\n clearInterval(this.refreshCheckInterval);\r\n }\r\n \r\n this.refreshCheckInterval = setInterval(() => {\r\n if (this.token) {\r\n this.checkAndRefreshToken();\r\n }\r\n }, 60 * 1000);\r\n }\r\n\r\n /**\r\n * Handle tab visibility changes - check token when user returns to tab.\r\n */\r\n private setupVisibilityHandler(): void {\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'visible' && this.token) {\r\n console.log('[API] Tab became visible, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n \r\n window.addEventListener('focus', () => {\r\n if (this.token) {\r\n console.log('[API] Window focused, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Check token expiry and refresh if needed.\r\n */\r\n protected async checkAndRefreshToken(): Promise<void> {\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 3 * 60 * 1000; // 3 minutes buffer\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token expired, refreshing immediately...');\r\n await this.refreshAccessToken();\r\n } else if (timeUntilExpiry <= refreshBuffer) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry / 1000)}s, refreshing proactively...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n\r\n /**\r\n * Parse JWT to get expiration time.\r\n */\r\n protected getTokenExpiry(token: string): number | null {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]));\r\n return payload.exp ? payload.exp * 1000 : null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Schedule proactive refresh 5 minutes before token expires.\r\n */\r\n protected scheduleProactiveRefresh(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 5 * 60 * 1000;\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token already expired, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n if (timeUntilExpiry <= refreshBuffer) {\r\n console.log('[API] Token expiring soon, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n const refreshIn = timeUntilExpiry - refreshBuffer;\r\n console.log(`[API] Scheduling proactive token refresh in ${Math.round(refreshIn / 60000)} minutes`);\r\n \r\n this.proactiveRefreshTimer = setTimeout(async () => {\r\n console.log('[API] Proactive token refresh triggered');\r\n await this.refreshAccessToken();\r\n }, refreshIn);\r\n }\r\n\r\n /**\r\n * Load tokens from localStorage on initialization.\r\n */\r\n protected loadTokens(): void {\r\n const storedToken = localStorage.getItem('auth_token');\r\n const storedRefreshToken = localStorage.getItem('refresh_token');\r\n \r\n if (storedToken) {\r\n this.token = storedToken;\r\n console.log('[API] Access token loaded from localStorage');\r\n }\r\n if (storedRefreshToken) {\r\n this.refreshToken = storedRefreshToken;\r\n console.log('[API] Refresh token loaded from localStorage');\r\n }\r\n \r\n if (this.token) {\r\n this.tokenRefreshPromise = this.checkAndRefreshToken();\r\n this.scheduleProactiveRefresh();\r\n }\r\n }\r\n\r\n /**\r\n * Wait for any pending token refresh to complete before making API calls.\r\n */\r\n async ensureTokenReady(): Promise<void> {\r\n if (this.tokenRefreshPromise) {\r\n await this.tokenRefreshPromise;\r\n this.tokenRefreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the refresh token.\r\n */\r\n async refreshAccessToken(): Promise<boolean> {\r\n if (this.isRefreshing) {\r\n return this.refreshPromise || Promise.resolve(false);\r\n }\r\n\r\n // Sync from localStorage before refresh (another tab may have rotated)\r\n const latestRefreshToken = localStorage.getItem('refresh_token');\r\n if (latestRefreshToken && latestRefreshToken !== this.refreshToken) {\r\n console.log('[API] Syncing refresh token from localStorage');\r\n this.refreshToken = latestRefreshToken;\r\n }\r\n\r\n if (!this.refreshToken) {\r\n console.warn('[API] No refresh token available');\r\n return false;\r\n }\r\n\r\n this.isRefreshing = true;\r\n this.refreshPromise = (async () => {\r\n try {\r\n console.log('[API] Refreshing access token...');\r\n const response = await fetch(`${this.apiBaseUrl}/api/auth/refresh`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n\r\n if (!response.ok) {\r\n console.error('[API] Token refresh failed:', response.status);\r\n if (response.status === 401 || response.status === 403) {\r\n console.log('[API] Refresh token is invalid, logging out...');\r\n this.clearAuth();\r\n } else {\r\n console.warn(`[API] Refresh failed with ${response.status}, will retry on next interval`);\r\n }\r\n return false;\r\n }\r\n\r\n const data = await response.json();\r\n \r\n this.token = data.access_token;\r\n localStorage.setItem('auth_token', data.access_token);\r\n \r\n if (data.refresh_token) {\r\n this.refreshToken = data.refresh_token;\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n console.log('[API] Refresh token rotated');\r\n }\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Access token refreshed successfully');\r\n return true;\r\n } catch (error) {\r\n console.error('[API] Token refresh network error:', error);\r\n console.warn('[API] Will retry refresh on next interval');\r\n return false;\r\n } finally {\r\n this.isRefreshing = false;\r\n this.refreshPromise = null;\r\n }\r\n })();\r\n\r\n return this.refreshPromise;\r\n }\r\n\r\n // ========================================================================\r\n // HTTP REQUEST METHOD\r\n // ========================================================================\r\n\r\n /**\r\n * Make an authenticated HTTP request.\r\n * Handles token refresh, 401 retry, and rate limiting automatically.\r\n * \r\n * This method is public so apps can make custom API calls without extending BaseApi.\r\n * \r\n * @example\r\n * const authApi = createAuthApi(API_URL);\r\n * const data = await authApi.request<MyType>('/api/my-endpoint/', { method: 'POST', body: JSON.stringify(payload) });\r\n */\r\n async request<T>(endpoint: string, options: RequestInit = {}, isRetry = false, retryCount = 0): Promise<T> {\r\n await this.ensureTokenReady();\r\n \r\n // Proactive token refresh before request\r\n if (this.token && !isRetry && !endpoint.includes('/auth/')) {\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (expiry) {\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n if (timeUntilExpiry < 2 * 60 * 1000) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry/1000)}s, refreshing before request...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n }\r\n \r\n const url = `${this.apiBaseUrl}${endpoint}`;\r\n \r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...(options.headers as Record<string, string>),\r\n };\r\n\r\n if (this.token) {\r\n headers.Authorization = `Bearer ${this.token}`;\r\n }\r\n\r\n console.log(`[API Request] ${options.method || 'GET'} ${url}`);\r\n \r\n try {\r\n const response = await fetch(url, { ...options, headers });\r\n \r\n console.log(`[API Response] ${response.status} ${response.statusText}`);\r\n \r\n if (!response.ok) {\r\n const error = await response.text();\r\n console.error(`[API Error] ${response.status}: ${error}`);\r\n \r\n // Rate limiting retry\r\n if (response.status === 429 && retryCount < 3) {\r\n const backoffMs = Math.pow(2, retryCount) * 1000;\r\n console.log(`[API] Rate limited (429), retrying in ${backoffMs}ms (attempt ${retryCount + 1}/3)...`);\r\n await new Promise(resolve => setTimeout(resolve, backoffMs));\r\n return this.request<T>(endpoint, options, isRetry, retryCount + 1);\r\n }\r\n \r\n // 401 retry with token refresh\r\n if (response.status === 401 && !isRetry && !endpoint.includes('/auth/login') && !endpoint.includes('/auth/register') && !endpoint.includes('/auth/refresh')) {\r\n console.log('[API] Attempting token refresh...');\r\n const refreshed = await this.refreshAccessToken();\r\n if (refreshed) {\r\n return this.request<T>(endpoint, options, true, retryCount);\r\n }\r\n console.warn('[API] Token refresh failed, returning error to caller');\r\n }\r\n \r\n const apiError = new Error(`API Error: ${response.status} - ${error}`) as Error & { status: number };\r\n apiError.status = response.status;\r\n throw apiError;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n console.error('[API Connection Error]:', error);\r\n if (error instanceof TypeError && error.message.includes('Failed to fetch')) {\r\n throw new Error(`Cannot connect to API: ${this.apiBaseUrl}`);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // ========================================================================\r\n // AUTHENTICATION METHODS\r\n // ========================================================================\r\n\r\n async login(email: string, password: string): Promise<AuthResponse> {\r\n console.log('[API] Login attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ email, password }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Login successful');\r\n return data;\r\n }\r\n\r\n async register(email: string, password: string, firstName: string, lastName: string): Promise<AuthResponse> {\r\n console.log('[API] Registration attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/register', {\r\n method: 'POST',\r\n body: JSON.stringify({ \r\n email, \r\n password, \r\n first_name: firstName, \r\n last_name: lastName \r\n }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Registration successful');\r\n return data;\r\n }\r\n\r\n async getMe(): Promise<{ user: User }> {\r\n console.log('[API] Fetching current user');\r\n return this.request<{ user: User }>('/api/auth/me');\r\n }\r\n\r\n async getCurrentUser(): Promise<User> {\r\n console.log('[API] Fetching current user data');\r\n const response = await this.request<{ user: User }>('/api/auth/me');\r\n localStorage.setItem('user', JSON.stringify(response.user));\r\n return response.user;\r\n }\r\n\r\n // ========================================================================\r\n // GOOGLE OAUTH AUTHENTICATION\r\n // ========================================================================\r\n\r\n /**\r\n * Internal: Authenticate with Google OAuth.\r\n * Use googleLogin() instead - THE ONE WAY.\r\n */\r\n private async _googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login attempt');\r\n \r\n const data = await this.request<GoogleOAuthResponse>('/api/auth/google/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ credential, nonce }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);\r\n return data;\r\n }\r\n\r\n /**\r\n * THE ONE WAY: Simplified Google OAuth login.\r\n * \r\n * This is the DEFAULT method for Google Sign-In. It handles the complete\r\n * OAuth flow in one call following the Chain of Events mantra:\r\n * \r\n * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())\r\n * 2. Send credential + nonce to backend for verification\r\n * 3. Store JWT tokens on success\r\n * 4. Clear nonce to prevent replay attacks\r\n * \r\n * If ANY step fails, the entire chain fails. No partial states.\r\n * \r\n * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)\r\n * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag\r\n * @throws Error if nonce is missing or authentication fails\r\n * \r\n * @example\r\n * ```typescript\r\n * // In component: generate nonce ONCE on mount\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // Pass nonce to GoogleLogin, then on success:\r\n * const handleSuccess = async (response: CredentialResponse) => {\r\n * const result = await authApi.googleLogin(response.credential!);\r\n * // Done! Tokens stored, nonce cleared, user authenticated.\r\n * };\r\n * ```\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login (THE ONE WAY)');\r\n \r\n // Step 1: Retrieve nonce - MUST exist or we fail immediately\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.');\r\n }\r\n \r\n // Step 2: Authenticate with backend\r\n const result = await this._googleOAuthLogin(credential, nonce);\r\n \r\n // Step 3: Clear nonce AFTER success to prevent replay attacks\r\n clearOAuthNonce();\r\n console.log('[API] OAuth nonce cleared (replay protection)');\r\n \r\n return result;\r\n }\r\n\r\n // ========================================================================\r\n // PASSWORD RESET FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Request a password reset email (forgot password flow).\r\n * \r\n * @param email - The email address to send reset link to\r\n * @returns Message confirming email was sent (or would be sent)\r\n */\r\n async requestPasswordReset(email: string): Promise<PasswordResetRequestResponse> {\r\n console.log('[API] Requesting password reset for:', email);\r\n \r\n return this.request<PasswordResetRequestResponse>('/api/auth/request-password-reset', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n /**\r\n * Reset password using token from email.\r\n * \r\n * @param token - The password reset token from the email link\r\n * @param newPassword - The new password (min 8 characters)\r\n * @returns Success message\r\n */\r\n async resetPassword(token: string, newPassword: string): Promise<PasswordResetResponse> {\r\n console.log('[API] Resetting password with token');\r\n \r\n return this.request<PasswordResetResponse>('/api/auth/reset-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ token, new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // EMAIL VERIFICATION FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Verify email address using token from verification email.\r\n * \r\n * @param token - The verification token from the email link\r\n * @returns Success message\r\n */\r\n async verifyEmail(token: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Verifying email with token');\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/verify-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ token }),\r\n });\r\n }\r\n\r\n /**\r\n * Request a new verification email.\r\n * \r\n * @param email - The email address to send verification to\r\n * @returns Message confirming email was sent\r\n */\r\n async requestVerificationEmail(email: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Requesting verification email for:', email);\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/request-verification-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // SET PASSWORD (FOR OAUTH-ONLY ACCOUNTS)\r\n // ========================================================================\r\n\r\n /**\r\n * Set password for OAuth-only accounts.\r\n * \r\n * Allows users who registered via Google OAuth to set a password\r\n * so they can also log in with email/password.\r\n * \r\n * @param newPassword - The password to set (min 8 characters)\r\n * @returns Success message\r\n */\r\n async setPassword(newPassword: string): Promise<SetPasswordResponse> {\r\n console.log('[API] Setting password for OAuth account');\r\n \r\n return this.request<SetPasswordResponse>('/api/auth/set-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // ACCOUNT MANAGEMENT\r\n // ========================================================================\r\n\r\n async deleteAccount(password: string, confirmText: string): Promise<{ message: string; note: string }> {\r\n console.log('[API] Deleting account (DESTRUCTIVE)');\r\n \r\n const response = await this.request<{ message: string; note: string }>('/api/auth/me', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ \r\n password, \r\n confirm_text: confirmText \r\n }),\r\n });\r\n \r\n this.clearAuth();\r\n console.log('[API] Account deleted successfully');\r\n return response;\r\n }\r\n\r\n async logout(): Promise<void> {\r\n console.log('[API] Logging out');\r\n \r\n if (this.refreshToken) {\r\n try {\r\n await this.request('/api/auth/logout', {\r\n method: 'POST',\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n console.log('[API] Refresh token revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke refresh token on server:', error);\r\n }\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n async logoutAllDevices(): Promise<void> {\r\n console.log('[API] Logging out from all devices');\r\n \r\n try {\r\n await this.request('/api/auth/logout-all', { method: 'POST' });\r\n console.log('[API] All sessions revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke all sessions:', error);\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n protected clearAuth(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n \r\n this.token = null;\r\n this.refreshToken = null;\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('refresh_token');\r\n localStorage.removeItem('user_data');\r\n console.log('[API] Auth cache cleared');\r\n \r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n\r\n // ========================================================================\r\n // API KEY MANAGEMENT\r\n // ========================================================================\r\n\r\n async listApiKeys(): Promise<{ api_keys: ApiKey[]; total: number }> {\r\n console.log('[API] Listing user API keys');\r\n return this.request('/api/auth/api-keys', { method: 'GET' });\r\n }\r\n\r\n async createApiKey(data: {\r\n name: string;\r\n scopes?: string;\r\n expires_in_days?: number;\r\n rate_limit_per_minute?: number;\r\n }): Promise<ApiKeyCreateResponse> {\r\n console.log(`[API] Creating API key: ${data.name}`);\r\n return this.request('/api/auth/api-keys', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n name: data.name,\r\n scopes: data.scopes || 'read,write',\r\n expires_in_days: data.expires_in_days,\r\n rate_limit_per_minute: data.rate_limit_per_minute || 60\r\n }),\r\n });\r\n }\r\n\r\n async revokeApiKey(keyId: string): Promise<{ message: string; key_prefix: string; revoked_at: string }> {\r\n console.log(`[API] Revoking API key: ${keyId}`);\r\n return this.request(`/api/auth/api-keys/${keyId}`, { method: 'DELETE' });\r\n }\r\n}\r\n\r\n// ========================================================================\r\n// FACTORY FUNCTION (RECOMMENDED)\r\n// ========================================================================\r\n// Use this instead of extending BaseApi. Cleaner, no inheritance.\r\n//\r\n// Usage:\r\n// import { createAuthApi } from '@rationalbloks/frontblok-auth';\r\n// export const authApi = createAuthApi(import.meta.env.VITE_API_URL);\r\n//\r\n// // Then use directly:\r\n// authApi.login(email, password);\r\n// authApi.logout();\r\n// authApi.listApiKeys();\r\n\r\n/**\r\n * Creates an auth API client instance.\r\n * Preferred over class inheritance - simpler and cleaner.\r\n * \r\n * @param apiBaseUrl - The backend API base URL\r\n * @returns A BaseApi instance with all auth methods\r\n */\r\nexport function createAuthApi(apiBaseUrl: string): BaseApi {\r\n return new BaseApi(apiBaseUrl);\r\n}\r\n\r\n// ========================================================================\r\n// STORAGE UTILITIES\r\n// ========================================================================\r\n\r\nexport const getStoredUser = (): User | null => {\r\n const userData = localStorage.getItem('user_data');\r\n if (!userData) return null;\r\n \r\n try {\r\n return JSON.parse(userData);\r\n } catch (error) {\r\n console.error('[API] Invalid user_data in localStorage:', error);\r\n localStorage.removeItem('user_data');\r\n return null;\r\n }\r\n};\r\n\r\nexport const getStoredToken = (): string | null => {\r\n return localStorage.getItem('auth_token');\r\n};\r\n\r\nexport const isAuthenticated = (): boolean => {\r\n return !!getStoredToken();\r\n};\r\n","// ========================================================================\r\n// AUTH CONTEXT\r\n// Universal authentication state management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// It provides:\r\n// - Authentication state (user, isLoading, isAuthenticated, error)\r\n// - Authentication actions (login, register, logout, clearError, refreshUser)\r\n// - React Context pattern for app-wide auth state\r\n// - Token expiration handling via 'auth:cleared' event\r\n// ========================================================================\r\n\r\nimport React, { createContext, useContext, useState, useEffect } from 'react';\r\nimport { getStoredUser, getStoredToken } from '../api';\r\nimport type { User } from '../api';\r\nimport type { BaseApi } from '../api/client';\r\n\r\n// ========================================================================\r\n// TYPES\r\n// ========================================================================\r\n\r\nexport interface AuthState {\r\n user: User | null;\r\n isLoading: boolean;\r\n isAuthenticated: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport interface AuthActions {\r\n login: (email: string, password: string) => Promise<boolean>;\r\n register: (email: string, password: string, firstName: string, lastName: string) => Promise<boolean>;\r\n logout: () => void;\r\n clearError: () => void;\r\n refreshUser: () => Promise<void>;\r\n}\r\n\r\nexport type AuthContextType = AuthState & AuthActions;\r\n\r\n// ========================================================================\r\n// CONTEXT\r\n// ========================================================================\r\n\r\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\r\n\r\n/**\r\n * Hook to access authentication state and actions.\r\n * Must be used within an AuthProvider.\r\n */\r\nexport const useAuth = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useAuth must be used within AuthProvider');\r\n }\r\n return context;\r\n};\r\n\r\n// ========================================================================\r\n// PROVIDER FACTORY\r\n// ========================================================================\r\n\r\n/**\r\n * Creates an AuthProvider component that uses the specified API instance.\r\n * This allows the universal auth context to work with any API that extends BaseApi.\r\n * \r\n * @example\r\n * ```typescript\r\n * // In your app's auth setup:\r\n * import { createAuthProvider } from '@/core/auth';\r\n * import { myAppApi } from '@/services/myAppApi';\r\n * \r\n * export const MyAppAuthProvider = createAuthProvider(myAppApi);\r\n * ```\r\n */\r\nexport function createAuthProvider(api: BaseApi) {\r\n const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\r\n const [state, setState] = useState<AuthState>({\r\n user: getStoredUser(),\r\n isLoading: false,\r\n isAuthenticated: !!getStoredToken(),\r\n error: null,\r\n });\r\n\r\n // Listen for auth:cleared event (token expiration, session invalidation)\r\n useEffect(() => {\r\n const handleAuthCleared = () => {\r\n console.log('[Auth] Session expired or invalidated - clearing auth state');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n window.addEventListener('auth:cleared', handleAuthCleared);\r\n return () => window.removeEventListener('auth:cleared', handleAuthCleared);\r\n }, []);\r\n\r\n // Refresh user data from backend on mount if authenticated\r\n useEffect(() => {\r\n const refreshUserData = async () => {\r\n const token = getStoredToken();\r\n if (token) {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n isAuthenticated: true,\r\n }));\r\n } catch (error: unknown) {\r\n console.error('[Auth] Failed to refresh user data:', error);\r\n const httpError = error as { status?: number };\r\n if (httpError?.status === 401 || httpError?.status === 403) {\r\n console.warn('[Auth] Auth failed - clearing local state only');\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('user_data');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n } else {\r\n console.warn('[Auth] Keeping user logged in with cached data');\r\n }\r\n }\r\n }\r\n };\r\n\r\n refreshUserData();\r\n }, []);\r\n\r\n const login = async (email: string, password: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.login(email, password);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Login failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const register = async (email: string, password: string, firstName: string, lastName: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.register(email, password, firstName, lastName);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Registration failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const logout = () => {\r\n api.logout();\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n const clearError = () => {\r\n setState(prev => ({ ...prev, error: null }));\r\n };\r\n\r\n const refreshUser = async () => {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n }));\r\n } catch (error) {\r\n console.error('[Auth] Failed to refresh user:', error);\r\n }\r\n };\r\n\r\n return (\r\n <AuthContext.Provider\r\n value={{\r\n ...state,\r\n login,\r\n register,\r\n logout,\r\n clearError,\r\n refreshUser,\r\n }}\r\n >\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n };\r\n\r\n return AuthProvider;\r\n}\r\n","// ========================================================================\r\n// CORE AUTH - App Provider Factory\r\n// ========================================================================\r\n// Creates a wrapped app component with OAuth providers configured.\r\n// This is the universal entry point for React apps with authentication.\r\n//\r\n// Usage:\r\n// import { createAppRoot } from './core/auth';\r\n// import App from './App';\r\n// createAppRoot(App, { googleClientId: '...' });\r\n// ========================================================================\r\n\r\nimport React, { StrictMode } from 'react';\r\nimport { createRoot } from 'react-dom/client';\r\nimport { GoogleOAuthProvider } from '@react-oauth/google';\r\n\r\nexport interface AuthConfig {\r\n /** Google OAuth Client ID (optional - only needed if using Google Sign-In) */\r\n googleClientId?: string;\r\n}\r\n\r\n/**\r\n * Creates and renders the app root with authentication providers.\r\n * This is the universal way to bootstrap a React app with auth.\r\n */\r\nexport function createAppRoot(\r\n App: React.ComponentType,\r\n config: AuthConfig = {}\r\n): void {\r\n const { googleClientId = '' } = config;\r\n\r\n const root = createRoot(document.getElementById('root')!);\r\n\r\n // Build the component tree with OAuth provider\r\n // Always wrap with GoogleOAuthProvider to prevent crashes in AuthView\r\n // When clientId is empty, Google Sign-In just won't work (graceful degradation)\r\n const appElement = (\r\n <GoogleOAuthProvider clientId={googleClientId}>\r\n <App />\r\n </GoogleOAuthProvider>\r\n );\r\n\r\n // Render with StrictMode\r\n root.render(\r\n <StrictMode>\r\n {appElement}\r\n </StrictMode>\r\n );\r\n}\r\n"],"names":["createContext","useContext","useState","useEffect","jsx","createRoot","GoogleOAuthProvider","StrictMode"],"mappings":";;;;;;AAwDO,MAAM,qBAAqB,MAAc;AAC9C,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,QAAM,QAAQ,MAAM,KAAK,OAAO,UAAQ,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACnF,iBAAe,QAAQ,sBAAsB,KAAK;AAClD,UAAQ,IAAI,6CAA6C;AACzD,SAAO;AACT;AAMO,MAAM,gBAAgB,MAAqB;AAChD,SAAO,eAAe,QAAQ,oBAAoB;AACpD;AAKO,MAAM,kBAAkB,MAAY;AACzC,iBAAe,WAAW,oBAAoB;AAC9C,UAAQ,IAAI,uBAAuB;AACrC;AAwBO,MAAM,QAAQ;AAAA,EAUnB,YAAY,YAAoB;AAThC,SAAU,QAAuB;AACjC,SAAU,eAA8B;AACxC,SAAU,eAAwB;AAClC,SAAU,iBAA0C;AACpD,SAAU,wBAA8D;AACxE,SAAU,uBAA8D;AACxE,SAAU,sBAA4C;AAIpD,SAAK,aAAa;AAClB,SAAK,WAAA;AACL,SAAK,uBAAA;AACL,SAAK,qBAAA;AACL,SAAK,0BAAA;AAEL,YAAQ,IAAI,mBAAmB,KAAK,UAAU,EAAE;AAChD,YAAQ,IAAI,sBAA4D,YAAY,EAAE;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,uBAA6B;AACnC,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,iBAAiB,WAAW,CAAC,UAAU;AAC5C,YAAI,MAAM,QAAQ,cAAc;AAC9B,kBAAQ,IAAI,gDAAgD;AAC5D,eAAK,QAAQ,MAAM;AAAA,QACrB,WAAW,MAAM,QAAQ,iBAAiB;AACxC,kBAAQ,IAAI,wDAAwD;AACpE,eAAK,eAAe,MAAM;AAAA,QAC5B,WAAW,MAAM,QAAQ,MAAM;AAE7B,kBAAQ,IAAI,yDAAyD;AACrE,eAAK,QAAQ;AACb,eAAK,eAAe;AACpB,iBAAO,cAAc,IAAI,YAAY,cAAc,CAAC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,4BAAkC;AACxC,QAAI,KAAK,sBAAsB;AAC7B,oBAAc,KAAK,oBAAoB;AAAA,IACzC;AAEA,SAAK,uBAAuB,YAAY,MAAM;AAC5C,UAAI,KAAK,OAAO;AACd,aAAK,qBAAA;AAAA,MACP;AAAA,IACF,GAAG,KAAK,GAAI;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKQ,yBAA+B;AACrC,QAAI,OAAO,aAAa,aAAa;AACnC,eAAS,iBAAiB,oBAAoB,MAAM;AAClD,YAAI,SAAS,oBAAoB,aAAa,KAAK,OAAO;AACxD,kBAAQ,IAAI,sDAAsD;AAClE,eAAK,qBAAA;AAAA,QACP;AAAA,MACF,CAAC;AAED,aAAO,iBAAiB,SAAS,MAAM;AACrC,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,kDAAkD;AAC9D,eAAK,qBAAA;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,uBAAsC;AACpD,QAAI,CAAC,KAAK,MAAO;AAEjB,UAAM,SAAS,KAAK,eAAe,KAAK,KAAK;AAC7C,QAAI,CAAC,OAAQ;AAEb,UAAM,MAAM,KAAK,IAAA;AACjB,UAAM,kBAAkB,SAAS;AACjC,UAAM,gBAAgB,IAAI,KAAK;AAE/B,QAAI,mBAAmB,GAAG;AACxB,cAAQ,IAAI,gDAAgD;AAC5D,YAAM,KAAK,mBAAA;AAAA,IACb,WAAW,mBAAmB,eAAe;AAC3C,cAAQ,IAAI,0BAA0B,KAAK,MAAM,kBAAkB,GAAI,CAAC,8BAA8B;AACtG,YAAM,KAAK,mBAAA;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,eAAe,OAA8B;AACrD,QAAI;AACF,YAAM,UAAU,KAAK,MAAM,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AACpD,aAAO,QAAQ,MAAM,QAAQ,MAAM,MAAO;AAAA,IAC5C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,2BAAiC;AACzC,QAAI,KAAK,uBAAuB;AAC9B,mBAAa,KAAK,qBAAqB;AACvC,WAAK,wBAAwB;AAAA,IAC/B;AAEA,QAAI,CAAC,KAAK,MAAO;AAEjB,UAAM,SAAS,KAAK,eAAe,KAAK,KAAK;AAC7C,QAAI,CAAC,OAAQ;AAEb,UAAM,MAAM,KAAK,IAAA;AACjB,UAAM,kBAAkB,SAAS;AACjC,UAAM,gBAAgB,IAAI,KAAK;AAE/B,QAAI,mBAAmB,GAAG;AACxB,cAAQ,IAAI,wDAAwD;AACpE,WAAK,mBAAA;AACL;AAAA,IACF;AAEA,QAAI,mBAAmB,eAAe;AACpC,cAAQ,IAAI,sDAAsD;AAClE,WAAK,mBAAA;AACL;AAAA,IACF;AAEA,UAAM,YAAY,kBAAkB;AACpC,YAAQ,IAAI,+CAA+C,KAAK,MAAM,YAAY,GAAK,CAAC,UAAU;AAElG,SAAK,wBAAwB,WAAW,YAAY;AAClD,cAAQ,IAAI,yCAAyC;AACrD,YAAM,KAAK,mBAAA;AAAA,IACb,GAAG,SAAS;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKU,aAAmB;AAC3B,UAAM,cAAc,aAAa,QAAQ,YAAY;AACrD,UAAM,qBAAqB,aAAa,QAAQ,eAAe;AAE/D,QAAI,aAAa;AACf,WAAK,QAAQ;AACb,cAAQ,IAAI,6CAA6C;AAAA,IAC3D;AACA,QAAI,oBAAoB;AACtB,WAAK,eAAe;AACpB,cAAQ,IAAI,8CAA8C;AAAA,IAC5D;AAEA,QAAI,KAAK,OAAO;AACd,WAAK,sBAAsB,KAAK,qBAAA;AAChC,WAAK,yBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAkC;AACtC,QAAI,KAAK,qBAAqB;AAC5B,YAAM,KAAK;AACX,WAAK,sBAAsB;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAuC;AAC3C,QAAI,KAAK,cAAc;AACrB,aAAO,KAAK,kBAAkB,QAAQ,QAAQ,KAAK;AAAA,IACrD;AAGA,UAAM,qBAAqB,aAAa,QAAQ,eAAe;AAC/D,QAAI,sBAAsB,uBAAuB,KAAK,cAAc;AAClE,cAAQ,IAAI,+CAA+C;AAC3D,WAAK,eAAe;AAAA,IACtB;AAEA,QAAI,CAAC,KAAK,cAAc;AACtB,cAAQ,KAAK,kCAAkC;AAC/C,aAAO;AAAA,IACT;AAEA,SAAK,eAAe;AACpB,SAAK,kBAAkB,YAAY;AACjC,UAAI;AACF,gBAAQ,IAAI,kCAAkC;AAC9C,cAAM,WAAW,MAAM,MAAM,GAAG,KAAK,UAAU,qBAAqB;AAAA,UAClE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,UAC3B,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,cAAc;AAAA,QAAA,CAC1D;AAED,YAAI,CAAC,SAAS,IAAI;AAChB,kBAAQ,MAAM,+BAA+B,SAAS,MAAM;AAC5D,cAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,oBAAQ,IAAI,gDAAgD;AAC5D,iBAAK,UAAA;AAAA,UACP,OAAO;AACL,oBAAQ,KAAK,6BAA6B,SAAS,MAAM,+BAA+B;AAAA,UAC1F;AACA,iBAAO;AAAA,QACT;AAEA,cAAM,OAAO,MAAM,SAAS,KAAA;AAE5B,aAAK,QAAQ,KAAK;AAClB,qBAAa,QAAQ,cAAc,KAAK,YAAY;AAEpD,YAAI,KAAK,eAAe;AACtB,eAAK,eAAe,KAAK;AACzB,uBAAa,QAAQ,iBAAiB,KAAK,aAAa;AACxD,kBAAQ,IAAI,6BAA6B;AAAA,QAC3C;AAEA,aAAK,yBAAA;AACL,gBAAQ,IAAI,2CAA2C;AACvD,eAAO;AAAA,MACT,SAAS,OAAO;AACd,gBAAQ,MAAM,sCAAsC,KAAK;AACzD,gBAAQ,KAAK,2CAA2C;AACxD,eAAO;AAAA,MACT,UAAA;AACE,aAAK,eAAe;AACpB,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF,GAAA;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,QAAW,UAAkB,UAAuB,CAAA,GAAI,UAAU,OAAO,aAAa,GAAe;AACzG,UAAM,KAAK,iBAAA;AAGX,QAAI,KAAK,SAAS,CAAC,WAAW,CAAC,SAAS,SAAS,QAAQ,GAAG;AAC1D,YAAM,SAAS,KAAK,eAAe,KAAK,KAAK;AAC7C,UAAI,QAAQ;AACV,cAAM,MAAM,KAAK,IAAA;AACjB,cAAM,kBAAkB,SAAS;AACjC,YAAI,kBAAkB,IAAI,KAAK,KAAM;AACnC,kBAAQ,IAAI,0BAA0B,KAAK,MAAM,kBAAgB,GAAI,CAAC,iCAAiC;AACvG,gBAAM,KAAK,mBAAA;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,GAAG,KAAK,UAAU,GAAG,QAAQ;AAEzC,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,GAAI,QAAQ;AAAA,IAAA;AAGd,QAAI,KAAK,OAAO;AACd,cAAQ,gBAAgB,UAAU,KAAK,KAAK;AAAA,IAC9C;AAEA,YAAQ,IAAI,iBAAiB,QAAQ,UAAU,KAAK,IAAI,GAAG,EAAE;AAE7D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE,GAAG,SAAS,SAAS;AAEzD,cAAQ,IAAI,kBAAkB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAEtE,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA;AAC7B,gBAAQ,MAAM,eAAe,SAAS,MAAM,KAAK,KAAK,EAAE;AAGxD,YAAI,SAAS,WAAW,OAAO,aAAa,GAAG;AAC7C,gBAAM,YAAY,KAAK,IAAI,GAAG,UAAU,IAAI;AAC5C,kBAAQ,IAAI,yCAAyC,SAAS,eAAe,aAAa,CAAC,QAAQ;AACnG,gBAAM,IAAI,QAAQ,CAAA,YAAW,WAAW,SAAS,SAAS,CAAC;AAC3D,iBAAO,KAAK,QAAW,UAAU,SAAS,SAAS,aAAa,CAAC;AAAA,QACnE;AAGA,YAAI,SAAS,WAAW,OAAO,CAAC,WAAW,CAAC,SAAS,SAAS,aAAa,KAAK,CAAC,SAAS,SAAS,gBAAgB,KAAK,CAAC,SAAS,SAAS,eAAe,GAAG;AAC3J,kBAAQ,IAAI,mCAAmC;AAC/C,gBAAM,YAAY,MAAM,KAAK,mBAAA;AAC7B,cAAI,WAAW;AACb,mBAAO,KAAK,QAAW,UAAU,SAAS,MAAM,UAAU;AAAA,UAC5D;AACA,kBAAQ,KAAK,uDAAuD;AAAA,QACtE;AAEA,cAAM,WAAW,IAAI,MAAM,cAAc,SAAS,MAAM,MAAM,KAAK,EAAE;AACrE,iBAAS,SAAS,SAAS;AAC3B,cAAM;AAAA,MACR;AAEA,aAAO,SAAS,KAAA;AAAA,IAClB,SAAS,OAAO;AACd,cAAQ,MAAM,2BAA2B,KAAK;AAC9C,UAAI,iBAAiB,aAAa,MAAM,QAAQ,SAAS,iBAAiB,GAAG;AAC3E,cAAM,IAAI,MAAM,0BAA0B,KAAK,UAAU,EAAE;AAAA,MAC7D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,OAAe,UAAyC;AAClE,YAAQ,IAAI,wBAAwB,KAAK;AAEzC,UAAM,OAAO,MAAM,KAAK,QAAsB,mBAAmB;AAAA,MAC/D,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO,UAAU;AAAA,IAAA,CACzC;AAED,SAAK,QAAQ,KAAK;AAClB,SAAK,eAAe,KAAK,iBAAiB;AAC1C,iBAAa,QAAQ,cAAc,KAAK,YAAY;AACpD,QAAI,KAAK,eAAe;AACtB,mBAAa,QAAQ,iBAAiB,KAAK,aAAa;AAAA,IAC1D;AACA,iBAAa,QAAQ,aAAa,KAAK,UAAU,KAAK,IAAI,CAAC;AAE3D,SAAK,yBAAA;AACL,YAAQ,IAAI,wBAAwB;AACpC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAS,OAAe,UAAkB,WAAmB,UAAyC;AAC1G,YAAQ,IAAI,+BAA+B,KAAK;AAEhD,UAAM,OAAO,MAAM,KAAK,QAAsB,sBAAsB;AAAA,MAClE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,WAAW;AAAA,MAAA,CACZ;AAAA,IAAA,CACF;AAED,SAAK,QAAQ,KAAK;AAClB,SAAK,eAAe,KAAK,iBAAiB;AAC1C,iBAAa,QAAQ,cAAc,KAAK,YAAY;AACpD,QAAI,KAAK,eAAe;AACtB,mBAAa,QAAQ,iBAAiB,KAAK,aAAa;AAAA,IAC1D;AACA,iBAAa,QAAQ,aAAa,KAAK,UAAU,KAAK,IAAI,CAAC;AAE3D,SAAK,yBAAA;AACL,YAAQ,IAAI,+BAA+B;AAC3C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAiC;AACrC,YAAQ,IAAI,6BAA6B;AACzC,WAAO,KAAK,QAAwB,cAAc;AAAA,EACpD;AAAA,EAEA,MAAM,iBAAgC;AACpC,YAAQ,IAAI,kCAAkC;AAC9C,UAAM,WAAW,MAAM,KAAK,QAAwB,cAAc;AAClE,iBAAa,QAAQ,QAAQ,KAAK,UAAU,SAAS,IAAI,CAAC;AAC1D,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,kBAAkB,YAAoB,OAA6C;AAC/F,YAAQ,IAAI,kCAAkC;AAE9C,UAAM,OAAO,MAAM,KAAK,QAA6B,0BAA0B;AAAA,MAC7E,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,YAAY,OAAO;AAAA,IAAA,CAC3C;AAED,SAAK,QAAQ,KAAK;AAClB,SAAK,eAAe,KAAK,iBAAiB;AAC1C,iBAAa,QAAQ,cAAc,KAAK,YAAY;AACpD,QAAI,KAAK,eAAe;AACtB,mBAAa,QAAQ,iBAAiB,KAAK,aAAa;AAAA,IAC1D;AACA,iBAAa,QAAQ,aAAa,KAAK,UAAU,KAAK,IAAI,CAAC;AAE3D,SAAK,yBAAA;AACL,YAAQ,IAAI,4CAA4C,KAAK,WAAW,GAAG;AAC3E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+BA,MAAM,YAAY,YAAkD;AAClE,YAAQ,IAAI,wCAAwC;AAGpD,UAAM,QAAQ,cAAA;AACd,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,qGAAqG;AAAA,IACvH;AAGA,UAAM,SAAS,MAAM,KAAK,kBAAkB,YAAY,KAAK;AAG7D,oBAAA;AACA,YAAQ,IAAI,+CAA+C;AAE3D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,qBAAqB,OAAsD;AAC/E,YAAQ,IAAI,wCAAwC,KAAK;AAEzD,WAAO,KAAK,QAAsC,oCAAoC;AAAA,MACpF,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO;AAAA,IAAA,CAC/B;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cAAc,OAAe,aAAqD;AACtF,YAAQ,IAAI,qCAAqC;AAEjD,WAAO,KAAK,QAA+B,4BAA4B;AAAA,MACrE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO,cAAc,aAAa;AAAA,IAAA,CAC1D;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,YAAY,OAAmD;AACnE,YAAQ,IAAI,kCAAkC;AAE9C,WAAO,KAAK,QAAmC,0BAA0B;AAAA,MACvE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO;AAAA,IAAA,CAC/B;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,yBAAyB,OAAmD;AAChF,YAAQ,IAAI,4CAA4C,KAAK;AAE7D,WAAO,KAAK,QAAmC,wCAAwC;AAAA,MACrF,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,OAAO;AAAA,IAAA,CAC/B;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,YAAY,aAAmD;AACnE,YAAQ,IAAI,0CAA0C;AAEtD,WAAO,KAAK,QAA6B,0BAA0B;AAAA,MACjE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU,EAAE,cAAc,aAAa;AAAA,IAAA,CACnD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,UAAkB,aAAiE;AACrG,YAAQ,IAAI,sCAAsC;AAElD,UAAM,WAAW,MAAM,KAAK,QAA2C,gBAAgB;AAAA,MACrF,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA,cAAc;AAAA,MAAA,CACf;AAAA,IAAA,CACF;AAED,SAAK,UAAA;AACL,YAAQ,IAAI,oCAAoC;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAwB;AAC5B,YAAQ,IAAI,mBAAmB;AAE/B,QAAI,KAAK,cAAc;AACrB,UAAI;AACF,cAAM,KAAK,QAAQ,oBAAoB;AAAA,UACrC,QAAQ;AAAA,UACR,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,cAAc;AAAA,QAAA,CAC1D;AACD,gBAAQ,IAAI,uCAAuC;AAAA,MACrD,SAAS,OAAO;AACd,gBAAQ,KAAK,mDAAmD,KAAK;AAAA,MACvE;AAAA,IACF;AAEA,SAAK,UAAA;AAAA,EACP;AAAA,EAEA,MAAM,mBAAkC;AACtC,YAAQ,IAAI,oCAAoC;AAEhD,QAAI;AACF,YAAM,KAAK,QAAQ,wBAAwB,EAAE,QAAQ,QAAQ;AAC7D,cAAQ,IAAI,sCAAsC;AAAA,IACpD,SAAS,OAAO;AACd,cAAQ,KAAK,wCAAwC,KAAK;AAAA,IAC5D;AAEA,SAAK,UAAA;AAAA,EACP;AAAA,EAEU,YAAkB;AAC1B,QAAI,KAAK,uBAAuB;AAC9B,mBAAa,KAAK,qBAAqB;AACvC,WAAK,wBAAwB;AAAA,IAC/B;AAEA,SAAK,QAAQ;AACb,SAAK,eAAe;AACpB,iBAAa,WAAW,YAAY;AACpC,iBAAa,WAAW,eAAe;AACvC,iBAAa,WAAW,WAAW;AACnC,YAAQ,IAAI,0BAA0B;AAEtC,WAAO,cAAc,IAAI,YAAY,cAAc,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAA8D;AAClE,YAAQ,IAAI,6BAA6B;AACzC,WAAO,KAAK,QAAQ,sBAAsB,EAAE,QAAQ,OAAO;AAAA,EAC7D;AAAA,EAEA,MAAM,aAAa,MAKe;AAChC,YAAQ,IAAI,2BAA2B,KAAK,IAAI,EAAE;AAClD,WAAO,KAAK,QAAQ,sBAAsB;AAAA,MACxC,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK,UAAU;AAAA,QACvB,iBAAiB,KAAK;AAAA,QACtB,uBAAuB,KAAK,yBAAyB;AAAA,MAAA,CACtD;AAAA,IAAA,CACF;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,OAAqF;AACtG,YAAQ,IAAI,2BAA2B,KAAK,EAAE;AAC9C,WAAO,KAAK,QAAQ,sBAAsB,KAAK,IAAI,EAAE,QAAQ,UAAU;AAAA,EACzE;AACF;AAuBO,SAAS,cAAc,YAA6B;AACzD,SAAO,IAAI,QAAQ,UAAU;AAC/B;AAMO,MAAM,gBAAgB,MAAmB;AAC9C,QAAM,WAAW,aAAa,QAAQ,WAAW;AACjD,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI;AACF,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B,SAAS,OAAO;AACd,YAAQ,MAAM,4CAA4C,KAAK;AAC/D,iBAAa,WAAW,WAAW;AACnC,WAAO;AAAA,EACT;AACF;AAEO,MAAM,iBAAiB,MAAqB;AACjD,SAAO,aAAa,QAAQ,YAAY;AAC1C;AAEO,MAAM,kBAAkB,MAAe;AAC5C,SAAO,CAAC,CAAC,eAAA;AACX;ACjxBA,MAAM,cAAcA,MAAAA,cAA2C,MAAS;AAMjE,MAAM,UAAU,MAAM;AAC3B,QAAM,UAAUC,MAAAA,WAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,SAAO;AACT;AAmBO,SAAS,mBAAmB,KAAc;AAC/C,QAAM,eAAwD,CAAC,EAAE,eAAe;AAC9E,UAAM,CAAC,OAAO,QAAQ,IAAIC,eAAoB;AAAA,MAC5C,MAAM,cAAA;AAAA,MACN,WAAW;AAAA,MACX,iBAAiB,CAAC,CAAC,eAAA;AAAA,MACnB,OAAO;AAAA,IAAA,CACR;AAGDC,UAAAA,UAAU,MAAM;AACd,YAAM,oBAAoB,MAAM;AAC9B,gBAAQ,IAAI,6DAA6D;AACzE,iBAAS;AAAA,UACP,MAAM;AAAA,UACN,iBAAiB;AAAA,UACjB,WAAW;AAAA,UACX,OAAO;AAAA,QAAA,CACR;AAAA,MACH;AAEA,aAAO,iBAAiB,gBAAgB,iBAAiB;AACzD,aAAO,MAAM,OAAO,oBAAoB,gBAAgB,iBAAiB;AAAA,IAC3E,GAAG,CAAA,CAAE;AAGLA,UAAAA,UAAU,MAAM;AACd,YAAM,kBAAkB,YAAY;AAClC,cAAM,QAAQ,eAAA;AACd,YAAI,OAAO;AACT,cAAI;AACF,kBAAM,cAAc,MAAM,IAAI,eAAA;AAC9B,qBAAS,CAAA,UAAS;AAAA,cAChB,GAAG;AAAA,cACH,MAAM;AAAA,cACN,iBAAiB;AAAA,YAAA,EACjB;AAAA,UACJ,SAAS,OAAgB;AACvB,oBAAQ,MAAM,uCAAuC,KAAK;AAC1D,kBAAM,YAAY;AAClB,iBAAI,uCAAW,YAAW,QAAO,uCAAW,YAAW,KAAK;AAC1D,sBAAQ,KAAK,gDAAgD;AAC7D,2BAAa,WAAW,YAAY;AACpC,2BAAa,WAAW,WAAW;AACnC,uBAAS;AAAA,gBACP,MAAM;AAAA,gBACN,iBAAiB;AAAA,gBACjB,WAAW;AAAA,gBACX,OAAO;AAAA,cAAA,CACR;AAAA,YACH,OAAO;AACL,sBAAQ,KAAK,gDAAgD;AAAA,YAC/D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,sBAAA;AAAA,IACF,GAAG,CAAA,CAAE;AAEL,UAAM,QAAQ,OAAO,OAAe,aAAuC;AACzE,eAAS,CAAA,UAAS,EAAE,GAAG,MAAM,WAAW,MAAM,OAAO,OAAO;AAE5D,UAAI;AACF,cAAM,SAAS,MAAM,IAAI,MAAM,OAAO,QAAQ;AAC9C,iBAAS,CAAA,UAAS;AAAA,UAChB,GAAG;AAAA,UACH,MAAM,OAAO;AAAA,UACb,iBAAiB;AAAA,UACjB,WAAW;AAAA,QAAA,EACX;AACF,eAAO;AAAA,MACT,SAAS,OAAO;AACd,iBAAS,CAAA,UAAS;AAAA,UAChB,GAAG;AAAA,UACH,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAChD,WAAW;AAAA,QAAA,EACX;AACF,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,OAAe,UAAkB,WAAmB,aAAuC;AACjH,eAAS,CAAA,UAAS,EAAE,GAAG,MAAM,WAAW,MAAM,OAAO,OAAO;AAE5D,UAAI;AACF,cAAM,SAAS,MAAM,IAAI,SAAS,OAAO,UAAU,WAAW,QAAQ;AACtE,iBAAS,CAAA,UAAS;AAAA,UAChB,GAAG;AAAA,UACH,MAAM,OAAO;AAAA,UACb,iBAAiB;AAAA,UACjB,WAAW;AAAA,QAAA,EACX;AACF,eAAO;AAAA,MACT,SAAS,OAAO;AACd,iBAAS,CAAA,UAAS;AAAA,UAChB,GAAG;AAAA,UACH,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAChD,WAAW;AAAA,QAAA,EACX;AACF,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,SAAS,MAAM;AACnB,UAAI,OAAA;AACJ,eAAS;AAAA,QACP,MAAM;AAAA,QACN,iBAAiB;AAAA,QACjB,WAAW;AAAA,QACX,OAAO;AAAA,MAAA,CACR;AAAA,IACH;AAEA,UAAM,aAAa,MAAM;AACvB,eAAS,WAAS,EAAE,GAAG,MAAM,OAAO,OAAO;AAAA,IAC7C;AAEA,UAAM,cAAc,YAAY;AAC9B,UAAI;AACF,cAAM,cAAc,MAAM,IAAI,eAAA;AAC9B,iBAAS,CAAA,UAAS;AAAA,UAChB,GAAG;AAAA,UACH,MAAM;AAAA,QAAA,EACN;AAAA,MACJ,SAAS,OAAO;AACd,gBAAQ,MAAM,kCAAkC,KAAK;AAAA,MACvD;AAAA,IACF;AAEA,WACEC,2BAAAA;AAAAA,MAAC,YAAY;AAAA,MAAZ;AAAA,QACC,OAAO;AAAA,UACL,GAAG;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,QAGD;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AAEA,SAAO;AACT;ACnMO,SAAS,cACd,KACA,SAAqB,IACf;AACN,QAAM,EAAE,iBAAiB,GAAA,IAAO;AAEhC,QAAM,OAAOC,OAAAA,WAAW,SAAS,eAAe,MAAM,CAAE;AAKxD,QAAM,aACJD,2BAAAA,IAACE,OAAAA,qBAAA,EAAoB,UAAU,gBAC7B,UAAAF,2BAAAA,IAAC,OAAI,EAAA,CACP;AAIF,OAAK;AAAA,IACHA,2BAAAA,IAACG,MAAAA,cACE,UAAA,WAAA,CACH;AAAA,EAAA;AAEJ;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { BaseApi, createAuthApi, getStoredUser, getStoredToken, isAuthenticated, generateOAuthNonce, getOAuthNonce, clearOAuthNonce } from './api';
1
+ export { BaseApi, createAuthApi, getStoredUser, getStoredToken, isAuthenticated, generateOAuthNonce } from './api';
2
2
  export type { User, ApiKey, ApiKeyCreateResponse, AuthResponse, GoogleOAuthResponse, PasswordResetRequestResponse, PasswordResetResponse, EmailVerificationResponse, SetPasswordResponse } from './api';
3
3
  export { useAuth, createAuthProvider, createAppRoot } from './auth';
4
4
  export type { AuthState, AuthActions, AuthContextType, AuthConfig } from './auth';
package/dist/index.js CHANGED
@@ -362,17 +362,10 @@ class BaseApi {
362
362
  // GOOGLE OAUTH AUTHENTICATION
363
363
  // ========================================================================
364
364
  /**
365
- * Authenticate with Google OAuth.
366
- *
367
- * SECURITY: Requires a nonce for CSRF protection.
368
- * Use generateOAuthNonce() before initiating Google Sign-In, then pass
369
- * the same nonce here to prevent CSRF attacks.
370
- *
371
- * @param credential - The Google ID token (JWT from Google Sign-In)
372
- * @param nonce - The CSRF protection nonce (must match what was set in Google Sign-In)
373
- * @returns GoogleOAuthResponse with tokens and user data
365
+ * Internal: Authenticate with Google OAuth.
366
+ * Use googleLogin() instead - THE ONE WAY.
374
367
  */
375
- async googleOAuthLogin(credential, nonce) {
368
+ async _googleOAuthLogin(credential, nonce) {
376
369
  console.log("[API] Google OAuth login attempt");
377
370
  const data = await this.request("/api/auth/google/login", {
378
371
  method: "POST",
@@ -424,7 +417,7 @@ class BaseApi {
424
417
  if (!nonce) {
425
418
  throw new Error("Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.");
426
419
  }
427
- const result = await this.googleOAuthLogin(credential, nonce);
420
+ const result = await this._googleOAuthLogin(credential, nonce);
428
421
  clearOAuthNonce();
429
422
  console.log("[API] OAuth nonce cleared (replay protection)");
430
423
  return result;
@@ -747,24 +740,19 @@ function createAuthProvider(api) {
747
740
  return AuthProvider;
748
741
  }
749
742
  function createAppRoot(App, config = {}) {
750
- const { googleClientId } = config;
743
+ const { googleClientId = "" } = config;
751
744
  const root = createRoot(document.getElementById("root"));
752
- let appElement = /* @__PURE__ */ jsx(App, {});
753
- if (googleClientId) {
754
- appElement = /* @__PURE__ */ jsx(GoogleOAuthProvider, { clientId: googleClientId, children: appElement });
755
- }
745
+ const appElement = /* @__PURE__ */ jsx(GoogleOAuthProvider, { clientId: googleClientId, children: /* @__PURE__ */ jsx(App, {}) });
756
746
  root.render(
757
747
  /* @__PURE__ */ jsx(StrictMode, { children: appElement })
758
748
  );
759
749
  }
760
750
  export {
761
751
  BaseApi,
762
- clearOAuthNonce,
763
752
  createAppRoot,
764
753
  createAuthApi,
765
754
  createAuthProvider,
766
755
  generateOAuthNonce,
767
- getOAuthNonce,
768
756
  getStoredToken,
769
757
  getStoredUser,
770
758
  isAuthenticated,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/api/client.ts","../src/auth/AuthContext.tsx","../src/auth/AppProvider.tsx"],"sourcesContent":["// ========================================================================\r\n// BASE API CLIENT\r\n// Universal HTTP client with JWT token management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// Copy as-is when creating new applications.\r\n//\r\n// Features:\r\n// - JWT token storage and automatic refresh\r\n// - Cross-tab synchronization via localStorage events\r\n// - Proactive token refresh before expiration\r\n// - Automatic 401 retry with token refresh\r\n// - Rate limiting (429) retry with exponential backoff\r\n// - Authentication methods (login, register, logout)\r\n// - API key management for external integrations\r\n//\r\n// Usage:\r\n// 1. Extend this class for your application's API\r\n// 2. Add your domain-specific CRUD methods in the subclass\r\n// 3. Call super() in constructor with your API base URL\r\n//\r\n// ========================================================================\r\n\r\nimport type { \r\n User, \r\n ApiKey, \r\n ApiKeyCreateResponse, \r\n AuthResponse,\r\n GoogleOAuthResponse,\r\n PasswordResetRequestResponse,\r\n PasswordResetResponse,\r\n EmailVerificationResponse,\r\n SetPasswordResponse\r\n} from './types';\r\n\r\n// ========================================================================\r\n// OAUTH UTILITIES (defined before class so they can be used within it)\r\n// ========================================================================\r\n\r\n/**\r\n * Generate a cryptographically secure nonce for OAuth CSRF protection.\r\n * \r\n * USAGE:\r\n * 1. Call generateOAuthNonce() in component useState (runs once on mount)\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. On success, call authApi.googleLogin(credential) - it handles the rest\r\n * \r\n * @example\r\n * ```typescript\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} onSuccess={...} />\r\n * // On success: await authApi.googleLogin(credential);\r\n * ```\r\n * \r\n * @returns The generated nonce string\r\n */\r\nexport const generateOAuthNonce = (): string => {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n const nonce = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\r\n sessionStorage.setItem('google_oauth_nonce', nonce);\r\n console.log('[OAuth] Generated nonce for CSRF protection');\r\n return nonce;\r\n};\r\n\r\n/**\r\n * Retrieve the stored OAuth nonce.\r\n * @returns The stored nonce or null if not found\r\n */\r\nexport const getOAuthNonce = (): string | null => {\r\n return sessionStorage.getItem('google_oauth_nonce');\r\n};\r\n\r\n/**\r\n * Clear the stored OAuth nonce (call after successful login).\r\n */\r\nexport const clearOAuthNonce = (): void => {\r\n sessionStorage.removeItem('google_oauth_nonce');\r\n console.log('[OAuth] Cleared nonce');\r\n};\r\n\r\n// ========================================================================\r\n// BASE API CLASS\r\n// ========================================================================\r\n\r\n/**\r\n * BaseApi - Universal HTTP client with authentication.\r\n * \r\n * Extend this class to add your application-specific API methods.\r\n * \r\n * @example\r\n * ```typescript\r\n * class MyAppApi extends BaseApi {\r\n * constructor() {\r\n * super(import.meta.env.VITE_API_URL || 'https://api.myapp.com');\r\n * }\r\n * \r\n * async getProducts() {\r\n * return this.request<Product[]>('/api/products/');\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class BaseApi {\r\n protected token: string | null = null;\r\n protected refreshToken: string | null = null;\r\n protected isRefreshing: boolean = false;\r\n protected refreshPromise: Promise<boolean> | null = null;\r\n protected proactiveRefreshTimer: ReturnType<typeof setTimeout> | null = null;\r\n protected refreshCheckInterval: ReturnType<typeof setInterval> | null = null;\r\n protected tokenRefreshPromise: Promise<void> | null = null;\r\n protected readonly apiBaseUrl: string;\r\n\r\n constructor(apiBaseUrl: string) {\r\n this.apiBaseUrl = apiBaseUrl;\r\n this.loadTokens();\r\n this.setupVisibilityHandler();\r\n this.setupStorageListener();\r\n this.startRefreshCheckInterval();\r\n \r\n console.log(`[API] Base URL: ${this.apiBaseUrl}`);\r\n console.log(`[API] Environment: ${import.meta.env.DEV ? 'Development' : 'Production'}`);\r\n }\r\n\r\n // ========================================================================\r\n // TOKEN MANAGEMENT\r\n // ========================================================================\r\n\r\n /**\r\n * Sync tokens across browser tabs when localStorage changes.\r\n * Prevents \"stale refresh token\" issue where Tab A rotates the token\r\n * but Tab B still has the old (now invalid) refresh token in memory.\r\n */\r\n private setupStorageListener(): void {\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('storage', (event) => {\r\n if (event.key === 'auth_token') {\r\n console.log('[API] Token updated in another tab, syncing...');\r\n this.token = event.newValue;\r\n } else if (event.key === 'refresh_token') {\r\n console.log('[API] Refresh token updated in another tab, syncing...');\r\n this.refreshToken = event.newValue;\r\n } else if (event.key === null) {\r\n // Storage was cleared (logout in another tab)\r\n console.log('[API] Storage cleared in another tab, syncing logout...');\r\n this.token = null;\r\n this.refreshToken = null;\r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Start interval-based token check (runs every 60 seconds).\r\n * More reliable than setTimeout which browsers throttle in background tabs.\r\n */\r\n private startRefreshCheckInterval(): void {\r\n if (this.refreshCheckInterval) {\r\n clearInterval(this.refreshCheckInterval);\r\n }\r\n \r\n this.refreshCheckInterval = setInterval(() => {\r\n if (this.token) {\r\n this.checkAndRefreshToken();\r\n }\r\n }, 60 * 1000);\r\n }\r\n\r\n /**\r\n * Handle tab visibility changes - check token when user returns to tab.\r\n */\r\n private setupVisibilityHandler(): void {\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'visible' && this.token) {\r\n console.log('[API] Tab became visible, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n \r\n window.addEventListener('focus', () => {\r\n if (this.token) {\r\n console.log('[API] Window focused, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Check token expiry and refresh if needed.\r\n */\r\n protected async checkAndRefreshToken(): Promise<void> {\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 3 * 60 * 1000; // 3 minutes buffer\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token expired, refreshing immediately...');\r\n await this.refreshAccessToken();\r\n } else if (timeUntilExpiry <= refreshBuffer) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry / 1000)}s, refreshing proactively...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n\r\n /**\r\n * Parse JWT to get expiration time.\r\n */\r\n protected getTokenExpiry(token: string): number | null {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]));\r\n return payload.exp ? payload.exp * 1000 : null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Schedule proactive refresh 5 minutes before token expires.\r\n */\r\n protected scheduleProactiveRefresh(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 5 * 60 * 1000;\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token already expired, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n if (timeUntilExpiry <= refreshBuffer) {\r\n console.log('[API] Token expiring soon, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n const refreshIn = timeUntilExpiry - refreshBuffer;\r\n console.log(`[API] Scheduling proactive token refresh in ${Math.round(refreshIn / 60000)} minutes`);\r\n \r\n this.proactiveRefreshTimer = setTimeout(async () => {\r\n console.log('[API] Proactive token refresh triggered');\r\n await this.refreshAccessToken();\r\n }, refreshIn);\r\n }\r\n\r\n /**\r\n * Load tokens from localStorage on initialization.\r\n */\r\n protected loadTokens(): void {\r\n const storedToken = localStorage.getItem('auth_token');\r\n const storedRefreshToken = localStorage.getItem('refresh_token');\r\n \r\n if (storedToken) {\r\n this.token = storedToken;\r\n console.log('[API] Access token loaded from localStorage');\r\n }\r\n if (storedRefreshToken) {\r\n this.refreshToken = storedRefreshToken;\r\n console.log('[API] Refresh token loaded from localStorage');\r\n }\r\n \r\n if (this.token) {\r\n this.tokenRefreshPromise = this.checkAndRefreshToken();\r\n this.scheduleProactiveRefresh();\r\n }\r\n }\r\n\r\n /**\r\n * Wait for any pending token refresh to complete before making API calls.\r\n */\r\n async ensureTokenReady(): Promise<void> {\r\n if (this.tokenRefreshPromise) {\r\n await this.tokenRefreshPromise;\r\n this.tokenRefreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the refresh token.\r\n */\r\n async refreshAccessToken(): Promise<boolean> {\r\n if (this.isRefreshing) {\r\n return this.refreshPromise || Promise.resolve(false);\r\n }\r\n\r\n // Sync from localStorage before refresh (another tab may have rotated)\r\n const latestRefreshToken = localStorage.getItem('refresh_token');\r\n if (latestRefreshToken && latestRefreshToken !== this.refreshToken) {\r\n console.log('[API] Syncing refresh token from localStorage');\r\n this.refreshToken = latestRefreshToken;\r\n }\r\n\r\n if (!this.refreshToken) {\r\n console.warn('[API] No refresh token available');\r\n return false;\r\n }\r\n\r\n this.isRefreshing = true;\r\n this.refreshPromise = (async () => {\r\n try {\r\n console.log('[API] Refreshing access token...');\r\n const response = await fetch(`${this.apiBaseUrl}/api/auth/refresh`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n\r\n if (!response.ok) {\r\n console.error('[API] Token refresh failed:', response.status);\r\n if (response.status === 401 || response.status === 403) {\r\n console.log('[API] Refresh token is invalid, logging out...');\r\n this.clearAuth();\r\n } else {\r\n console.warn(`[API] Refresh failed with ${response.status}, will retry on next interval`);\r\n }\r\n return false;\r\n }\r\n\r\n const data = await response.json();\r\n \r\n this.token = data.access_token;\r\n localStorage.setItem('auth_token', data.access_token);\r\n \r\n if (data.refresh_token) {\r\n this.refreshToken = data.refresh_token;\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n console.log('[API] Refresh token rotated');\r\n }\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Access token refreshed successfully');\r\n return true;\r\n } catch (error) {\r\n console.error('[API] Token refresh network error:', error);\r\n console.warn('[API] Will retry refresh on next interval');\r\n return false;\r\n } finally {\r\n this.isRefreshing = false;\r\n this.refreshPromise = null;\r\n }\r\n })();\r\n\r\n return this.refreshPromise;\r\n }\r\n\r\n // ========================================================================\r\n // HTTP REQUEST METHOD\r\n // ========================================================================\r\n\r\n /**\r\n * Make an authenticated HTTP request.\r\n * Handles token refresh, 401 retry, and rate limiting automatically.\r\n * \r\n * This method is public so apps can make custom API calls without extending BaseApi.\r\n * \r\n * @example\r\n * const authApi = createAuthApi(API_URL);\r\n * const data = await authApi.request<MyType>('/api/my-endpoint/', { method: 'POST', body: JSON.stringify(payload) });\r\n */\r\n async request<T>(endpoint: string, options: RequestInit = {}, isRetry = false, retryCount = 0): Promise<T> {\r\n await this.ensureTokenReady();\r\n \r\n // Proactive token refresh before request\r\n if (this.token && !isRetry && !endpoint.includes('/auth/')) {\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (expiry) {\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n if (timeUntilExpiry < 2 * 60 * 1000) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry/1000)}s, refreshing before request...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n }\r\n \r\n const url = `${this.apiBaseUrl}${endpoint}`;\r\n \r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...(options.headers as Record<string, string>),\r\n };\r\n\r\n if (this.token) {\r\n headers.Authorization = `Bearer ${this.token}`;\r\n }\r\n\r\n console.log(`[API Request] ${options.method || 'GET'} ${url}`);\r\n \r\n try {\r\n const response = await fetch(url, { ...options, headers });\r\n \r\n console.log(`[API Response] ${response.status} ${response.statusText}`);\r\n \r\n if (!response.ok) {\r\n const error = await response.text();\r\n console.error(`[API Error] ${response.status}: ${error}`);\r\n \r\n // Rate limiting retry\r\n if (response.status === 429 && retryCount < 3) {\r\n const backoffMs = Math.pow(2, retryCount) * 1000;\r\n console.log(`[API] Rate limited (429), retrying in ${backoffMs}ms (attempt ${retryCount + 1}/3)...`);\r\n await new Promise(resolve => setTimeout(resolve, backoffMs));\r\n return this.request<T>(endpoint, options, isRetry, retryCount + 1);\r\n }\r\n \r\n // 401 retry with token refresh\r\n if (response.status === 401 && !isRetry && !endpoint.includes('/auth/login') && !endpoint.includes('/auth/register') && !endpoint.includes('/auth/refresh')) {\r\n console.log('[API] Attempting token refresh...');\r\n const refreshed = await this.refreshAccessToken();\r\n if (refreshed) {\r\n return this.request<T>(endpoint, options, true, retryCount);\r\n }\r\n console.warn('[API] Token refresh failed, returning error to caller');\r\n }\r\n \r\n const apiError = new Error(`API Error: ${response.status} - ${error}`) as Error & { status: number };\r\n apiError.status = response.status;\r\n throw apiError;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n console.error('[API Connection Error]:', error);\r\n if (error instanceof TypeError && error.message.includes('Failed to fetch')) {\r\n throw new Error(`Cannot connect to API: ${this.apiBaseUrl}`);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // ========================================================================\r\n // AUTHENTICATION METHODS\r\n // ========================================================================\r\n\r\n async login(email: string, password: string): Promise<AuthResponse> {\r\n console.log('[API] Login attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ email, password }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Login successful');\r\n return data;\r\n }\r\n\r\n async register(email: string, password: string, firstName: string, lastName: string): Promise<AuthResponse> {\r\n console.log('[API] Registration attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/register', {\r\n method: 'POST',\r\n body: JSON.stringify({ \r\n email, \r\n password, \r\n first_name: firstName, \r\n last_name: lastName \r\n }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Registration successful');\r\n return data;\r\n }\r\n\r\n async getMe(): Promise<{ user: User }> {\r\n console.log('[API] Fetching current user');\r\n return this.request<{ user: User }>('/api/auth/me');\r\n }\r\n\r\n async getCurrentUser(): Promise<User> {\r\n console.log('[API] Fetching current user data');\r\n const response = await this.request<{ user: User }>('/api/auth/me');\r\n localStorage.setItem('user', JSON.stringify(response.user));\r\n return response.user;\r\n }\r\n\r\n // ========================================================================\r\n // GOOGLE OAUTH AUTHENTICATION\r\n // ========================================================================\r\n\r\n /**\r\n * Authenticate with Google OAuth.\r\n * \r\n * SECURITY: Requires a nonce for CSRF protection.\r\n * Use generateOAuthNonce() before initiating Google Sign-In, then pass\r\n * the same nonce here to prevent CSRF attacks.\r\n * \r\n * @param credential - The Google ID token (JWT from Google Sign-In)\r\n * @param nonce - The CSRF protection nonce (must match what was set in Google Sign-In)\r\n * @returns GoogleOAuthResponse with tokens and user data\r\n */\r\n async googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login attempt');\r\n \r\n const data = await this.request<GoogleOAuthResponse>('/api/auth/google/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ credential, nonce }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);\r\n return data;\r\n }\r\n\r\n /**\r\n * THE ONE WAY: Simplified Google OAuth login.\r\n * \r\n * This is the DEFAULT method for Google Sign-In. It handles the complete\r\n * OAuth flow in one call following the Chain of Events mantra:\r\n * \r\n * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())\r\n * 2. Send credential + nonce to backend for verification\r\n * 3. Store JWT tokens on success\r\n * 4. Clear nonce to prevent replay attacks\r\n * \r\n * If ANY step fails, the entire chain fails. No partial states.\r\n * \r\n * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)\r\n * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag\r\n * @throws Error if nonce is missing or authentication fails\r\n * \r\n * @example\r\n * ```typescript\r\n * // In component: generate nonce ONCE on mount\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // Pass nonce to GoogleLogin, then on success:\r\n * const handleSuccess = async (response: CredentialResponse) => {\r\n * const result = await authApi.googleLogin(response.credential!);\r\n * // Done! Tokens stored, nonce cleared, user authenticated.\r\n * };\r\n * ```\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login (THE ONE WAY)');\r\n \r\n // Step 1: Retrieve nonce - MUST exist or we fail immediately\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.');\r\n }\r\n \r\n // Step 2: Authenticate with backend (uses internal googleOAuthLogin)\r\n const result = await this.googleOAuthLogin(credential, nonce);\r\n \r\n // Step 3: Clear nonce AFTER success to prevent replay attacks\r\n clearOAuthNonce();\r\n console.log('[API] OAuth nonce cleared (replay protection)');\r\n \r\n return result;\r\n }\r\n\r\n // ========================================================================\r\n // PASSWORD RESET FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Request a password reset email (forgot password flow).\r\n * \r\n * @param email - The email address to send reset link to\r\n * @returns Message confirming email was sent (or would be sent)\r\n */\r\n async requestPasswordReset(email: string): Promise<PasswordResetRequestResponse> {\r\n console.log('[API] Requesting password reset for:', email);\r\n \r\n return this.request<PasswordResetRequestResponse>('/api/auth/request-password-reset', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n /**\r\n * Reset password using token from email.\r\n * \r\n * @param token - The password reset token from the email link\r\n * @param newPassword - The new password (min 8 characters)\r\n * @returns Success message\r\n */\r\n async resetPassword(token: string, newPassword: string): Promise<PasswordResetResponse> {\r\n console.log('[API] Resetting password with token');\r\n \r\n return this.request<PasswordResetResponse>('/api/auth/reset-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ token, new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // EMAIL VERIFICATION FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Verify email address using token from verification email.\r\n * \r\n * @param token - The verification token from the email link\r\n * @returns Success message\r\n */\r\n async verifyEmail(token: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Verifying email with token');\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/verify-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ token }),\r\n });\r\n }\r\n\r\n /**\r\n * Request a new verification email.\r\n * \r\n * @param email - The email address to send verification to\r\n * @returns Message confirming email was sent\r\n */\r\n async requestVerificationEmail(email: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Requesting verification email for:', email);\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/request-verification-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // SET PASSWORD (FOR OAUTH-ONLY ACCOUNTS)\r\n // ========================================================================\r\n\r\n /**\r\n * Set password for OAuth-only accounts.\r\n * \r\n * Allows users who registered via Google OAuth to set a password\r\n * so they can also log in with email/password.\r\n * \r\n * @param newPassword - The password to set (min 8 characters)\r\n * @returns Success message\r\n */\r\n async setPassword(newPassword: string): Promise<SetPasswordResponse> {\r\n console.log('[API] Setting password for OAuth account');\r\n \r\n return this.request<SetPasswordResponse>('/api/auth/set-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // ACCOUNT MANAGEMENT\r\n // ========================================================================\r\n\r\n async deleteAccount(password: string, confirmText: string): Promise<{ message: string; note: string }> {\r\n console.log('[API] Deleting account (DESTRUCTIVE)');\r\n \r\n const response = await this.request<{ message: string; note: string }>('/api/auth/me', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ \r\n password, \r\n confirm_text: confirmText \r\n }),\r\n });\r\n \r\n this.clearAuth();\r\n console.log('[API] Account deleted successfully');\r\n return response;\r\n }\r\n\r\n async logout(): Promise<void> {\r\n console.log('[API] Logging out');\r\n \r\n if (this.refreshToken) {\r\n try {\r\n await this.request('/api/auth/logout', {\r\n method: 'POST',\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n console.log('[API] Refresh token revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke refresh token on server:', error);\r\n }\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n async logoutAllDevices(): Promise<void> {\r\n console.log('[API] Logging out from all devices');\r\n \r\n try {\r\n await this.request('/api/auth/logout-all', { method: 'POST' });\r\n console.log('[API] All sessions revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke all sessions:', error);\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n protected clearAuth(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n \r\n this.token = null;\r\n this.refreshToken = null;\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('refresh_token');\r\n localStorage.removeItem('user_data');\r\n console.log('[API] Auth cache cleared');\r\n \r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n\r\n // ========================================================================\r\n // API KEY MANAGEMENT\r\n // ========================================================================\r\n\r\n async listApiKeys(): Promise<{ api_keys: ApiKey[]; total: number }> {\r\n console.log('[API] Listing user API keys');\r\n return this.request('/api/auth/api-keys', { method: 'GET' });\r\n }\r\n\r\n async createApiKey(data: {\r\n name: string;\r\n scopes?: string;\r\n expires_in_days?: number;\r\n rate_limit_per_minute?: number;\r\n }): Promise<ApiKeyCreateResponse> {\r\n console.log(`[API] Creating API key: ${data.name}`);\r\n return this.request('/api/auth/api-keys', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n name: data.name,\r\n scopes: data.scopes || 'read,write',\r\n expires_in_days: data.expires_in_days,\r\n rate_limit_per_minute: data.rate_limit_per_minute || 60\r\n }),\r\n });\r\n }\r\n\r\n async revokeApiKey(keyId: string): Promise<{ message: string; key_prefix: string; revoked_at: string }> {\r\n console.log(`[API] Revoking API key: ${keyId}`);\r\n return this.request(`/api/auth/api-keys/${keyId}`, { method: 'DELETE' });\r\n }\r\n}\r\n\r\n// ========================================================================\r\n// FACTORY FUNCTION (RECOMMENDED)\r\n// ========================================================================\r\n// Use this instead of extending BaseApi. Cleaner, no inheritance.\r\n//\r\n// Usage:\r\n// import { createAuthApi } from '@rationalbloks/frontblok-auth';\r\n// export const authApi = createAuthApi(import.meta.env.VITE_API_URL);\r\n//\r\n// // Then use directly:\r\n// authApi.login(email, password);\r\n// authApi.logout();\r\n// authApi.listApiKeys();\r\n\r\n/**\r\n * Creates an auth API client instance.\r\n * Preferred over class inheritance - simpler and cleaner.\r\n * \r\n * @param apiBaseUrl - The backend API base URL\r\n * @returns A BaseApi instance with all auth methods\r\n */\r\nexport function createAuthApi(apiBaseUrl: string): BaseApi {\r\n return new BaseApi(apiBaseUrl);\r\n}\r\n\r\n// ========================================================================\r\n// STORAGE UTILITIES\r\n// ========================================================================\r\n\r\nexport const getStoredUser = (): User | null => {\r\n const userData = localStorage.getItem('user_data');\r\n if (!userData) return null;\r\n \r\n try {\r\n return JSON.parse(userData);\r\n } catch (error) {\r\n console.error('[API] Invalid user_data in localStorage:', error);\r\n localStorage.removeItem('user_data');\r\n return null;\r\n }\r\n};\r\n\r\nexport const getStoredToken = (): string | null => {\r\n return localStorage.getItem('auth_token');\r\n};\r\n\r\nexport const isAuthenticated = (): boolean => {\r\n return !!getStoredToken();\r\n};\r\n","// ========================================================================\r\n// AUTH CONTEXT\r\n// Universal authentication state management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// It provides:\r\n// - Authentication state (user, isLoading, isAuthenticated, error)\r\n// - Authentication actions (login, register, logout, clearError, refreshUser)\r\n// - React Context pattern for app-wide auth state\r\n// - Token expiration handling via 'auth:cleared' event\r\n// ========================================================================\r\n\r\nimport React, { createContext, useContext, useState, useEffect } from 'react';\r\nimport { getStoredUser, getStoredToken } from '../api';\r\nimport type { User } from '../api';\r\nimport type { BaseApi } from '../api/client';\r\n\r\n// ========================================================================\r\n// TYPES\r\n// ========================================================================\r\n\r\nexport interface AuthState {\r\n user: User | null;\r\n isLoading: boolean;\r\n isAuthenticated: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport interface AuthActions {\r\n login: (email: string, password: string) => Promise<boolean>;\r\n register: (email: string, password: string, firstName: string, lastName: string) => Promise<boolean>;\r\n logout: () => void;\r\n clearError: () => void;\r\n refreshUser: () => Promise<void>;\r\n}\r\n\r\nexport type AuthContextType = AuthState & AuthActions;\r\n\r\n// ========================================================================\r\n// CONTEXT\r\n// ========================================================================\r\n\r\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\r\n\r\n/**\r\n * Hook to access authentication state and actions.\r\n * Must be used within an AuthProvider.\r\n */\r\nexport const useAuth = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useAuth must be used within AuthProvider');\r\n }\r\n return context;\r\n};\r\n\r\n// ========================================================================\r\n// PROVIDER FACTORY\r\n// ========================================================================\r\n\r\n/**\r\n * Creates an AuthProvider component that uses the specified API instance.\r\n * This allows the universal auth context to work with any API that extends BaseApi.\r\n * \r\n * @example\r\n * ```typescript\r\n * // In your app's auth setup:\r\n * import { createAuthProvider } from '@/core/auth';\r\n * import { myAppApi } from '@/services/myAppApi';\r\n * \r\n * export const MyAppAuthProvider = createAuthProvider(myAppApi);\r\n * ```\r\n */\r\nexport function createAuthProvider(api: BaseApi) {\r\n const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\r\n const [state, setState] = useState<AuthState>({\r\n user: getStoredUser(),\r\n isLoading: false,\r\n isAuthenticated: !!getStoredToken(),\r\n error: null,\r\n });\r\n\r\n // Listen for auth:cleared event (token expiration, session invalidation)\r\n useEffect(() => {\r\n const handleAuthCleared = () => {\r\n console.log('[Auth] Session expired or invalidated - clearing auth state');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n window.addEventListener('auth:cleared', handleAuthCleared);\r\n return () => window.removeEventListener('auth:cleared', handleAuthCleared);\r\n }, []);\r\n\r\n // Refresh user data from backend on mount if authenticated\r\n useEffect(() => {\r\n const refreshUserData = async () => {\r\n const token = getStoredToken();\r\n if (token) {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n isAuthenticated: true,\r\n }));\r\n } catch (error: unknown) {\r\n console.error('[Auth] Failed to refresh user data:', error);\r\n const httpError = error as { status?: number };\r\n if (httpError?.status === 401 || httpError?.status === 403) {\r\n console.warn('[Auth] Auth failed - clearing local state only');\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('user_data');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n } else {\r\n console.warn('[Auth] Keeping user logged in with cached data');\r\n }\r\n }\r\n }\r\n };\r\n\r\n refreshUserData();\r\n }, []);\r\n\r\n const login = async (email: string, password: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.login(email, password);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Login failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const register = async (email: string, password: string, firstName: string, lastName: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.register(email, password, firstName, lastName);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Registration failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const logout = () => {\r\n api.logout();\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n const clearError = () => {\r\n setState(prev => ({ ...prev, error: null }));\r\n };\r\n\r\n const refreshUser = async () => {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n }));\r\n } catch (error) {\r\n console.error('[Auth] Failed to refresh user:', error);\r\n }\r\n };\r\n\r\n return (\r\n <AuthContext.Provider\r\n value={{\r\n ...state,\r\n login,\r\n register,\r\n logout,\r\n clearError,\r\n refreshUser,\r\n }}\r\n >\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n };\r\n\r\n return AuthProvider;\r\n}\r\n","// ========================================================================\r\n// CORE AUTH - App Provider Factory\r\n// ========================================================================\r\n// Creates a wrapped app component with OAuth providers configured.\r\n// This is the universal entry point for React apps with authentication.\r\n//\r\n// Usage:\r\n// import { createAppRoot } from './core/auth';\r\n// import App from './App';\r\n// createAppRoot(App, { googleClientId: '...' });\r\n// ========================================================================\r\n\r\nimport React, { StrictMode } from 'react';\r\nimport { createRoot } from 'react-dom/client';\r\nimport { GoogleOAuthProvider } from '@react-oauth/google';\r\n\r\nexport interface AuthConfig {\r\n /** Google OAuth Client ID (optional - only needed if using Google Sign-In) */\r\n googleClientId?: string;\r\n}\r\n\r\n/**\r\n * Creates and renders the app root with authentication providers.\r\n * This is the universal way to bootstrap a React app with auth.\r\n */\r\nexport function createAppRoot(\r\n App: React.ComponentType,\r\n config: AuthConfig = {}\r\n): void {\r\n const { googleClientId } = config;\r\n\r\n const root = createRoot(document.getElementById('root')!);\r\n\r\n // Build the component tree with optional providers\r\n let appElement: React.ReactElement = <App />;\r\n\r\n // Wrap with Google OAuth if client ID is provided\r\n if (googleClientId) {\r\n appElement = (\r\n <GoogleOAuthProvider clientId={googleClientId}>\r\n {appElement}\r\n </GoogleOAuthProvider>\r\n );\r\n }\r\n\r\n // Render with StrictMode\r\n root.render(\r\n <StrictMode>\r\n {appElement}\r\n </StrictMode>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/api/client.ts","../src/auth/AuthContext.tsx","../src/auth/AppProvider.tsx"],"sourcesContent":["// ========================================================================\r\n// BASE API CLIENT\r\n// Universal HTTP client with JWT token management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// Copy as-is when creating new applications.\r\n//\r\n// Features:\r\n// - JWT token storage and automatic refresh\r\n// - Cross-tab synchronization via localStorage events\r\n// - Proactive token refresh before expiration\r\n// - Automatic 401 retry with token refresh\r\n// - Rate limiting (429) retry with exponential backoff\r\n// - Authentication methods (login, register, logout)\r\n// - API key management for external integrations\r\n//\r\n// Usage:\r\n// 1. Extend this class for your application's API\r\n// 2. Add your domain-specific CRUD methods in the subclass\r\n// 3. Call super() in constructor with your API base URL\r\n//\r\n// ========================================================================\r\n\r\nimport type { \r\n User, \r\n ApiKey, \r\n ApiKeyCreateResponse, \r\n AuthResponse,\r\n GoogleOAuthResponse,\r\n PasswordResetRequestResponse,\r\n PasswordResetResponse,\r\n EmailVerificationResponse,\r\n SetPasswordResponse\r\n} from './types';\r\n\r\n// ========================================================================\r\n// OAUTH UTILITIES (defined before class so they can be used within it)\r\n// ========================================================================\r\n\r\n/**\r\n * Generate a cryptographically secure nonce for OAuth CSRF protection.\r\n * \r\n * USAGE:\r\n * 1. Call generateOAuthNonce() in component useState (runs once on mount)\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. On success, call authApi.googleLogin(credential) - it handles the rest\r\n * \r\n * @example\r\n * ```typescript\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} onSuccess={...} />\r\n * // On success: await authApi.googleLogin(credential);\r\n * ```\r\n * \r\n * @returns The generated nonce string\r\n */\r\nexport const generateOAuthNonce = (): string => {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n const nonce = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\r\n sessionStorage.setItem('google_oauth_nonce', nonce);\r\n console.log('[OAuth] Generated nonce for CSRF protection');\r\n return nonce;\r\n};\r\n\r\n/**\r\n * Retrieve the stored OAuth nonce.\r\n * @returns The stored nonce or null if not found\r\n */\r\nexport const getOAuthNonce = (): string | null => {\r\n return sessionStorage.getItem('google_oauth_nonce');\r\n};\r\n\r\n/**\r\n * Clear the stored OAuth nonce (call after successful login).\r\n */\r\nexport const clearOAuthNonce = (): void => {\r\n sessionStorage.removeItem('google_oauth_nonce');\r\n console.log('[OAuth] Cleared nonce');\r\n};\r\n\r\n// ========================================================================\r\n// BASE API CLASS\r\n// ========================================================================\r\n\r\n/**\r\n * BaseApi - Universal HTTP client with authentication.\r\n * \r\n * Extend this class to add your application-specific API methods.\r\n * \r\n * @example\r\n * ```typescript\r\n * class MyAppApi extends BaseApi {\r\n * constructor() {\r\n * super(import.meta.env.VITE_API_URL || 'https://api.myapp.com');\r\n * }\r\n * \r\n * async getProducts() {\r\n * return this.request<Product[]>('/api/products/');\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class BaseApi {\r\n protected token: string | null = null;\r\n protected refreshToken: string | null = null;\r\n protected isRefreshing: boolean = false;\r\n protected refreshPromise: Promise<boolean> | null = null;\r\n protected proactiveRefreshTimer: ReturnType<typeof setTimeout> | null = null;\r\n protected refreshCheckInterval: ReturnType<typeof setInterval> | null = null;\r\n protected tokenRefreshPromise: Promise<void> | null = null;\r\n protected readonly apiBaseUrl: string;\r\n\r\n constructor(apiBaseUrl: string) {\r\n this.apiBaseUrl = apiBaseUrl;\r\n this.loadTokens();\r\n this.setupVisibilityHandler();\r\n this.setupStorageListener();\r\n this.startRefreshCheckInterval();\r\n \r\n console.log(`[API] Base URL: ${this.apiBaseUrl}`);\r\n console.log(`[API] Environment: ${import.meta.env.DEV ? 'Development' : 'Production'}`);\r\n }\r\n\r\n // ========================================================================\r\n // TOKEN MANAGEMENT\r\n // ========================================================================\r\n\r\n /**\r\n * Sync tokens across browser tabs when localStorage changes.\r\n * Prevents \"stale refresh token\" issue where Tab A rotates the token\r\n * but Tab B still has the old (now invalid) refresh token in memory.\r\n */\r\n private setupStorageListener(): void {\r\n if (typeof window !== 'undefined') {\r\n window.addEventListener('storage', (event) => {\r\n if (event.key === 'auth_token') {\r\n console.log('[API] Token updated in another tab, syncing...');\r\n this.token = event.newValue;\r\n } else if (event.key === 'refresh_token') {\r\n console.log('[API] Refresh token updated in another tab, syncing...');\r\n this.refreshToken = event.newValue;\r\n } else if (event.key === null) {\r\n // Storage was cleared (logout in another tab)\r\n console.log('[API] Storage cleared in another tab, syncing logout...');\r\n this.token = null;\r\n this.refreshToken = null;\r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Start interval-based token check (runs every 60 seconds).\r\n * More reliable than setTimeout which browsers throttle in background tabs.\r\n */\r\n private startRefreshCheckInterval(): void {\r\n if (this.refreshCheckInterval) {\r\n clearInterval(this.refreshCheckInterval);\r\n }\r\n \r\n this.refreshCheckInterval = setInterval(() => {\r\n if (this.token) {\r\n this.checkAndRefreshToken();\r\n }\r\n }, 60 * 1000);\r\n }\r\n\r\n /**\r\n * Handle tab visibility changes - check token when user returns to tab.\r\n */\r\n private setupVisibilityHandler(): void {\r\n if (typeof document !== 'undefined') {\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'visible' && this.token) {\r\n console.log('[API] Tab became visible, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n \r\n window.addEventListener('focus', () => {\r\n if (this.token) {\r\n console.log('[API] Window focused, checking token validity...');\r\n this.checkAndRefreshToken();\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Check token expiry and refresh if needed.\r\n */\r\n protected async checkAndRefreshToken(): Promise<void> {\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 3 * 60 * 1000; // 3 minutes buffer\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token expired, refreshing immediately...');\r\n await this.refreshAccessToken();\r\n } else if (timeUntilExpiry <= refreshBuffer) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry / 1000)}s, refreshing proactively...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n\r\n /**\r\n * Parse JWT to get expiration time.\r\n */\r\n protected getTokenExpiry(token: string): number | null {\r\n try {\r\n const payload = JSON.parse(atob(token.split('.')[1]));\r\n return payload.exp ? payload.exp * 1000 : null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Schedule proactive refresh 5 minutes before token expires.\r\n */\r\n protected scheduleProactiveRefresh(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n\r\n if (!this.token) return;\r\n\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (!expiry) return;\r\n\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n const refreshBuffer = 5 * 60 * 1000;\r\n\r\n if (timeUntilExpiry <= 0) {\r\n console.log('[API] Token already expired, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n if (timeUntilExpiry <= refreshBuffer) {\r\n console.log('[API] Token expiring soon, refreshing immediately...');\r\n this.refreshAccessToken();\r\n return;\r\n }\r\n\r\n const refreshIn = timeUntilExpiry - refreshBuffer;\r\n console.log(`[API] Scheduling proactive token refresh in ${Math.round(refreshIn / 60000)} minutes`);\r\n \r\n this.proactiveRefreshTimer = setTimeout(async () => {\r\n console.log('[API] Proactive token refresh triggered');\r\n await this.refreshAccessToken();\r\n }, refreshIn);\r\n }\r\n\r\n /**\r\n * Load tokens from localStorage on initialization.\r\n */\r\n protected loadTokens(): void {\r\n const storedToken = localStorage.getItem('auth_token');\r\n const storedRefreshToken = localStorage.getItem('refresh_token');\r\n \r\n if (storedToken) {\r\n this.token = storedToken;\r\n console.log('[API] Access token loaded from localStorage');\r\n }\r\n if (storedRefreshToken) {\r\n this.refreshToken = storedRefreshToken;\r\n console.log('[API] Refresh token loaded from localStorage');\r\n }\r\n \r\n if (this.token) {\r\n this.tokenRefreshPromise = this.checkAndRefreshToken();\r\n this.scheduleProactiveRefresh();\r\n }\r\n }\r\n\r\n /**\r\n * Wait for any pending token refresh to complete before making API calls.\r\n */\r\n async ensureTokenReady(): Promise<void> {\r\n if (this.tokenRefreshPromise) {\r\n await this.tokenRefreshPromise;\r\n this.tokenRefreshPromise = null;\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the access token using the refresh token.\r\n */\r\n async refreshAccessToken(): Promise<boolean> {\r\n if (this.isRefreshing) {\r\n return this.refreshPromise || Promise.resolve(false);\r\n }\r\n\r\n // Sync from localStorage before refresh (another tab may have rotated)\r\n const latestRefreshToken = localStorage.getItem('refresh_token');\r\n if (latestRefreshToken && latestRefreshToken !== this.refreshToken) {\r\n console.log('[API] Syncing refresh token from localStorage');\r\n this.refreshToken = latestRefreshToken;\r\n }\r\n\r\n if (!this.refreshToken) {\r\n console.warn('[API] No refresh token available');\r\n return false;\r\n }\r\n\r\n this.isRefreshing = true;\r\n this.refreshPromise = (async () => {\r\n try {\r\n console.log('[API] Refreshing access token...');\r\n const response = await fetch(`${this.apiBaseUrl}/api/auth/refresh`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n\r\n if (!response.ok) {\r\n console.error('[API] Token refresh failed:', response.status);\r\n if (response.status === 401 || response.status === 403) {\r\n console.log('[API] Refresh token is invalid, logging out...');\r\n this.clearAuth();\r\n } else {\r\n console.warn(`[API] Refresh failed with ${response.status}, will retry on next interval`);\r\n }\r\n return false;\r\n }\r\n\r\n const data = await response.json();\r\n \r\n this.token = data.access_token;\r\n localStorage.setItem('auth_token', data.access_token);\r\n \r\n if (data.refresh_token) {\r\n this.refreshToken = data.refresh_token;\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n console.log('[API] Refresh token rotated');\r\n }\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Access token refreshed successfully');\r\n return true;\r\n } catch (error) {\r\n console.error('[API] Token refresh network error:', error);\r\n console.warn('[API] Will retry refresh on next interval');\r\n return false;\r\n } finally {\r\n this.isRefreshing = false;\r\n this.refreshPromise = null;\r\n }\r\n })();\r\n\r\n return this.refreshPromise;\r\n }\r\n\r\n // ========================================================================\r\n // HTTP REQUEST METHOD\r\n // ========================================================================\r\n\r\n /**\r\n * Make an authenticated HTTP request.\r\n * Handles token refresh, 401 retry, and rate limiting automatically.\r\n * \r\n * This method is public so apps can make custom API calls without extending BaseApi.\r\n * \r\n * @example\r\n * const authApi = createAuthApi(API_URL);\r\n * const data = await authApi.request<MyType>('/api/my-endpoint/', { method: 'POST', body: JSON.stringify(payload) });\r\n */\r\n async request<T>(endpoint: string, options: RequestInit = {}, isRetry = false, retryCount = 0): Promise<T> {\r\n await this.ensureTokenReady();\r\n \r\n // Proactive token refresh before request\r\n if (this.token && !isRetry && !endpoint.includes('/auth/')) {\r\n const expiry = this.getTokenExpiry(this.token);\r\n if (expiry) {\r\n const now = Date.now();\r\n const timeUntilExpiry = expiry - now;\r\n if (timeUntilExpiry < 2 * 60 * 1000) {\r\n console.log(`[API] Token expires in ${Math.round(timeUntilExpiry/1000)}s, refreshing before request...`);\r\n await this.refreshAccessToken();\r\n }\r\n }\r\n }\r\n \r\n const url = `${this.apiBaseUrl}${endpoint}`;\r\n \r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...(options.headers as Record<string, string>),\r\n };\r\n\r\n if (this.token) {\r\n headers.Authorization = `Bearer ${this.token}`;\r\n }\r\n\r\n console.log(`[API Request] ${options.method || 'GET'} ${url}`);\r\n \r\n try {\r\n const response = await fetch(url, { ...options, headers });\r\n \r\n console.log(`[API Response] ${response.status} ${response.statusText}`);\r\n \r\n if (!response.ok) {\r\n const error = await response.text();\r\n console.error(`[API Error] ${response.status}: ${error}`);\r\n \r\n // Rate limiting retry\r\n if (response.status === 429 && retryCount < 3) {\r\n const backoffMs = Math.pow(2, retryCount) * 1000;\r\n console.log(`[API] Rate limited (429), retrying in ${backoffMs}ms (attempt ${retryCount + 1}/3)...`);\r\n await new Promise(resolve => setTimeout(resolve, backoffMs));\r\n return this.request<T>(endpoint, options, isRetry, retryCount + 1);\r\n }\r\n \r\n // 401 retry with token refresh\r\n if (response.status === 401 && !isRetry && !endpoint.includes('/auth/login') && !endpoint.includes('/auth/register') && !endpoint.includes('/auth/refresh')) {\r\n console.log('[API] Attempting token refresh...');\r\n const refreshed = await this.refreshAccessToken();\r\n if (refreshed) {\r\n return this.request<T>(endpoint, options, true, retryCount);\r\n }\r\n console.warn('[API] Token refresh failed, returning error to caller');\r\n }\r\n \r\n const apiError = new Error(`API Error: ${response.status} - ${error}`) as Error & { status: number };\r\n apiError.status = response.status;\r\n throw apiError;\r\n }\r\n\r\n return response.json();\r\n } catch (error) {\r\n console.error('[API Connection Error]:', error);\r\n if (error instanceof TypeError && error.message.includes('Failed to fetch')) {\r\n throw new Error(`Cannot connect to API: ${this.apiBaseUrl}`);\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n // ========================================================================\r\n // AUTHENTICATION METHODS\r\n // ========================================================================\r\n\r\n async login(email: string, password: string): Promise<AuthResponse> {\r\n console.log('[API] Login attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ email, password }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Login successful');\r\n return data;\r\n }\r\n\r\n async register(email: string, password: string, firstName: string, lastName: string): Promise<AuthResponse> {\r\n console.log('[API] Registration attempt:', email);\r\n \r\n const data = await this.request<AuthResponse>('/api/auth/register', {\r\n method: 'POST',\r\n body: JSON.stringify({ \r\n email, \r\n password, \r\n first_name: firstName, \r\n last_name: lastName \r\n }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log('[API] Registration successful');\r\n return data;\r\n }\r\n\r\n async getMe(): Promise<{ user: User }> {\r\n console.log('[API] Fetching current user');\r\n return this.request<{ user: User }>('/api/auth/me');\r\n }\r\n\r\n async getCurrentUser(): Promise<User> {\r\n console.log('[API] Fetching current user data');\r\n const response = await this.request<{ user: User }>('/api/auth/me');\r\n localStorage.setItem('user', JSON.stringify(response.user));\r\n return response.user;\r\n }\r\n\r\n // ========================================================================\r\n // GOOGLE OAUTH AUTHENTICATION\r\n // ========================================================================\r\n\r\n /**\r\n * Internal: Authenticate with Google OAuth.\r\n * Use googleLogin() instead - THE ONE WAY.\r\n */\r\n private async _googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login attempt');\r\n \r\n const data = await this.request<GoogleOAuthResponse>('/api/auth/google/login', {\r\n method: 'POST',\r\n body: JSON.stringify({ credential, nonce }),\r\n });\r\n\r\n this.token = data.access_token;\r\n this.refreshToken = data.refresh_token || null;\r\n localStorage.setItem('auth_token', data.access_token);\r\n if (data.refresh_token) {\r\n localStorage.setItem('refresh_token', data.refresh_token);\r\n }\r\n localStorage.setItem('user_data', JSON.stringify(data.user));\r\n \r\n this.scheduleProactiveRefresh();\r\n console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);\r\n return data;\r\n }\r\n\r\n /**\r\n * THE ONE WAY: Simplified Google OAuth login.\r\n * \r\n * This is the DEFAULT method for Google Sign-In. It handles the complete\r\n * OAuth flow in one call following the Chain of Events mantra:\r\n * \r\n * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())\r\n * 2. Send credential + nonce to backend for verification\r\n * 3. Store JWT tokens on success\r\n * 4. Clear nonce to prevent replay attacks\r\n * \r\n * If ANY step fails, the entire chain fails. No partial states.\r\n * \r\n * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)\r\n * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag\r\n * @throws Error if nonce is missing or authentication fails\r\n * \r\n * @example\r\n * ```typescript\r\n * // In component: generate nonce ONCE on mount\r\n * const [nonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // Pass nonce to GoogleLogin, then on success:\r\n * const handleSuccess = async (response: CredentialResponse) => {\r\n * const result = await authApi.googleLogin(response.credential!);\r\n * // Done! Tokens stored, nonce cleared, user authenticated.\r\n * };\r\n * ```\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n console.log('[API] Google OAuth login (THE ONE WAY)');\r\n \r\n // Step 1: Retrieve nonce - MUST exist or we fail immediately\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.');\r\n }\r\n \r\n // Step 2: Authenticate with backend\r\n const result = await this._googleOAuthLogin(credential, nonce);\r\n \r\n // Step 3: Clear nonce AFTER success to prevent replay attacks\r\n clearOAuthNonce();\r\n console.log('[API] OAuth nonce cleared (replay protection)');\r\n \r\n return result;\r\n }\r\n\r\n // ========================================================================\r\n // PASSWORD RESET FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Request a password reset email (forgot password flow).\r\n * \r\n * @param email - The email address to send reset link to\r\n * @returns Message confirming email was sent (or would be sent)\r\n */\r\n async requestPasswordReset(email: string): Promise<PasswordResetRequestResponse> {\r\n console.log('[API] Requesting password reset for:', email);\r\n \r\n return this.request<PasswordResetRequestResponse>('/api/auth/request-password-reset', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n /**\r\n * Reset password using token from email.\r\n * \r\n * @param token - The password reset token from the email link\r\n * @param newPassword - The new password (min 8 characters)\r\n * @returns Success message\r\n */\r\n async resetPassword(token: string, newPassword: string): Promise<PasswordResetResponse> {\r\n console.log('[API] Resetting password with token');\r\n \r\n return this.request<PasswordResetResponse>('/api/auth/reset-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ token, new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // EMAIL VERIFICATION FLOW\r\n // ========================================================================\r\n\r\n /**\r\n * Verify email address using token from verification email.\r\n * \r\n * @param token - The verification token from the email link\r\n * @returns Success message\r\n */\r\n async verifyEmail(token: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Verifying email with token');\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/verify-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ token }),\r\n });\r\n }\r\n\r\n /**\r\n * Request a new verification email.\r\n * \r\n * @param email - The email address to send verification to\r\n * @returns Message confirming email was sent\r\n */\r\n async requestVerificationEmail(email: string): Promise<EmailVerificationResponse> {\r\n console.log('[API] Requesting verification email for:', email);\r\n \r\n return this.request<EmailVerificationResponse>('/api/auth/request-verification-email', {\r\n method: 'POST',\r\n body: JSON.stringify({ email }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // SET PASSWORD (FOR OAUTH-ONLY ACCOUNTS)\r\n // ========================================================================\r\n\r\n /**\r\n * Set password for OAuth-only accounts.\r\n * \r\n * Allows users who registered via Google OAuth to set a password\r\n * so they can also log in with email/password.\r\n * \r\n * @param newPassword - The password to set (min 8 characters)\r\n * @returns Success message\r\n */\r\n async setPassword(newPassword: string): Promise<SetPasswordResponse> {\r\n console.log('[API] Setting password for OAuth account');\r\n \r\n return this.request<SetPasswordResponse>('/api/auth/set-password', {\r\n method: 'POST',\r\n body: JSON.stringify({ new_password: newPassword }),\r\n });\r\n }\r\n\r\n // ========================================================================\r\n // ACCOUNT MANAGEMENT\r\n // ========================================================================\r\n\r\n async deleteAccount(password: string, confirmText: string): Promise<{ message: string; note: string }> {\r\n console.log('[API] Deleting account (DESTRUCTIVE)');\r\n \r\n const response = await this.request<{ message: string; note: string }>('/api/auth/me', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ \r\n password, \r\n confirm_text: confirmText \r\n }),\r\n });\r\n \r\n this.clearAuth();\r\n console.log('[API] Account deleted successfully');\r\n return response;\r\n }\r\n\r\n async logout(): Promise<void> {\r\n console.log('[API] Logging out');\r\n \r\n if (this.refreshToken) {\r\n try {\r\n await this.request('/api/auth/logout', {\r\n method: 'POST',\r\n body: JSON.stringify({ refresh_token: this.refreshToken }),\r\n });\r\n console.log('[API] Refresh token revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke refresh token on server:', error);\r\n }\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n async logoutAllDevices(): Promise<void> {\r\n console.log('[API] Logging out from all devices');\r\n \r\n try {\r\n await this.request('/api/auth/logout-all', { method: 'POST' });\r\n console.log('[API] All sessions revoked on server');\r\n } catch (error) {\r\n console.warn('[API] Failed to revoke all sessions:', error);\r\n }\r\n \r\n this.clearAuth();\r\n }\r\n\r\n protected clearAuth(): void {\r\n if (this.proactiveRefreshTimer) {\r\n clearTimeout(this.proactiveRefreshTimer);\r\n this.proactiveRefreshTimer = null;\r\n }\r\n \r\n this.token = null;\r\n this.refreshToken = null;\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('refresh_token');\r\n localStorage.removeItem('user_data');\r\n console.log('[API] Auth cache cleared');\r\n \r\n window.dispatchEvent(new CustomEvent('auth:cleared'));\r\n }\r\n\r\n // ========================================================================\r\n // API KEY MANAGEMENT\r\n // ========================================================================\r\n\r\n async listApiKeys(): Promise<{ api_keys: ApiKey[]; total: number }> {\r\n console.log('[API] Listing user API keys');\r\n return this.request('/api/auth/api-keys', { method: 'GET' });\r\n }\r\n\r\n async createApiKey(data: {\r\n name: string;\r\n scopes?: string;\r\n expires_in_days?: number;\r\n rate_limit_per_minute?: number;\r\n }): Promise<ApiKeyCreateResponse> {\r\n console.log(`[API] Creating API key: ${data.name}`);\r\n return this.request('/api/auth/api-keys', {\r\n method: 'POST',\r\n body: JSON.stringify({\r\n name: data.name,\r\n scopes: data.scopes || 'read,write',\r\n expires_in_days: data.expires_in_days,\r\n rate_limit_per_minute: data.rate_limit_per_minute || 60\r\n }),\r\n });\r\n }\r\n\r\n async revokeApiKey(keyId: string): Promise<{ message: string; key_prefix: string; revoked_at: string }> {\r\n console.log(`[API] Revoking API key: ${keyId}`);\r\n return this.request(`/api/auth/api-keys/${keyId}`, { method: 'DELETE' });\r\n }\r\n}\r\n\r\n// ========================================================================\r\n// FACTORY FUNCTION (RECOMMENDED)\r\n// ========================================================================\r\n// Use this instead of extending BaseApi. Cleaner, no inheritance.\r\n//\r\n// Usage:\r\n// import { createAuthApi } from '@rationalbloks/frontblok-auth';\r\n// export const authApi = createAuthApi(import.meta.env.VITE_API_URL);\r\n//\r\n// // Then use directly:\r\n// authApi.login(email, password);\r\n// authApi.logout();\r\n// authApi.listApiKeys();\r\n\r\n/**\r\n * Creates an auth API client instance.\r\n * Preferred over class inheritance - simpler and cleaner.\r\n * \r\n * @param apiBaseUrl - The backend API base URL\r\n * @returns A BaseApi instance with all auth methods\r\n */\r\nexport function createAuthApi(apiBaseUrl: string): BaseApi {\r\n return new BaseApi(apiBaseUrl);\r\n}\r\n\r\n// ========================================================================\r\n// STORAGE UTILITIES\r\n// ========================================================================\r\n\r\nexport const getStoredUser = (): User | null => {\r\n const userData = localStorage.getItem('user_data');\r\n if (!userData) return null;\r\n \r\n try {\r\n return JSON.parse(userData);\r\n } catch (error) {\r\n console.error('[API] Invalid user_data in localStorage:', error);\r\n localStorage.removeItem('user_data');\r\n return null;\r\n }\r\n};\r\n\r\nexport const getStoredToken = (): string | null => {\r\n return localStorage.getItem('auth_token');\r\n};\r\n\r\nexport const isAuthenticated = (): boolean => {\r\n return !!getStoredToken();\r\n};\r\n","// ========================================================================\r\n// AUTH CONTEXT\r\n// Universal authentication state management\r\n// ========================================================================\r\n// This file is part of the UNIVERSAL BOILERPLATE.\r\n// It provides:\r\n// - Authentication state (user, isLoading, isAuthenticated, error)\r\n// - Authentication actions (login, register, logout, clearError, refreshUser)\r\n// - React Context pattern for app-wide auth state\r\n// - Token expiration handling via 'auth:cleared' event\r\n// ========================================================================\r\n\r\nimport React, { createContext, useContext, useState, useEffect } from 'react';\r\nimport { getStoredUser, getStoredToken } from '../api';\r\nimport type { User } from '../api';\r\nimport type { BaseApi } from '../api/client';\r\n\r\n// ========================================================================\r\n// TYPES\r\n// ========================================================================\r\n\r\nexport interface AuthState {\r\n user: User | null;\r\n isLoading: boolean;\r\n isAuthenticated: boolean;\r\n error: string | null;\r\n}\r\n\r\nexport interface AuthActions {\r\n login: (email: string, password: string) => Promise<boolean>;\r\n register: (email: string, password: string, firstName: string, lastName: string) => Promise<boolean>;\r\n logout: () => void;\r\n clearError: () => void;\r\n refreshUser: () => Promise<void>;\r\n}\r\n\r\nexport type AuthContextType = AuthState & AuthActions;\r\n\r\n// ========================================================================\r\n// CONTEXT\r\n// ========================================================================\r\n\r\nconst AuthContext = createContext<AuthContextType | undefined>(undefined);\r\n\r\n/**\r\n * Hook to access authentication state and actions.\r\n * Must be used within an AuthProvider.\r\n */\r\nexport const useAuth = () => {\r\n const context = useContext(AuthContext);\r\n if (!context) {\r\n throw new Error('useAuth must be used within AuthProvider');\r\n }\r\n return context;\r\n};\r\n\r\n// ========================================================================\r\n// PROVIDER FACTORY\r\n// ========================================================================\r\n\r\n/**\r\n * Creates an AuthProvider component that uses the specified API instance.\r\n * This allows the universal auth context to work with any API that extends BaseApi.\r\n * \r\n * @example\r\n * ```typescript\r\n * // In your app's auth setup:\r\n * import { createAuthProvider } from '@/core/auth';\r\n * import { myAppApi } from '@/services/myAppApi';\r\n * \r\n * export const MyAppAuthProvider = createAuthProvider(myAppApi);\r\n * ```\r\n */\r\nexport function createAuthProvider(api: BaseApi) {\r\n const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\r\n const [state, setState] = useState<AuthState>({\r\n user: getStoredUser(),\r\n isLoading: false,\r\n isAuthenticated: !!getStoredToken(),\r\n error: null,\r\n });\r\n\r\n // Listen for auth:cleared event (token expiration, session invalidation)\r\n useEffect(() => {\r\n const handleAuthCleared = () => {\r\n console.log('[Auth] Session expired or invalidated - clearing auth state');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n window.addEventListener('auth:cleared', handleAuthCleared);\r\n return () => window.removeEventListener('auth:cleared', handleAuthCleared);\r\n }, []);\r\n\r\n // Refresh user data from backend on mount if authenticated\r\n useEffect(() => {\r\n const refreshUserData = async () => {\r\n const token = getStoredToken();\r\n if (token) {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n isAuthenticated: true,\r\n }));\r\n } catch (error: unknown) {\r\n console.error('[Auth] Failed to refresh user data:', error);\r\n const httpError = error as { status?: number };\r\n if (httpError?.status === 401 || httpError?.status === 403) {\r\n console.warn('[Auth] Auth failed - clearing local state only');\r\n localStorage.removeItem('auth_token');\r\n localStorage.removeItem('user_data');\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n } else {\r\n console.warn('[Auth] Keeping user logged in with cached data');\r\n }\r\n }\r\n }\r\n };\r\n\r\n refreshUserData();\r\n }, []);\r\n\r\n const login = async (email: string, password: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.login(email, password);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Login failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const register = async (email: string, password: string, firstName: string, lastName: string): Promise<boolean> => {\r\n setState(prev => ({ ...prev, isLoading: true, error: null }));\r\n \r\n try {\r\n const result = await api.register(email, password, firstName, lastName);\r\n setState(prev => ({\r\n ...prev,\r\n user: result.user,\r\n isAuthenticated: true,\r\n isLoading: false,\r\n }));\r\n return true;\r\n } catch (error) {\r\n setState(prev => ({\r\n ...prev,\r\n error: error instanceof Error ? error.message : 'Registration failed',\r\n isLoading: false,\r\n }));\r\n return false;\r\n }\r\n };\r\n\r\n const logout = () => {\r\n api.logout();\r\n setState({\r\n user: null,\r\n isAuthenticated: false,\r\n isLoading: false,\r\n error: null,\r\n });\r\n };\r\n\r\n const clearError = () => {\r\n setState(prev => ({ ...prev, error: null }));\r\n };\r\n\r\n const refreshUser = async () => {\r\n try {\r\n const currentUser = await api.getCurrentUser();\r\n setState(prev => ({\r\n ...prev,\r\n user: currentUser,\r\n }));\r\n } catch (error) {\r\n console.error('[Auth] Failed to refresh user:', error);\r\n }\r\n };\r\n\r\n return (\r\n <AuthContext.Provider\r\n value={{\r\n ...state,\r\n login,\r\n register,\r\n logout,\r\n clearError,\r\n refreshUser,\r\n }}\r\n >\r\n {children}\r\n </AuthContext.Provider>\r\n );\r\n };\r\n\r\n return AuthProvider;\r\n}\r\n","// ========================================================================\r\n// CORE AUTH - App Provider Factory\r\n// ========================================================================\r\n// Creates a wrapped app component with OAuth providers configured.\r\n// This is the universal entry point for React apps with authentication.\r\n//\r\n// Usage:\r\n// import { createAppRoot } from './core/auth';\r\n// import App from './App';\r\n// createAppRoot(App, { googleClientId: '...' });\r\n// ========================================================================\r\n\r\nimport React, { StrictMode } from 'react';\r\nimport { createRoot } from 'react-dom/client';\r\nimport { GoogleOAuthProvider } from '@react-oauth/google';\r\n\r\nexport interface AuthConfig {\r\n /** Google OAuth Client ID (optional - only needed if using Google Sign-In) */\r\n googleClientId?: string;\r\n}\r\n\r\n/**\r\n * Creates and renders the app root with authentication providers.\r\n * This is the universal way to bootstrap a React app with auth.\r\n */\r\nexport function createAppRoot(\r\n App: React.ComponentType,\r\n config: AuthConfig = {}\r\n): void {\r\n const { googleClientId = '' } = config;\r\n\r\n const root = createRoot(document.getElementById('root')!);\r\n\r\n // Build the component tree with OAuth provider\r\n // Always wrap with GoogleOAuthProvider to prevent crashes in AuthView\r\n // When clientId is empty, Google Sign-In just won't work (graceful degradation)\r\n const appElement = (\r\n <GoogleOAuthProvider clientId={googleClientId}>\r\n <App />\r\n </GoogleOAuthProvider>\r\n );\r\n\r\n // Render with StrictMode\r\n root.render(\r\n <StrictMode>\r\n {appElement}\r\n </StrictMode>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rationalbloks/frontblok-auth",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Authentication mechanics for RationalBloks frontends - JWT tokens, Google OAuth, password reset, email verification, cross-tab sync",
5
5
  "author": "RationalBloks Team",
6
6
  "license": "MIT",