@rationalbloks/frontblok-auth 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +54 -30
- package/dist/index.cjs +48 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +48 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/api/client.d.ts
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
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() before initiating Google Sign-In
|
|
7
|
+
* 2. Pass the nonce to GoogleLogin component via the 'nonce' prop
|
|
8
|
+
* 3. The nonce is automatically stored in sessionStorage
|
|
9
|
+
* 4. When calling googleLogin() or googleOAuthLogin(), the nonce is retrieved
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const nonce = generateOAuthNonce();
|
|
14
|
+
* // Pass to GoogleLogin: <GoogleLogin nonce={nonce} ... />
|
|
15
|
+
* // On success: authApi.googleLogin(credential) // nonce handled automatically
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @returns The generated nonce string
|
|
19
|
+
*/
|
|
20
|
+
export declare const generateOAuthNonce: () => string;
|
|
21
|
+
/**
|
|
22
|
+
* Retrieve the stored OAuth nonce.
|
|
23
|
+
*
|
|
24
|
+
* @returns The stored nonce or null if not found
|
|
25
|
+
*/
|
|
26
|
+
export declare const getOAuthNonce: () => string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Clear the stored OAuth nonce (call after successful login).
|
|
29
|
+
*/
|
|
30
|
+
export declare const clearOAuthNonce: () => void;
|
|
2
31
|
/**
|
|
3
32
|
* BaseApi - Universal HTTP client with authentication.
|
|
4
33
|
*
|
|
@@ -84,7 +113,7 @@ export declare class BaseApi {
|
|
|
84
113
|
}>;
|
|
85
114
|
getCurrentUser(): Promise<User>;
|
|
86
115
|
/**
|
|
87
|
-
* Authenticate with Google OAuth.
|
|
116
|
+
* Authenticate with Google OAuth (with explicit nonce).
|
|
88
117
|
*
|
|
89
118
|
* SECURITY: Requires a nonce for CSRF protection.
|
|
90
119
|
* Use generateOAuthNonce() before initiating Google Sign-In, then pass
|
|
@@ -95,6 +124,30 @@ export declare class BaseApi {
|
|
|
95
124
|
* @returns GoogleOAuthResponse with tokens and user data
|
|
96
125
|
*/
|
|
97
126
|
googleOAuthLogin(credential: string, nonce: string): Promise<GoogleOAuthResponse>;
|
|
127
|
+
/**
|
|
128
|
+
* Authenticate with Google OAuth (simplified - auto-retrieves nonce).
|
|
129
|
+
*
|
|
130
|
+
* This is a convenience wrapper that automatically retrieves and clears
|
|
131
|
+
* the stored nonce. Use this when you've already set up the nonce via
|
|
132
|
+
* generateOAuthNonce() and passed it to the GoogleLogin component.
|
|
133
|
+
*
|
|
134
|
+
* USAGE:
|
|
135
|
+
* ```typescript
|
|
136
|
+
* // 1. Generate nonce when component mounts
|
|
137
|
+
* const [oauthNonce] = useState(() => generateOAuthNonce());
|
|
138
|
+
*
|
|
139
|
+
* // 2. Pass to GoogleLogin
|
|
140
|
+
* <GoogleLogin nonce={oauthNonce} onSuccess={handleSuccess} />
|
|
141
|
+
*
|
|
142
|
+
* // 3. In success handler, just call:
|
|
143
|
+
* const result = await authApi.googleLogin(credentialResponse.credential);
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @param credential - The Google ID token (JWT from Google Sign-In)
|
|
147
|
+
* @returns GoogleOAuthResponse with tokens and user data
|
|
148
|
+
* @throws Error if no nonce is stored (forgot to call generateOAuthNonce)
|
|
149
|
+
*/
|
|
150
|
+
googleLogin(credential: string): Promise<GoogleOAuthResponse>;
|
|
98
151
|
/**
|
|
99
152
|
* Request a password reset email (forgot password flow).
|
|
100
153
|
*
|
|
@@ -168,32 +221,3 @@ export declare function createAuthApi(apiBaseUrl: string): BaseApi;
|
|
|
168
221
|
export declare const getStoredUser: () => User | null;
|
|
169
222
|
export declare const getStoredToken: () => string | null;
|
|
170
223
|
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;
|
|
@@ -349,7 +364,7 @@ class BaseApi {
|
|
|
349
364
|
// GOOGLE OAUTH AUTHENTICATION
|
|
350
365
|
// ========================================================================
|
|
351
366
|
/**
|
|
352
|
-
* Authenticate with Google OAuth.
|
|
367
|
+
* Authenticate with Google OAuth (with explicit nonce).
|
|
353
368
|
*
|
|
354
369
|
* SECURITY: Requires a nonce for CSRF protection.
|
|
355
370
|
* Use generateOAuthNonce() before initiating Google Sign-In, then pass
|
|
@@ -376,6 +391,38 @@ class BaseApi {
|
|
|
376
391
|
console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);
|
|
377
392
|
return data;
|
|
378
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Authenticate with Google OAuth (simplified - auto-retrieves nonce).
|
|
396
|
+
*
|
|
397
|
+
* This is a convenience wrapper that automatically retrieves and clears
|
|
398
|
+
* the stored nonce. Use this when you've already set up the nonce via
|
|
399
|
+
* generateOAuthNonce() and passed it to the GoogleLogin component.
|
|
400
|
+
*
|
|
401
|
+
* USAGE:
|
|
402
|
+
* ```typescript
|
|
403
|
+
* // 1. Generate nonce when component mounts
|
|
404
|
+
* const [oauthNonce] = useState(() => generateOAuthNonce());
|
|
405
|
+
*
|
|
406
|
+
* // 2. Pass to GoogleLogin
|
|
407
|
+
* <GoogleLogin nonce={oauthNonce} onSuccess={handleSuccess} />
|
|
408
|
+
*
|
|
409
|
+
* // 3. In success handler, just call:
|
|
410
|
+
* const result = await authApi.googleLogin(credentialResponse.credential);
|
|
411
|
+
* ```
|
|
412
|
+
*
|
|
413
|
+
* @param credential - The Google ID token (JWT from Google Sign-In)
|
|
414
|
+
* @returns GoogleOAuthResponse with tokens and user data
|
|
415
|
+
* @throws Error if no nonce is stored (forgot to call generateOAuthNonce)
|
|
416
|
+
*/
|
|
417
|
+
async googleLogin(credential) {
|
|
418
|
+
const nonce = getOAuthNonce();
|
|
419
|
+
if (!nonce) {
|
|
420
|
+
throw new Error("No OAuth nonce found. Call generateOAuthNonce() before initiating Google Sign-In.");
|
|
421
|
+
}
|
|
422
|
+
const result = await this.googleOAuthLogin(credential, nonce);
|
|
423
|
+
clearOAuthNonce();
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
379
426
|
// ========================================================================
|
|
380
427
|
// PASSWORD RESET FLOW
|
|
381
428
|
// ========================================================================
|
|
@@ -552,21 +599,6 @@ const getStoredToken = () => {
|
|
|
552
599
|
const isAuthenticated = () => {
|
|
553
600
|
return !!getStoredToken();
|
|
554
601
|
};
|
|
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
602
|
const AuthContext = react.createContext(void 0);
|
|
571
603
|
const useAuth = () => {
|
|
572
604
|
const context = react.useContext(AuthContext);
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 in methods)\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 googleLogin() or googleOAuthLogin(), the nonce is retrieved\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.googleLogin(credential) // nonce handled automatically\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// ========================================================================\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 (with explicit nonce).\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 * Authenticate with Google OAuth (simplified - auto-retrieves nonce).\r\n * \r\n * This is a convenience wrapper that automatically retrieves and clears\r\n * the stored nonce. Use this when you've already set up the nonce via\r\n * generateOAuthNonce() and passed it to the GoogleLogin component.\r\n * \r\n * USAGE:\r\n * ```typescript\r\n * // 1. Generate nonce when component mounts\r\n * const [oauthNonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // 2. Pass to GoogleLogin\r\n * <GoogleLogin nonce={oauthNonce} onSuccess={handleSuccess} />\r\n * \r\n * // 3. In success handler, just call:\r\n * const result = await authApi.googleLogin(credentialResponse.credential);\r\n * ```\r\n * \r\n * @param credential - The Google ID token (JWT from Google Sign-In)\r\n * @returns GoogleOAuthResponse with tokens and user data\r\n * @throws Error if no nonce is stored (forgot to call generateOAuthNonce)\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('No OAuth nonce found. Call generateOAuthNonce() before initiating Google Sign-In.');\r\n }\r\n \r\n const result = await this.googleOAuthLogin(credential, nonce);\r\n \r\n // Clear nonce after successful authentication (prevent replay)\r\n clearOAuthNonce();\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;
|
|
@@ -347,7 +362,7 @@ class BaseApi {
|
|
|
347
362
|
// GOOGLE OAUTH AUTHENTICATION
|
|
348
363
|
// ========================================================================
|
|
349
364
|
/**
|
|
350
|
-
* Authenticate with Google OAuth.
|
|
365
|
+
* Authenticate with Google OAuth (with explicit nonce).
|
|
351
366
|
*
|
|
352
367
|
* SECURITY: Requires a nonce for CSRF protection.
|
|
353
368
|
* Use generateOAuthNonce() before initiating Google Sign-In, then pass
|
|
@@ -374,6 +389,38 @@ class BaseApi {
|
|
|
374
389
|
console.log(`[API] Google OAuth successful (new_user: ${data.is_new_user})`);
|
|
375
390
|
return data;
|
|
376
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Authenticate with Google OAuth (simplified - auto-retrieves nonce).
|
|
394
|
+
*
|
|
395
|
+
* This is a convenience wrapper that automatically retrieves and clears
|
|
396
|
+
* the stored nonce. Use this when you've already set up the nonce via
|
|
397
|
+
* generateOAuthNonce() and passed it to the GoogleLogin component.
|
|
398
|
+
*
|
|
399
|
+
* USAGE:
|
|
400
|
+
* ```typescript
|
|
401
|
+
* // 1. Generate nonce when component mounts
|
|
402
|
+
* const [oauthNonce] = useState(() => generateOAuthNonce());
|
|
403
|
+
*
|
|
404
|
+
* // 2. Pass to GoogleLogin
|
|
405
|
+
* <GoogleLogin nonce={oauthNonce} onSuccess={handleSuccess} />
|
|
406
|
+
*
|
|
407
|
+
* // 3. In success handler, just call:
|
|
408
|
+
* const result = await authApi.googleLogin(credentialResponse.credential);
|
|
409
|
+
* ```
|
|
410
|
+
*
|
|
411
|
+
* @param credential - The Google ID token (JWT from Google Sign-In)
|
|
412
|
+
* @returns GoogleOAuthResponse with tokens and user data
|
|
413
|
+
* @throws Error if no nonce is stored (forgot to call generateOAuthNonce)
|
|
414
|
+
*/
|
|
415
|
+
async googleLogin(credential) {
|
|
416
|
+
const nonce = getOAuthNonce();
|
|
417
|
+
if (!nonce) {
|
|
418
|
+
throw new Error("No OAuth nonce found. Call generateOAuthNonce() before initiating Google Sign-In.");
|
|
419
|
+
}
|
|
420
|
+
const result = await this.googleOAuthLogin(credential, nonce);
|
|
421
|
+
clearOAuthNonce();
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
377
424
|
// ========================================================================
|
|
378
425
|
// PASSWORD RESET FLOW
|
|
379
426
|
// ========================================================================
|
|
@@ -550,21 +597,6 @@ const getStoredToken = () => {
|
|
|
550
597
|
const isAuthenticated = () => {
|
|
551
598
|
return !!getStoredToken();
|
|
552
599
|
};
|
|
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
600
|
const AuthContext = createContext(void 0);
|
|
569
601
|
const useAuth = () => {
|
|
570
602
|
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 in methods)\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 googleLogin() or googleOAuthLogin(), the nonce is retrieved\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.googleLogin(credential) // nonce handled automatically\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// ========================================================================\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 (with explicit nonce).\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 * Authenticate with Google OAuth (simplified - auto-retrieves nonce).\r\n * \r\n * This is a convenience wrapper that automatically retrieves and clears\r\n * the stored nonce. Use this when you've already set up the nonce via\r\n * generateOAuthNonce() and passed it to the GoogleLogin component.\r\n * \r\n * USAGE:\r\n * ```typescript\r\n * // 1. Generate nonce when component mounts\r\n * const [oauthNonce] = useState(() => generateOAuthNonce());\r\n * \r\n * // 2. Pass to GoogleLogin\r\n * <GoogleLogin nonce={oauthNonce} onSuccess={handleSuccess} />\r\n * \r\n * // 3. In success handler, just call:\r\n * const result = await authApi.googleLogin(credentialResponse.credential);\r\n * ```\r\n * \r\n * @param credential - The Google ID token (JWT from Google Sign-In)\r\n * @returns GoogleOAuthResponse with tokens and user data\r\n * @throws Error if no nonce is stored (forgot to call generateOAuthNonce)\r\n */\r\n async googleLogin(credential: string): Promise<GoogleOAuthResponse> {\r\n const nonce = getOAuthNonce();\r\n if (!nonce) {\r\n throw new Error('No OAuth nonce found. Call generateOAuthNonce() before initiating Google Sign-In.');\r\n }\r\n \r\n const result = await this.googleOAuthLogin(credential, nonce);\r\n \r\n // Clear nonce after successful authentication (prevent replay)\r\n clearOAuthNonce();\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.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Authentication mechanics for RationalBloks frontends - JWT tokens, Google OAuth, password reset, email verification, cross-tab sync",
|
|
5
5
|
"author": "RationalBloks Team",
|
|
6
6
|
"license": "MIT",
|