@rationalbloks/frontblok-auth 0.2.0 → 0.2.2

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.
@@ -1,4 +1,31 @@
1
1
  import type { User, ApiKey, ApiKeyCreateResponse, AuthResponse, GoogleOAuthResponse, PasswordResetRequestResponse, PasswordResetResponse, EmailVerificationResponse, SetPasswordResponse } from './types';
2
+ /**
3
+ * Generate a cryptographically secure nonce for OAuth CSRF protection.
4
+ *
5
+ * USAGE:
6
+ * 1. Call generateOAuthNonce() in component useState (runs once on mount)
7
+ * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop
8
+ * 3. On success, call authApi.googleLogin(credential) - it handles the rest
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const [nonce] = useState(() => generateOAuthNonce());
13
+ * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} onSuccess={...} />
14
+ * // On success: await authApi.googleLogin(credential);
15
+ * ```
16
+ *
17
+ * @returns The generated nonce string
18
+ */
19
+ export declare const generateOAuthNonce: () => string;
20
+ /**
21
+ * Retrieve the stored OAuth nonce.
22
+ * @returns The stored nonce or null if not found
23
+ */
24
+ export declare const getOAuthNonce: () => string | null;
25
+ /**
26
+ * Clear the stored OAuth nonce (call after successful login).
27
+ */
28
+ export declare const clearOAuthNonce: () => void;
2
29
  /**
3
30
  * BaseApi - Universal HTTP client with authentication.
4
31
  *
@@ -95,6 +122,36 @@ export declare class BaseApi {
95
122
  * @returns GoogleOAuthResponse with tokens and user data
96
123
  */
97
124
  googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse>;
125
+ /**
126
+ * THE ONE WAY: Simplified Google OAuth login.
127
+ *
128
+ * This is the DEFAULT method for Google Sign-In. It handles the complete
129
+ * OAuth flow in one call following the Chain of Events mantra:
130
+ *
131
+ * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())
132
+ * 2. Send credential + nonce to backend for verification
133
+ * 3. Store JWT tokens on success
134
+ * 4. Clear nonce to prevent replay attacks
135
+ *
136
+ * If ANY step fails, the entire chain fails. No partial states.
137
+ *
138
+ * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)
139
+ * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag
140
+ * @throws Error if nonce is missing or authentication fails
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // In component: generate nonce ONCE on mount
145
+ * const [nonce] = useState(() => generateOAuthNonce());
146
+ *
147
+ * // Pass nonce to GoogleLogin, then on success:
148
+ * const handleSuccess = async (response: CredentialResponse) => {
149
+ * const result = await authApi.googleLogin(response.credential!);
150
+ * // Done! Tokens stored, nonce cleared, user authenticated.
151
+ * };
152
+ * ```
153
+ */
154
+ googleLogin(credential: string): Promise<GoogleOAuthResponse>;
98
155
  /**
99
156
  * Request a password reset email (forgot password flow).
100
157
  *
@@ -168,32 +225,3 @@ export declare function createAuthApi(apiBaseUrl: string): BaseApi;
168
225
  export declare const getStoredUser: () => User | null;
169
226
  export declare const getStoredToken: () => string | null;
170
227
  export declare const isAuthenticated: () => boolean;
171
- /**
172
- * Generate a cryptographically secure nonce for OAuth CSRF protection.
173
- *
174
- * USAGE:
175
- * 1. Call generateOAuthNonce() before initiating Google Sign-In
176
- * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop
177
- * 3. The nonce is automatically stored in sessionStorage
178
- * 4. When calling googleOAuthLogin(), retrieve it with getOAuthNonce()
179
- *
180
- * @example
181
- * ```typescript
182
- * const nonce = generateOAuthNonce();
183
- * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} ... />
184
- * // On success: authApi.googleOAuthLogin(credential, getOAuthNonce()!)
185
- * ```
186
- *
187
- * @returns The generated nonce string
188
- */
189
- export declare const generateOAuthNonce: () => string;
190
- /**
191
- * Retrieve the stored OAuth nonce.
192
- *
193
- * @returns The stored nonce or null if not found
194
- */
195
- export declare const getOAuthNonce: () => string | null;
196
- /**
197
- * Clear the stored OAuth nonce (call after successful login).
198
- */
199
- export declare const clearOAuthNonce: () => void;
package/dist/index.cjs CHANGED
@@ -4,6 +4,21 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("react");
5
5
  const client = require("react-dom/client");
6
6
  const google = require("@react-oauth/google");
7
+ const generateOAuthNonce = () => {
8
+ const array = new Uint8Array(32);
9
+ crypto.getRandomValues(array);
10
+ const nonce = Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
11
+ sessionStorage.setItem("google_oauth_nonce", nonce);
12
+ console.log("[OAuth] Generated nonce for CSRF protection");
13
+ return nonce;
14
+ };
15
+ const getOAuthNonce = () => {
16
+ return sessionStorage.getItem("google_oauth_nonce");
17
+ };
18
+ const clearOAuthNonce = () => {
19
+ sessionStorage.removeItem("google_oauth_nonce");
20
+ console.log("[OAuth] Cleared nonce");
21
+ };
7
22
  class BaseApi {
8
23
  constructor(apiBaseUrl) {
9
24
  this.token = null;
@@ -376,6 +391,46 @@ class BaseApi {
376
391
  console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);
377
392
  return data;
378
393
  }
394
+ /**
395
+ * THE ONE WAY: Simplified Google OAuth login.
396
+ *
397
+ * This is the DEFAULT method for Google Sign-In. It handles the complete
398
+ * OAuth flow in one call following the Chain of Events mantra:
399
+ *
400
+ * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())
401
+ * 2. Send credential + nonce to backend for verification
402
+ * 3. Store JWT tokens on success
403
+ * 4. Clear nonce to prevent replay attacks
404
+ *
405
+ * If ANY step fails, the entire chain fails. No partial states.
406
+ *
407
+ * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)
408
+ * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag
409
+ * @throws Error if nonce is missing or authentication fails
410
+ *
411
+ * @example
412
+ * ```typescript
413
+ * // In component: generate nonce ONCE on mount
414
+ * const [nonce] = useState(() => generateOAuthNonce());
415
+ *
416
+ * // Pass nonce to GoogleLogin, then on success:
417
+ * const handleSuccess = async (response: CredentialResponse) => {
418
+ * const result = await authApi.googleLogin(response.credential!);
419
+ * // Done! Tokens stored, nonce cleared, user authenticated.
420
+ * };
421
+ * ```
422
+ */
423
+ async googleLogin(credential) {
424
+ console.log("[API] Google OAuth login (THE ONE WAY)");
425
+ const nonce = getOAuthNonce();
426
+ if (!nonce) {
427
+ throw new Error("Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.");
428
+ }
429
+ const result = await this.googleOAuthLogin(credential, nonce);
430
+ clearOAuthNonce();
431
+ console.log("[API] OAuth nonce cleared (replay protection)");
432
+ return result;
433
+ }
379
434
  // ========================================================================
380
435
  // PASSWORD RESET FLOW
381
436
  // ========================================================================
@@ -552,21 +607,6 @@ const getStoredToken = () => {
552
607
  const isAuthenticated = () => {
553
608
  return !!getStoredToken();
554
609
  };
555
- const generateOAuthNonce = () => {
556
- const array = new Uint8Array(32);
557
- crypto.getRandomValues(array);
558
- const nonce = Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
559
- sessionStorage.setItem("google_oauth_nonce", nonce);
560
- console.log("[OAuth] Generated nonce for CSRF protection");
561
- return nonce;
562
- };
563
- const getOAuthNonce = () => {
564
- return sessionStorage.getItem("google_oauth_nonce");
565
- };
566
- const clearOAuthNonce = () => {
567
- sessionStorage.removeItem("google_oauth_nonce");
568
- console.log("[OAuth] Cleared nonce");
569
- };
570
610
  const AuthContext = react.createContext(void 0);
571
611
  const useAuth = () => {
572
612
  const context = react.useContext(AuthContext);
@@ -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 * 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 // 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// ========================================================================\r\n// OAUTH UTILITIES\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() before initiating Google Sign-In\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. The nonce is automatically stored in sessionStorage\r\n * 4. When calling googleOAuthLogin(), retrieve it with getOAuthNonce()\r\n * \r\n * @example\r\n * ```typescript\r\n * const nonce = generateOAuthNonce();\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} ... />\r\n * // On success: authApi.googleOAuthLogin(credential, getOAuthNonce()!)\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 * \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// 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 * 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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"}
package/dist/index.js CHANGED
@@ -2,6 +2,21 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useState, useEffect, StrictMode } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
4
  import { GoogleOAuthProvider } from "@react-oauth/google";
5
+ const generateOAuthNonce = () => {
6
+ const array = new Uint8Array(32);
7
+ crypto.getRandomValues(array);
8
+ const nonce = Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
9
+ sessionStorage.setItem("google_oauth_nonce", nonce);
10
+ console.log("[OAuth] Generated nonce for CSRF protection");
11
+ return nonce;
12
+ };
13
+ const getOAuthNonce = () => {
14
+ return sessionStorage.getItem("google_oauth_nonce");
15
+ };
16
+ const clearOAuthNonce = () => {
17
+ sessionStorage.removeItem("google_oauth_nonce");
18
+ console.log("[OAuth] Cleared nonce");
19
+ };
5
20
  class BaseApi {
6
21
  constructor(apiBaseUrl) {
7
22
  this.token = null;
@@ -374,6 +389,46 @@ class BaseApi {
374
389
  console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);
375
390
  return data;
376
391
  }
392
+ /**
393
+ * THE ONE WAY: Simplified Google OAuth login.
394
+ *
395
+ * This is the DEFAULT method for Google Sign-In. It handles the complete
396
+ * OAuth flow in one call following the Chain of Events mantra:
397
+ *
398
+ * 1. Retrieve nonce from sessionStorage (must exist from generateOAuthNonce())
399
+ * 2. Send credential + nonce to backend for verification
400
+ * 3. Store JWT tokens on success
401
+ * 4. Clear nonce to prevent replay attacks
402
+ *
403
+ * If ANY step fails, the entire chain fails. No partial states.
404
+ *
405
+ * @param credential - The Google ID token (JWT from GoogleLogin onSuccess)
406
+ * @returns GoogleOAuthResponse with tokens, user data, and is_new_user flag
407
+ * @throws Error if nonce is missing or authentication fails
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * // In component: generate nonce ONCE on mount
412
+ * const [nonce] = useState(() => generateOAuthNonce());
413
+ *
414
+ * // Pass nonce to GoogleLogin, then on success:
415
+ * const handleSuccess = async (response: CredentialResponse) => {
416
+ * const result = await authApi.googleLogin(response.credential!);
417
+ * // Done! Tokens stored, nonce cleared, user authenticated.
418
+ * };
419
+ * ```
420
+ */
421
+ async googleLogin(credential) {
422
+ console.log("[API] Google OAuth login (THE ONE WAY)");
423
+ const nonce = getOAuthNonce();
424
+ if (!nonce) {
425
+ throw new Error("Security validation failed: OAuth nonce not found. Call generateOAuthNonce() before Google Sign-In.");
426
+ }
427
+ const result = await this.googleOAuthLogin(credential, nonce);
428
+ clearOAuthNonce();
429
+ console.log("[API] OAuth nonce cleared (replay protection)");
430
+ return result;
431
+ }
377
432
  // ========================================================================
378
433
  // PASSWORD RESET FLOW
379
434
  // ========================================================================
@@ -550,21 +605,6 @@ const getStoredToken = () => {
550
605
  const isAuthenticated = () => {
551
606
  return !!getStoredToken();
552
607
  };
553
- const generateOAuthNonce = () => {
554
- const array = new Uint8Array(32);
555
- crypto.getRandomValues(array);
556
- const nonce = Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
557
- sessionStorage.setItem("google_oauth_nonce", nonce);
558
- console.log("[OAuth] Generated nonce for CSRF protection");
559
- return nonce;
560
- };
561
- const getOAuthNonce = () => {
562
- return sessionStorage.getItem("google_oauth_nonce");
563
- };
564
- const clearOAuthNonce = () => {
565
- sessionStorage.removeItem("google_oauth_nonce");
566
- console.log("[OAuth] Cleared nonce");
567
- };
568
608
  const AuthContext = createContext(void 0);
569
609
  const useAuth = () => {
570
610
  const context = useContext(AuthContext);
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 * 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 // 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// ========================================================================\r\n// OAUTH UTILITIES\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() before initiating Google Sign-In\r\n * 2. Pass the nonce to GoogleLogin component via the 'nonce' prop\r\n * 3. The nonce is automatically stored in sessionStorage\r\n * 4. When calling googleOAuthLogin(), retrieve it with getOAuthNonce()\r\n * \r\n * @example\r\n * ```typescript\r\n * const nonce = generateOAuthNonce();\r\n * // Pass to GoogleLogin: <GoogleLogin nonce={nonce} ... />\r\n * // On success: authApi.googleOAuthLogin(credential, getOAuthNonce()!)\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 * \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// 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 * 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rationalbloks/frontblok-auth",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",