@lenan-soft/auth 1.0.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.
@@ -0,0 +1,531 @@
1
+ // src/react/auth-provider.tsx
2
+ import {
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useMemo,
8
+ useState
9
+ } from "react";
10
+
11
+ // src/shared/types.ts
12
+ var DEFAULT_AUTH_ENDPOINTS = {
13
+ login: "/auth/login",
14
+ register: "/auth/register",
15
+ refresh: "/auth/refresh",
16
+ logout: "/auth/logout",
17
+ me: "/auth/me"
18
+ };
19
+ var TOKEN_STORAGE_KEYS = {
20
+ ACCESS_TOKEN: "lenan_access_token",
21
+ REFRESH_TOKEN: "lenan_refresh_token"
22
+ };
23
+
24
+ // src/react/auth-client.ts
25
+ var AuthClient = class {
26
+ config;
27
+ endpoints;
28
+ accessTokenKey;
29
+ refreshTokenKey;
30
+ isRefreshing = false;
31
+ refreshPromise = null;
32
+ constructor(config, endpoints) {
33
+ this.config = config;
34
+ this.endpoints = { ...DEFAULT_AUTH_ENDPOINTS, ...endpoints };
35
+ this.accessTokenKey = config.accessTokenKey ?? TOKEN_STORAGE_KEYS.ACCESS_TOKEN;
36
+ this.refreshTokenKey = config.refreshTokenKey ?? TOKEN_STORAGE_KEYS.REFRESH_TOKEN;
37
+ }
38
+ /**
39
+ * Make an authenticated request
40
+ * Automatically attaches access token and handles 401 refresh
41
+ */
42
+ async request(path, options = {}) {
43
+ const accessToken = await this.getAccessToken();
44
+ const headers = {
45
+ "Content-Type": "application/json",
46
+ ...this.config.headers,
47
+ ...options.headers || {}
48
+ };
49
+ if (accessToken) {
50
+ headers["Authorization"] = `Bearer ${accessToken}`;
51
+ }
52
+ const response = await fetch(`${this.config.baseUrl}${path}`, {
53
+ ...options,
54
+ headers
55
+ });
56
+ if (response.status === 401 && accessToken) {
57
+ const refreshed = await this.tryRefreshToken();
58
+ if (refreshed) {
59
+ headers["Authorization"] = `Bearer ${refreshed.accessToken}`;
60
+ const retryResponse = await fetch(`${this.config.baseUrl}${path}`, {
61
+ ...options,
62
+ headers
63
+ });
64
+ return this.handleResponse(retryResponse);
65
+ }
66
+ }
67
+ return this.handleResponse(response);
68
+ }
69
+ /**
70
+ * Login with email and password
71
+ */
72
+ async login(email, password) {
73
+ const response = await fetch(
74
+ `${this.config.baseUrl}${this.endpoints.login}`,
75
+ {
76
+ method: "POST",
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ ...this.config.headers
80
+ },
81
+ body: JSON.stringify({ email, password })
82
+ }
83
+ );
84
+ const tokens = await this.handleResponse(response);
85
+ await this.storeTokens(tokens);
86
+ return tokens;
87
+ }
88
+ /**
89
+ * Register a new user
90
+ */
91
+ async register(email, password, confirmPassword) {
92
+ const response = await fetch(
93
+ `${this.config.baseUrl}${this.endpoints.register}`,
94
+ {
95
+ method: "POST",
96
+ headers: {
97
+ "Content-Type": "application/json",
98
+ ...this.config.headers
99
+ },
100
+ body: JSON.stringify({
101
+ email,
102
+ password,
103
+ confirmPassword: confirmPassword ?? password
104
+ })
105
+ }
106
+ );
107
+ const tokens = await this.handleResponse(response);
108
+ await this.storeTokens(tokens);
109
+ return tokens;
110
+ }
111
+ /**
112
+ * Refresh the access token
113
+ */
114
+ async refresh() {
115
+ const refreshToken = await this.getRefreshToken();
116
+ if (!refreshToken) {
117
+ return null;
118
+ }
119
+ try {
120
+ const response = await fetch(
121
+ `${this.config.baseUrl}${this.endpoints.refresh}`,
122
+ {
123
+ method: "POST",
124
+ headers: {
125
+ "Content-Type": "application/json",
126
+ ...this.config.headers
127
+ },
128
+ body: JSON.stringify({ refreshToken })
129
+ }
130
+ );
131
+ if (!response.ok) {
132
+ await this.clearTokens();
133
+ return null;
134
+ }
135
+ const tokens = await response.json();
136
+ await this.storeTokens(tokens);
137
+ return tokens;
138
+ } catch {
139
+ await this.clearTokens();
140
+ return null;
141
+ }
142
+ }
143
+ /**
144
+ * Logout the user
145
+ */
146
+ async logout() {
147
+ const accessToken = await this.getAccessToken();
148
+ if (accessToken) {
149
+ try {
150
+ await fetch(`${this.config.baseUrl}${this.endpoints.logout}`, {
151
+ method: "POST",
152
+ headers: {
153
+ "Content-Type": "application/json",
154
+ Authorization: `Bearer ${accessToken}`,
155
+ ...this.config.headers
156
+ }
157
+ });
158
+ } catch {
159
+ }
160
+ }
161
+ await this.clearTokens();
162
+ }
163
+ /**
164
+ * Get the current user
165
+ */
166
+ async me() {
167
+ const accessToken = await this.getAccessToken();
168
+ if (!accessToken) {
169
+ return null;
170
+ }
171
+ try {
172
+ const response = await this.request(this.endpoints.me);
173
+ return response;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+ /**
179
+ * Check if user has tokens stored
180
+ */
181
+ async hasTokens() {
182
+ const accessToken = await this.getAccessToken();
183
+ return !!accessToken;
184
+ }
185
+ /**
186
+ * Get stored tokens
187
+ */
188
+ async getTokens() {
189
+ const [accessToken, refreshToken] = await Promise.all([
190
+ this.getAccessToken(),
191
+ this.getRefreshToken()
192
+ ]);
193
+ if (!accessToken || !refreshToken) {
194
+ return null;
195
+ }
196
+ return { accessToken, refreshToken };
197
+ }
198
+ async tryRefreshToken() {
199
+ if (this.isRefreshing) {
200
+ return this.refreshPromise;
201
+ }
202
+ this.isRefreshing = true;
203
+ this.refreshPromise = this.refresh();
204
+ try {
205
+ const result = await this.refreshPromise;
206
+ return result;
207
+ } finally {
208
+ this.isRefreshing = false;
209
+ this.refreshPromise = null;
210
+ }
211
+ }
212
+ async handleResponse(response) {
213
+ if (!response.ok) {
214
+ let errorMessage = `Request failed with status ${response.status}`;
215
+ try {
216
+ const errorBody = await response.json();
217
+ if (typeof errorBody === "object" && errorBody !== null) {
218
+ const body = errorBody;
219
+ if (typeof body.message === "string") {
220
+ errorMessage = body.message;
221
+ } else if (typeof body.error === "string") {
222
+ errorMessage = body.error;
223
+ }
224
+ }
225
+ } catch {
226
+ }
227
+ throw new Error(errorMessage);
228
+ }
229
+ const text = await response.text();
230
+ if (!text) {
231
+ return {};
232
+ }
233
+ return JSON.parse(text);
234
+ }
235
+ async getAccessToken() {
236
+ return Promise.resolve(
237
+ this.config.tokenStorage.getItem(this.accessTokenKey)
238
+ );
239
+ }
240
+ async getRefreshToken() {
241
+ return Promise.resolve(
242
+ this.config.tokenStorage.getItem(this.refreshTokenKey)
243
+ );
244
+ }
245
+ async storeTokens(tokens) {
246
+ await Promise.all([
247
+ this.config.tokenStorage.setItem(this.accessTokenKey, tokens.accessToken),
248
+ this.config.tokenStorage.setItem(
249
+ this.refreshTokenKey,
250
+ tokens.refreshToken
251
+ )
252
+ ]);
253
+ }
254
+ async clearTokens() {
255
+ await Promise.all([
256
+ this.config.tokenStorage.removeItem(this.accessTokenKey),
257
+ this.config.tokenStorage.removeItem(this.refreshTokenKey)
258
+ ]);
259
+ }
260
+ };
261
+ function createAuthClient(config, endpoints) {
262
+ return new AuthClient(config, endpoints);
263
+ }
264
+
265
+ // src/react/storage.ts
266
+ var LocalStorageAdapter = class {
267
+ memoryStorage = /* @__PURE__ */ new Map();
268
+ isLocalStorageAvailable;
269
+ constructor() {
270
+ this.isLocalStorageAvailable = this.checkLocalStorage();
271
+ }
272
+ checkLocalStorage() {
273
+ try {
274
+ const testKey = "__lenan_auth_test__";
275
+ localStorage.setItem(testKey, testKey);
276
+ localStorage.removeItem(testKey);
277
+ return true;
278
+ } catch {
279
+ return false;
280
+ }
281
+ }
282
+ getItem(key) {
283
+ if (this.isLocalStorageAvailable) {
284
+ return localStorage.getItem(key);
285
+ }
286
+ return this.memoryStorage.get(key) ?? null;
287
+ }
288
+ setItem(key, value) {
289
+ if (this.isLocalStorageAvailable) {
290
+ localStorage.setItem(key, value);
291
+ } else {
292
+ this.memoryStorage.set(key, value);
293
+ }
294
+ }
295
+ removeItem(key) {
296
+ if (this.isLocalStorageAvailable) {
297
+ localStorage.removeItem(key);
298
+ } else {
299
+ this.memoryStorage.delete(key);
300
+ }
301
+ }
302
+ };
303
+ var MemoryStorageAdapter = class {
304
+ storage = /* @__PURE__ */ new Map();
305
+ getItem(key) {
306
+ return this.storage.get(key) ?? null;
307
+ }
308
+ setItem(key, value) {
309
+ this.storage.set(key, value);
310
+ }
311
+ removeItem(key) {
312
+ this.storage.delete(key);
313
+ }
314
+ clear() {
315
+ this.storage.clear();
316
+ }
317
+ };
318
+
319
+ // src/react/auth-provider.tsx
320
+ import { jsx } from "react/jsx-runtime";
321
+ var AuthContext = createContext(null);
322
+ function AuthProvider({
323
+ baseUrl,
324
+ tokenStorage,
325
+ endpoints,
326
+ headers,
327
+ children,
328
+ onAuthStateChange,
329
+ autoRefresh = true
330
+ }) {
331
+ const [state, setState] = useState({
332
+ user: null,
333
+ tokens: null,
334
+ isLoading: true,
335
+ isAuthenticated: false,
336
+ error: null
337
+ });
338
+ const client = useMemo(() => {
339
+ const storage = tokenStorage ?? new LocalStorageAdapter();
340
+ return new AuthClient(
341
+ { baseUrl, tokenStorage: storage, headers },
342
+ endpoints
343
+ );
344
+ }, [baseUrl, tokenStorage, endpoints, headers]);
345
+ const updateState = useCallback(
346
+ (updates) => {
347
+ setState((prev) => {
348
+ const newState = { ...prev, ...updates };
349
+ onAuthStateChange?.(newState);
350
+ return newState;
351
+ });
352
+ },
353
+ [onAuthStateChange]
354
+ );
355
+ useEffect(() => {
356
+ const initAuth = async () => {
357
+ try {
358
+ const hasTokens = await client.hasTokens();
359
+ if (!hasTokens) {
360
+ updateState({ isLoading: false });
361
+ return;
362
+ }
363
+ const user = await client.me();
364
+ if (user) {
365
+ const tokens = await client.getTokens();
366
+ updateState({
367
+ user,
368
+ tokens,
369
+ isAuthenticated: true,
370
+ isLoading: false
371
+ });
372
+ } else if (autoRefresh) {
373
+ const newTokens = await client.refresh();
374
+ if (newTokens) {
375
+ const refreshedUser = await client.me();
376
+ updateState({
377
+ user: refreshedUser,
378
+ tokens: newTokens,
379
+ isAuthenticated: !!refreshedUser,
380
+ isLoading: false
381
+ });
382
+ } else {
383
+ updateState({ isLoading: false });
384
+ }
385
+ } else {
386
+ updateState({ isLoading: false });
387
+ }
388
+ } catch (error) {
389
+ updateState({
390
+ isLoading: false,
391
+ error: error instanceof Error ? error.message : "Failed to initialize auth"
392
+ });
393
+ }
394
+ };
395
+ initAuth();
396
+ }, [client, autoRefresh, updateState]);
397
+ const login = useCallback(
398
+ async (email, password) => {
399
+ updateState({ isLoading: true, error: null });
400
+ try {
401
+ const tokens = await client.login(email, password);
402
+ const user = await client.me();
403
+ updateState({
404
+ user,
405
+ tokens,
406
+ isAuthenticated: true,
407
+ isLoading: false
408
+ });
409
+ } catch (error) {
410
+ updateState({
411
+ isLoading: false,
412
+ error: error instanceof Error ? error.message : "Login failed"
413
+ });
414
+ throw error;
415
+ }
416
+ },
417
+ [client, updateState]
418
+ );
419
+ const register = useCallback(
420
+ async (email, password, confirmPassword) => {
421
+ updateState({ isLoading: true, error: null });
422
+ try {
423
+ const tokens = await client.register(email, password, confirmPassword);
424
+ const user = await client.me();
425
+ updateState({
426
+ user,
427
+ tokens,
428
+ isAuthenticated: true,
429
+ isLoading: false
430
+ });
431
+ } catch (error) {
432
+ updateState({
433
+ isLoading: false,
434
+ error: error instanceof Error ? error.message : "Registration failed"
435
+ });
436
+ throw error;
437
+ }
438
+ },
439
+ [client, updateState]
440
+ );
441
+ const logout = useCallback(async () => {
442
+ updateState({ isLoading: true, error: null });
443
+ try {
444
+ await client.logout();
445
+ updateState({
446
+ user: null,
447
+ tokens: null,
448
+ isAuthenticated: false,
449
+ isLoading: false
450
+ });
451
+ } catch (error) {
452
+ updateState({
453
+ user: null,
454
+ tokens: null,
455
+ isAuthenticated: false,
456
+ isLoading: false,
457
+ error: error instanceof Error ? error.message : "Logout failed"
458
+ });
459
+ }
460
+ }, [client, updateState]);
461
+ const refreshToken = useCallback(async () => {
462
+ try {
463
+ const tokens = await client.refresh();
464
+ if (tokens) {
465
+ const user = await client.me();
466
+ updateState({
467
+ user,
468
+ tokens,
469
+ isAuthenticated: !!user
470
+ });
471
+ } else {
472
+ updateState({
473
+ user: null,
474
+ tokens: null,
475
+ isAuthenticated: false
476
+ });
477
+ }
478
+ } catch (error) {
479
+ updateState({
480
+ user: null,
481
+ tokens: null,
482
+ isAuthenticated: false,
483
+ error: error instanceof Error ? error.message : "Token refresh failed"
484
+ });
485
+ }
486
+ }, [client, updateState]);
487
+ const clearError = useCallback(() => {
488
+ updateState({ error: null });
489
+ }, [updateState]);
490
+ const contextValue = useMemo(
491
+ () => ({
492
+ ...state,
493
+ login,
494
+ register,
495
+ logout,
496
+ refreshToken,
497
+ clearError,
498
+ client
499
+ }),
500
+ [state, login, register, logout, refreshToken, clearError, client]
501
+ );
502
+ return /* @__PURE__ */ jsx(AuthContext.Provider, { value: contextValue, children });
503
+ }
504
+ function useAuth() {
505
+ const context = useContext(AuthContext);
506
+ if (!context) {
507
+ throw new Error("useAuth must be used within an AuthProvider");
508
+ }
509
+ return context;
510
+ }
511
+ function useUser() {
512
+ const { user } = useAuth();
513
+ return user;
514
+ }
515
+ function useIsAuthenticated() {
516
+ const { isAuthenticated, isLoading } = useAuth();
517
+ return isAuthenticated && !isLoading;
518
+ }
519
+ export {
520
+ AuthClient,
521
+ AuthProvider,
522
+ DEFAULT_AUTH_ENDPOINTS,
523
+ LocalStorageAdapter,
524
+ MemoryStorageAdapter,
525
+ TOKEN_STORAGE_KEYS,
526
+ createAuthClient,
527
+ useAuth,
528
+ useIsAuthenticated,
529
+ useUser
530
+ };
531
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/auth-provider.tsx","../../src/shared/types.ts","../../src/react/auth-client.ts","../../src/react/storage.ts"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport type {\n AuthEndpoints,\n AuthState,\n BaseUser,\n TokenStorage,\n} from \"../shared\";\nimport { AuthClient } from \"./auth-client\";\nimport { LocalStorageAdapter } from \"./storage\";\n\n/**\n * Auth context value interface\n */\nexport interface AuthContextValue<\n TUser extends BaseUser = BaseUser,\n> extends AuthState<TUser> {\n /** Login with email and password */\n login: (email: string, password: string) => Promise<void>;\n /** Register a new user */\n register: (\n email: string,\n password: string,\n confirmPassword?: string,\n ) => Promise<void>;\n /** Logout the current user */\n logout: () => Promise<void>;\n /** Manually refresh tokens */\n refreshToken: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n /** The auth client instance */\n client: AuthClient;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n/**\n * Auth provider configuration\n */\nexport interface AuthProviderProps {\n /** Base URL for auth API endpoints */\n baseUrl: string;\n /** Token storage implementation (defaults to localStorage) */\n tokenStorage?: TokenStorage;\n /** Custom auth endpoints */\n endpoints?: Partial<AuthEndpoints>;\n /** Custom headers for requests */\n headers?: Record<string, string>;\n /** Children to render */\n children: ReactNode;\n /** Callback when auth state changes */\n onAuthStateChange?: (state: AuthState) => void;\n /** Whether to automatically refresh token on mount */\n autoRefresh?: boolean;\n}\n\n/**\n * Auth provider component\n * Wraps your app and provides auth state and methods via context\n *\n * @example\n * ```tsx\n * <AuthProvider baseUrl=\"https://api.example.com\">\n * <App />\n * </AuthProvider>\n * ```\n */\nexport function AuthProvider<TUser extends BaseUser = BaseUser>({\n baseUrl,\n tokenStorage,\n endpoints,\n headers,\n children,\n onAuthStateChange,\n autoRefresh = true,\n}: AuthProviderProps) {\n const [state, setState] = useState<AuthState<TUser>>({\n user: null,\n tokens: null,\n isLoading: true,\n isAuthenticated: false,\n error: null,\n });\n\n // Create the auth client\n const client = useMemo(() => {\n const storage = tokenStorage ?? new LocalStorageAdapter();\n return new AuthClient(\n { baseUrl, tokenStorage: storage, headers },\n endpoints,\n );\n }, [baseUrl, tokenStorage, endpoints, headers]);\n\n // Update state helper\n const updateState = useCallback(\n (updates: Partial<AuthState<TUser>>) => {\n setState((prev) => {\n const newState = { ...prev, ...updates };\n onAuthStateChange?.(newState);\n return newState;\n });\n },\n [onAuthStateChange],\n );\n\n // Initialize auth state on mount\n useEffect(() => {\n const initAuth = async () => {\n try {\n const hasTokens = await client.hasTokens();\n\n if (!hasTokens) {\n updateState({ isLoading: false });\n return;\n }\n\n // Try to get current user\n const user = await client.me<TUser>();\n\n if (user) {\n const tokens = await client.getTokens();\n updateState({\n user,\n tokens,\n isAuthenticated: true,\n isLoading: false,\n });\n } else if (autoRefresh) {\n // Try to refresh tokens\n const newTokens = await client.refresh();\n if (newTokens) {\n const refreshedUser = await client.me<TUser>();\n updateState({\n user: refreshedUser,\n tokens: newTokens,\n isAuthenticated: !!refreshedUser,\n isLoading: false,\n });\n } else {\n updateState({ isLoading: false });\n }\n } else {\n updateState({ isLoading: false });\n }\n } catch (error) {\n updateState({\n isLoading: false,\n error:\n error instanceof Error\n ? error.message\n : \"Failed to initialize auth\",\n });\n }\n };\n\n initAuth();\n }, [client, autoRefresh, updateState]);\n\n // Login handler\n const login = useCallback(\n async (email: string, password: string) => {\n updateState({ isLoading: true, error: null });\n\n try {\n const tokens = await client.login(email, password);\n const user = await client.me<TUser>();\n\n updateState({\n user,\n tokens,\n isAuthenticated: true,\n isLoading: false,\n });\n } catch (error) {\n updateState({\n isLoading: false,\n error: error instanceof Error ? error.message : \"Login failed\",\n });\n throw error;\n }\n },\n [client, updateState],\n );\n\n // Register handler\n const register = useCallback(\n async (email: string, password: string, confirmPassword?: string) => {\n updateState({ isLoading: true, error: null });\n\n try {\n const tokens = await client.register(email, password, confirmPassword);\n const user = await client.me<TUser>();\n\n updateState({\n user,\n tokens,\n isAuthenticated: true,\n isLoading: false,\n });\n } catch (error) {\n updateState({\n isLoading: false,\n error: error instanceof Error ? error.message : \"Registration failed\",\n });\n throw error;\n }\n },\n [client, updateState],\n );\n\n // Logout handler\n const logout = useCallback(async () => {\n updateState({ isLoading: true, error: null });\n\n try {\n await client.logout();\n\n updateState({\n user: null,\n tokens: null,\n isAuthenticated: false,\n isLoading: false,\n });\n } catch (error) {\n // Still clear local state even if logout API fails\n updateState({\n user: null,\n tokens: null,\n isAuthenticated: false,\n isLoading: false,\n error: error instanceof Error ? error.message : \"Logout failed\",\n });\n }\n }, [client, updateState]);\n\n // Refresh token handler\n const refreshToken = useCallback(async () => {\n try {\n const tokens = await client.refresh();\n\n if (tokens) {\n const user = await client.me<TUser>();\n updateState({\n user,\n tokens,\n isAuthenticated: !!user,\n });\n } else {\n updateState({\n user: null,\n tokens: null,\n isAuthenticated: false,\n });\n }\n } catch (error) {\n updateState({\n user: null,\n tokens: null,\n isAuthenticated: false,\n error: error instanceof Error ? error.message : \"Token refresh failed\",\n });\n }\n }, [client, updateState]);\n\n // Clear error handler\n const clearError = useCallback(() => {\n updateState({ error: null });\n }, [updateState]);\n\n // Memoize context value\n const contextValue = useMemo<AuthContextValue<TUser>>(\n () => ({\n ...state,\n login,\n register,\n logout,\n refreshToken,\n clearError,\n client,\n }),\n [state, login, register, logout, refreshToken, clearError, client],\n );\n\n return (\n <AuthContext.Provider value={contextValue as AuthContextValue}>\n {children}\n </AuthContext.Provider>\n );\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n *\n * @example\n * ```tsx\n * function LoginButton() {\n * const { login, isLoading, error } = useAuth();\n *\n * const handleLogin = async () => {\n * await login('user@example.com', 'password');\n * };\n *\n * return (\n * <button onClick={handleLogin} disabled={isLoading}>\n * {isLoading ? 'Logging in...' : 'Login'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useAuth<\n TUser extends BaseUser = BaseUser,\n>(): AuthContextValue<TUser> {\n const context = useContext(AuthContext);\n\n if (!context) {\n throw new Error(\"useAuth must be used within an AuthProvider\");\n }\n\n return context as AuthContextValue<TUser>;\n}\n\n/**\n * Hook to get the current authenticated user\n * Returns null if not authenticated\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const user = useUser();\n *\n * if (!user) {\n * return <div>Not logged in</div>;\n * }\n *\n * return <div>Welcome, {user.email}!</div>;\n * }\n * ```\n */\nexport function useUser<TUser extends BaseUser = BaseUser>(): TUser | null {\n const { user } = useAuth<TUser>();\n return user;\n}\n\n/**\n * Hook to check if user is authenticated\n *\n * @example\n * ```tsx\n * function ProtectedRoute({ children }) {\n * const isAuthenticated = useIsAuthenticated();\n *\n * if (!isAuthenticated) {\n * return <Navigate to=\"/login\" />;\n * }\n *\n * return children;\n * }\n * ```\n */\nexport function useIsAuthenticated(): boolean {\n const { isAuthenticated, isLoading } = useAuth();\n return isAuthenticated && !isLoading;\n}\n","/**\n * JWT payload structure\n */\nexport interface JwtPayload {\n /** User ID (subject) */\n sub: string;\n /** User email */\n email: string;\n /** Issued at timestamp */\n iat?: number;\n /** Expiration timestamp */\n exp?: number;\n}\n\n/**\n * Token response containing access and refresh tokens\n */\nexport interface AuthTokens {\n /** Short-lived access token */\n accessToken: string;\n /** Long-lived refresh token */\n refreshToken: string;\n}\n\n/**\n * Base user interface - minimal fields required by the auth system\n * Consumers should extend this with their own user properties\n */\nexport interface BaseUser {\n /** Unique user identifier */\n id: string;\n /** User email address */\n email: string;\n}\n\n/**\n * User with password hash - used internally for authentication\n */\nexport interface UserWithPassword extends BaseUser {\n /** Bcrypt hashed password */\n passwordHash: string;\n}\n\n/**\n * User with refresh token - used for token refresh operations\n */\nexport interface UserWithRefreshToken extends BaseUser {\n /** Hashed refresh token (nullable when logged out) */\n hashedRefreshToken: string | null;\n}\n\n/**\n * Full auth user interface combining all auth-related fields\n */\nexport interface AuthUser extends BaseUser {\n passwordHash: string;\n hashedRefreshToken: string | null;\n}\n\n/**\n * Login credentials\n */\nexport interface LoginCredentials {\n email: string;\n password: string;\n}\n\n/**\n * Registration data\n */\nexport interface RegisterData {\n email: string;\n password: string;\n}\n\n/**\n * Auth state for frontend applications\n */\nexport interface AuthState<TUser extends BaseUser = BaseUser> {\n /** Current authenticated user or null */\n user: TUser | null;\n /** Current tokens or null */\n tokens: AuthTokens | null;\n /** Whether auth state is being loaded/validated */\n isLoading: boolean;\n /** Whether user is authenticated */\n isAuthenticated: boolean;\n /** Auth error if any */\n error: string | null;\n}\n\n/**\n * Token storage interface for frontend applications\n * Implement this to use different storage backends (localStorage, SecureStore, etc.)\n */\nexport interface TokenStorage {\n getItem(key: string): Promise<string | null> | string | null;\n setItem(key: string, value: string): Promise<void> | void;\n removeItem(key: string): Promise<void> | void;\n}\n\n/**\n * Auth client configuration for frontend HTTP client\n */\nexport interface AuthClientConfig {\n /** Base URL for auth API endpoints */\n baseUrl: string;\n /** Token storage implementation */\n tokenStorage: TokenStorage;\n /** Custom headers to include in requests */\n headers?: Record<string, string>;\n /** Access token storage key (default: 'lenan_access_token') */\n accessTokenKey?: string;\n /** Refresh token storage key (default: 'lenan_refresh_token') */\n refreshTokenKey?: string;\n}\n\n/**\n * Auth API endpoints configuration\n */\nexport interface AuthEndpoints {\n login: string;\n register: string;\n refresh: string;\n logout: string;\n me: string;\n}\n\n/**\n * Default auth endpoints\n */\nexport const DEFAULT_AUTH_ENDPOINTS: AuthEndpoints = {\n login: \"/auth/login\",\n register: \"/auth/register\",\n refresh: \"/auth/refresh\",\n logout: \"/auth/logout\",\n me: \"/auth/me\",\n};\n\n/**\n * Default token storage keys\n */\nexport const TOKEN_STORAGE_KEYS = {\n ACCESS_TOKEN: \"lenan_access_token\",\n REFRESH_TOKEN: \"lenan_refresh_token\",\n} as const;\n","import type {\n AuthClientConfig,\n AuthEndpoints,\n AuthTokens,\n BaseUser,\n} from \"../shared\";\nimport { DEFAULT_AUTH_ENDPOINTS, TOKEN_STORAGE_KEYS } from \"../shared\";\n\n/**\n * Auth HTTP client for making authenticated requests\n * Handles token management, automatic refresh, and request/response handling\n */\nexport class AuthClient {\n private config: AuthClientConfig;\n private endpoints: AuthEndpoints;\n private accessTokenKey: string;\n private refreshTokenKey: string;\n private isRefreshing = false;\n private refreshPromise: Promise<AuthTokens | null> | null = null;\n\n constructor(config: AuthClientConfig, endpoints?: Partial<AuthEndpoints>) {\n this.config = config;\n this.endpoints = { ...DEFAULT_AUTH_ENDPOINTS, ...endpoints };\n this.accessTokenKey =\n config.accessTokenKey ?? TOKEN_STORAGE_KEYS.ACCESS_TOKEN;\n this.refreshTokenKey =\n config.refreshTokenKey ?? TOKEN_STORAGE_KEYS.REFRESH_TOKEN;\n }\n\n /**\n * Make an authenticated request\n * Automatically attaches access token and handles 401 refresh\n */\n async request<T = any>(path: string, options: RequestInit = {}): Promise<T> {\n const accessToken = await this.getAccessToken();\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.config.headers,\n ...((options.headers as Record<string, string>) || {}),\n };\n\n if (accessToken) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n }\n\n const response = await fetch(`${this.config.baseUrl}${path}`, {\n ...options,\n headers,\n });\n\n // Handle 401 - try to refresh token\n if (response.status === 401 && accessToken) {\n const refreshed = await this.tryRefreshToken();\n if (refreshed) {\n // Retry the request with new token\n headers[\"Authorization\"] = `Bearer ${refreshed.accessToken}`;\n const retryResponse = await fetch(`${this.config.baseUrl}${path}`, {\n ...options,\n headers,\n });\n return this.handleResponse(retryResponse);\n }\n }\n\n return this.handleResponse(response);\n }\n\n /**\n * Login with email and password\n */\n async login(email: string, password: string): Promise<AuthTokens> {\n const response = await fetch(\n `${this.config.baseUrl}${this.endpoints.login}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.config.headers,\n },\n body: JSON.stringify({ email, password }),\n },\n );\n\n const tokens = await this.handleResponse<AuthTokens>(response);\n await this.storeTokens(tokens);\n return tokens;\n }\n\n /**\n * Register a new user\n */\n async register(\n email: string,\n password: string,\n confirmPassword?: string,\n ): Promise<AuthTokens> {\n const response = await fetch(\n `${this.config.baseUrl}${this.endpoints.register}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.config.headers,\n },\n body: JSON.stringify({\n email,\n password,\n confirmPassword: confirmPassword ?? password,\n }),\n },\n );\n\n const tokens = await this.handleResponse<AuthTokens>(response);\n await this.storeTokens(tokens);\n return tokens;\n }\n\n /**\n * Refresh the access token\n */\n async refresh(): Promise<AuthTokens | null> {\n const refreshToken = await this.getRefreshToken();\n if (!refreshToken) {\n return null;\n }\n\n try {\n const response = await fetch(\n `${this.config.baseUrl}${this.endpoints.refresh}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...this.config.headers,\n },\n body: JSON.stringify({ refreshToken }),\n },\n );\n\n if (!response.ok) {\n await this.clearTokens();\n return null;\n }\n\n const tokens = (await response.json()) as AuthTokens;\n await this.storeTokens(tokens);\n return tokens;\n } catch {\n await this.clearTokens();\n return null;\n }\n }\n\n /**\n * Logout the user\n */\n async logout(): Promise<void> {\n const accessToken = await this.getAccessToken();\n\n if (accessToken) {\n try {\n await fetch(`${this.config.baseUrl}${this.endpoints.logout}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n ...this.config.headers,\n },\n });\n } catch {\n // Ignore logout errors - still clear local tokens\n }\n }\n\n await this.clearTokens();\n }\n\n /**\n * Get the current user\n */\n async me<TUser extends BaseUser = BaseUser>(): Promise<TUser | null> {\n const accessToken = await this.getAccessToken();\n if (!accessToken) {\n return null;\n }\n\n try {\n const response = await this.request<TUser>(this.endpoints.me);\n return response;\n } catch {\n return null;\n }\n }\n\n /**\n * Check if user has tokens stored\n */\n async hasTokens(): Promise<boolean> {\n const accessToken = await this.getAccessToken();\n return !!accessToken;\n }\n\n /**\n * Get stored tokens\n */\n async getTokens(): Promise<AuthTokens | null> {\n const [accessToken, refreshToken] = await Promise.all([\n this.getAccessToken(),\n this.getRefreshToken(),\n ]);\n\n if (!accessToken || !refreshToken) {\n return null;\n }\n\n return { accessToken, refreshToken };\n }\n\n private async tryRefreshToken(): Promise<AuthTokens | null> {\n // Prevent multiple simultaneous refresh attempts\n if (this.isRefreshing) {\n return this.refreshPromise;\n }\n\n this.isRefreshing = true;\n this.refreshPromise = this.refresh();\n\n try {\n const result = await this.refreshPromise;\n return result;\n } finally {\n this.isRefreshing = false;\n this.refreshPromise = null;\n }\n }\n\n private async handleResponse<T>(response: Response): Promise<T> {\n if (!response.ok) {\n let errorMessage = `Request failed with status ${response.status}`;\n\n try {\n const errorBody = await response.json();\n if (typeof errorBody === \"object\" && errorBody !== null) {\n const body = errorBody as Record<string, unknown>;\n if (typeof body.message === \"string\") {\n errorMessage = body.message;\n } else if (typeof body.error === \"string\") {\n errorMessage = body.error;\n }\n }\n } catch {\n // Ignore JSON parse errors\n }\n\n throw new Error(errorMessage);\n }\n\n // Handle empty responses\n const text = await response.text();\n if (!text) {\n return {} as T;\n }\n\n return JSON.parse(text) as T;\n }\n\n private async getAccessToken(): Promise<string | null> {\n return Promise.resolve(\n this.config.tokenStorage.getItem(this.accessTokenKey),\n );\n }\n\n private async getRefreshToken(): Promise<string | null> {\n return Promise.resolve(\n this.config.tokenStorage.getItem(this.refreshTokenKey),\n );\n }\n\n private async storeTokens(tokens: AuthTokens): Promise<void> {\n await Promise.all([\n this.config.tokenStorage.setItem(this.accessTokenKey, tokens.accessToken),\n this.config.tokenStorage.setItem(\n this.refreshTokenKey,\n tokens.refreshToken,\n ),\n ]);\n }\n\n private async clearTokens(): Promise<void> {\n await Promise.all([\n this.config.tokenStorage.removeItem(this.accessTokenKey),\n this.config.tokenStorage.removeItem(this.refreshTokenKey),\n ]);\n }\n}\n\n/**\n * Create an auth client instance\n */\nexport function createAuthClient(\n config: AuthClientConfig,\n endpoints?: Partial<AuthEndpoints>,\n): AuthClient {\n return new AuthClient(config, endpoints);\n}\n","import type { TokenStorage } from \"../shared\";\n\n/**\n * localStorage-based token storage for web browsers\n * Falls back to in-memory storage if localStorage is not available\n */\nexport class LocalStorageAdapter implements TokenStorage {\n private memoryStorage: Map<string, string> = new Map();\n private isLocalStorageAvailable: boolean;\n\n constructor() {\n this.isLocalStorageAvailable = this.checkLocalStorage();\n }\n\n private checkLocalStorage(): boolean {\n try {\n const testKey = \"__lenan_auth_test__\";\n localStorage.setItem(testKey, testKey);\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n\n getItem(key: string): string | null {\n if (this.isLocalStorageAvailable) {\n return localStorage.getItem(key);\n }\n return this.memoryStorage.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n if (this.isLocalStorageAvailable) {\n localStorage.setItem(key, value);\n } else {\n this.memoryStorage.set(key, value);\n }\n }\n\n removeItem(key: string): void {\n if (this.isLocalStorageAvailable) {\n localStorage.removeItem(key);\n } else {\n this.memoryStorage.delete(key);\n }\n }\n}\n\n/**\n * In-memory token storage for server-side rendering or testing\n */\nexport class MemoryStorageAdapter implements TokenStorage {\n private storage: Map<string, string> = new Map();\n\n getItem(key: string): string | null {\n return this.storage.get(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.storage.set(key, value);\n }\n\n removeItem(key: string): void {\n this.storage.delete(key);\n }\n\n clear(): void {\n this.storage.clear();\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react/native/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SecureStoreAdapter: () => SecureStoreAdapter,
24
+ createSecureStoreAdapter: () => createSecureStoreAdapter
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var SecureStoreAdapter = class {
28
+ secureStore;
29
+ constructor(secureStoreModule) {
30
+ if (secureStoreModule) {
31
+ this.secureStore = secureStoreModule;
32
+ } else {
33
+ try {
34
+ this.secureStore = require("expo-secure-store");
35
+ } catch {
36
+ throw new Error(
37
+ "expo-secure-store is not installed. Please install it with: npx expo install expo-secure-store"
38
+ );
39
+ }
40
+ }
41
+ }
42
+ async getItem(key) {
43
+ return this.secureStore.getItemAsync(key);
44
+ }
45
+ async setItem(key, value) {
46
+ return this.secureStore.setItemAsync(key, value);
47
+ }
48
+ async removeItem(key) {
49
+ return this.secureStore.deleteItemAsync(key);
50
+ }
51
+ };
52
+ function createSecureStoreAdapter(secureStoreModule) {
53
+ return new SecureStoreAdapter(secureStoreModule);
54
+ }
55
+ // Annotate the CommonJS export names for ESM import in node:
56
+ 0 && (module.exports = {
57
+ SecureStoreAdapter,
58
+ createSecureStoreAdapter
59
+ });
60
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/react/native/index.ts"],"sourcesContent":["import type { TokenStorage } from \"../../shared\";\n\n// Type declaration for expo-secure-store\n// This allows the adapter to work without bundling the actual dependency\ntype SecureStoreModule = {\n getItemAsync: (key: string) => Promise<string | null>;\n setItemAsync: (key: string, value: string) => Promise<void>;\n deleteItemAsync: (key: string) => Promise<void>;\n};\n\n/**\n * Expo SecureStore adapter for React Native\n * Provides secure token storage using expo-secure-store\n *\n * @example\n * ```tsx\n * import { SecureStoreAdapter } from '@lenan-soft/auth/react/native';\n * import { AuthProvider } from '@lenan-soft/auth/react';\n *\n * export default function App() {\n * return (\n * <AuthProvider\n * baseUrl=\"https://api.example.com\"\n * tokenStorage={new SecureStoreAdapter()}\n * >\n * <RootNavigator />\n * </AuthProvider>\n * );\n * }\n * ```\n */\nexport class SecureStoreAdapter implements TokenStorage {\n private secureStore: SecureStoreModule;\n\n constructor(secureStoreModule?: SecureStoreModule) {\n if (secureStoreModule) {\n this.secureStore = secureStoreModule;\n } else {\n // Dynamic import to avoid bundling expo-secure-store\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n try {\n this.secureStore = require(\"expo-secure-store\") as SecureStoreModule;\n } catch {\n throw new Error(\n \"expo-secure-store is not installed. Please install it with: npx expo install expo-secure-store\",\n );\n }\n }\n }\n\n async getItem(key: string): Promise<string | null> {\n return this.secureStore.getItemAsync(key);\n }\n\n async setItem(key: string, value: string): Promise<void> {\n return this.secureStore.setItemAsync(key, value);\n }\n\n async removeItem(key: string): Promise<void> {\n return this.secureStore.deleteItemAsync(key);\n }\n}\n\n/**\n * Create a SecureStore adapter with an injected module\n * Useful for when you want to pass the module reference explicitly\n */\nexport function createSecureStoreAdapter(\n secureStoreModule: SecureStoreModule,\n): SecureStoreAdapter {\n return new SecureStoreAdapter(secureStoreModule);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BO,IAAM,qBAAN,MAAiD;AAAA,EAC9C;AAAA,EAER,YAAY,mBAAuC;AACjD,QAAI,mBAAmB;AACrB,WAAK,cAAc;AAAA,IACrB,OAAO;AAGL,UAAI;AACF,aAAK,cAAc,QAAQ,mBAAmB;AAAA,MAChD,QAAQ;AACN,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,KAAqC;AACjD,WAAO,KAAK,YAAY,aAAa,GAAG;AAAA,EAC1C;AAAA,EAEA,MAAM,QAAQ,KAAa,OAA8B;AACvD,WAAO,KAAK,YAAY,aAAa,KAAK,KAAK;AAAA,EACjD;AAAA,EAEA,MAAM,WAAW,KAA4B;AAC3C,WAAO,KAAK,YAAY,gBAAgB,GAAG;AAAA,EAC7C;AACF;AAMO,SAAS,yBACd,mBACoB;AACpB,SAAO,IAAI,mBAAmB,iBAAiB;AACjD;","names":[]}