@pixygon/auth 1.0.0
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/README.md +267 -0
- package/dist/chunk-E34M2RJD.mjs +1003 -0
- package/dist/components/index.d.mts +14 -0
- package/dist/components/index.d.ts +14 -0
- package/dist/components/index.js +1720 -0
- package/dist/components/index.mjs +1646 -0
- package/dist/index-CIK2MKl9.d.mts +201 -0
- package/dist/index-CIK2MKl9.d.ts +201 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +1037 -0
- package/dist/index.mjs +28 -0
- package/package.json +58 -0
- package/src/api/client.ts +358 -0
- package/src/components/ForgotPasswordForm.tsx +410 -0
- package/src/components/LoginForm.tsx +323 -0
- package/src/components/PixygonAuth.tsx +170 -0
- package/src/components/RegisterForm.tsx +463 -0
- package/src/components/VerifyForm.tsx +411 -0
- package/src/components/index.ts +40 -0
- package/src/components/styles.ts +282 -0
- package/src/hooks/index.ts +284 -0
- package/src/hooks/useProfileSync.ts +201 -0
- package/src/index.ts +99 -0
- package/src/providers/AuthProvider.tsx +480 -0
- package/src/types/index.ts +310 -0
- package/src/utils/storage.ts +167 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1037 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AuthContext: () => AuthContext,
|
|
24
|
+
AuthProvider: () => AuthProvider,
|
|
25
|
+
createAuthApiClient: () => createAuthApiClient,
|
|
26
|
+
createTokenStorage: () => createTokenStorage,
|
|
27
|
+
useAuth: () => useAuth,
|
|
28
|
+
useAuthContext: () => useAuthContext,
|
|
29
|
+
useAuthError: () => useAuthError,
|
|
30
|
+
useAuthStatus: () => useAuthStatus,
|
|
31
|
+
useProfileSync: () => useProfileSync,
|
|
32
|
+
useRequireAuth: () => useRequireAuth,
|
|
33
|
+
useToken: () => useToken,
|
|
34
|
+
useUser: () => useUser
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/providers/AuthProvider.tsx
|
|
39
|
+
var import_react = require("react");
|
|
40
|
+
|
|
41
|
+
// src/utils/storage.ts
|
|
42
|
+
var getKeys = (appId) => ({
|
|
43
|
+
ACCESS_TOKEN: `${appId}_access_token`,
|
|
44
|
+
REFRESH_TOKEN: `${appId}_refresh_token`,
|
|
45
|
+
USER: `${appId}_user`,
|
|
46
|
+
EXPIRES_AT: `${appId}_expires_at`
|
|
47
|
+
});
|
|
48
|
+
var defaultStorage = {
|
|
49
|
+
getItem: (key) => {
|
|
50
|
+
if (typeof window === "undefined") return null;
|
|
51
|
+
return localStorage.getItem(key);
|
|
52
|
+
},
|
|
53
|
+
setItem: (key, value) => {
|
|
54
|
+
if (typeof window === "undefined") return;
|
|
55
|
+
localStorage.setItem(key, value);
|
|
56
|
+
},
|
|
57
|
+
removeItem: (key) => {
|
|
58
|
+
if (typeof window === "undefined") return;
|
|
59
|
+
localStorage.removeItem(key);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function createTokenStorage(appId, storage = defaultStorage) {
|
|
63
|
+
const keys = getKeys(appId);
|
|
64
|
+
return {
|
|
65
|
+
/**
|
|
66
|
+
* Get the stored access token
|
|
67
|
+
*/
|
|
68
|
+
getAccessToken: async () => {
|
|
69
|
+
const token = await storage.getItem(keys.ACCESS_TOKEN);
|
|
70
|
+
return token;
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* Get the stored refresh token
|
|
74
|
+
*/
|
|
75
|
+
getRefreshToken: async () => {
|
|
76
|
+
const token = await storage.getItem(keys.REFRESH_TOKEN);
|
|
77
|
+
return token;
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Get the stored user
|
|
81
|
+
*/
|
|
82
|
+
getUser: async () => {
|
|
83
|
+
const userJson = await storage.getItem(keys.USER);
|
|
84
|
+
if (!userJson) return null;
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(userJson);
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* Get token expiration time
|
|
93
|
+
*/
|
|
94
|
+
getExpiresAt: async () => {
|
|
95
|
+
const expiresAt = await storage.getItem(keys.EXPIRES_AT);
|
|
96
|
+
return expiresAt ? parseInt(expiresAt, 10) : null;
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* Check if access token is expired
|
|
100
|
+
*/
|
|
101
|
+
isTokenExpired: async () => {
|
|
102
|
+
const expiresAt = await storage.getItem(keys.EXPIRES_AT);
|
|
103
|
+
if (!expiresAt) return true;
|
|
104
|
+
return Date.now() >= parseInt(expiresAt, 10);
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Check if token will expire within threshold (in seconds)
|
|
108
|
+
*/
|
|
109
|
+
willExpireSoon: async (thresholdSeconds = 300) => {
|
|
110
|
+
const expiresAt = await storage.getItem(keys.EXPIRES_AT);
|
|
111
|
+
if (!expiresAt) return true;
|
|
112
|
+
const expiryTime = parseInt(expiresAt, 10);
|
|
113
|
+
const thresholdMs = thresholdSeconds * 1e3;
|
|
114
|
+
return Date.now() >= expiryTime - thresholdMs;
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Store tokens and user data
|
|
118
|
+
*/
|
|
119
|
+
setTokens: async (accessToken, refreshToken, expiresIn, user) => {
|
|
120
|
+
await storage.setItem(keys.ACCESS_TOKEN, accessToken);
|
|
121
|
+
if (refreshToken) {
|
|
122
|
+
await storage.setItem(keys.REFRESH_TOKEN, refreshToken);
|
|
123
|
+
}
|
|
124
|
+
if (expiresIn) {
|
|
125
|
+
const expiresAt = Date.now() + expiresIn * 1e3;
|
|
126
|
+
await storage.setItem(keys.EXPIRES_AT, expiresAt.toString());
|
|
127
|
+
}
|
|
128
|
+
await storage.setItem(keys.USER, JSON.stringify(user));
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Update tokens after refresh
|
|
132
|
+
*/
|
|
133
|
+
updateTokens: async (tokens) => {
|
|
134
|
+
await storage.setItem(keys.ACCESS_TOKEN, tokens.accessToken);
|
|
135
|
+
await storage.setItem(keys.REFRESH_TOKEN, tokens.refreshToken);
|
|
136
|
+
const expiresAt = Date.now() + tokens.expiresIn * 1e3;
|
|
137
|
+
await storage.setItem(keys.EXPIRES_AT, expiresAt.toString());
|
|
138
|
+
},
|
|
139
|
+
/**
|
|
140
|
+
* Update user data
|
|
141
|
+
*/
|
|
142
|
+
updateUser: async (user) => {
|
|
143
|
+
await storage.setItem(keys.USER, JSON.stringify(user));
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* Clear all stored auth data
|
|
147
|
+
*/
|
|
148
|
+
clear: async () => {
|
|
149
|
+
await storage.removeItem(keys.ACCESS_TOKEN);
|
|
150
|
+
await storage.removeItem(keys.REFRESH_TOKEN);
|
|
151
|
+
await storage.removeItem(keys.USER);
|
|
152
|
+
await storage.removeItem(keys.EXPIRES_AT);
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* Get all stored auth data
|
|
156
|
+
*/
|
|
157
|
+
getAll: async () => {
|
|
158
|
+
const [accessToken, refreshToken, user, expiresAt] = await Promise.all([
|
|
159
|
+
storage.getItem(keys.ACCESS_TOKEN),
|
|
160
|
+
storage.getItem(keys.REFRESH_TOKEN),
|
|
161
|
+
storage.getItem(keys.USER),
|
|
162
|
+
storage.getItem(keys.EXPIRES_AT)
|
|
163
|
+
]);
|
|
164
|
+
return {
|
|
165
|
+
accessToken,
|
|
166
|
+
refreshToken,
|
|
167
|
+
user: user ? JSON.parse(user) : null,
|
|
168
|
+
expiresAt: expiresAt ? parseInt(expiresAt, 10) : null
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/api/client.ts
|
|
175
|
+
function parseErrorCode(status, message) {
|
|
176
|
+
if (message?.toLowerCase().includes("not found")) return "USER_NOT_FOUND";
|
|
177
|
+
if (message?.toLowerCase().includes("exists")) return "USER_EXISTS";
|
|
178
|
+
if (message?.toLowerCase().includes("verified")) return "EMAIL_NOT_VERIFIED";
|
|
179
|
+
if (message?.toLowerCase().includes("invalid") && message?.toLowerCase().includes("code")) {
|
|
180
|
+
return "INVALID_VERIFICATION_CODE";
|
|
181
|
+
}
|
|
182
|
+
if (message?.toLowerCase().includes("expired") && message?.toLowerCase().includes("code")) {
|
|
183
|
+
return "EXPIRED_VERIFICATION_CODE";
|
|
184
|
+
}
|
|
185
|
+
switch (status) {
|
|
186
|
+
case 401:
|
|
187
|
+
return "INVALID_CREDENTIALS";
|
|
188
|
+
case 403:
|
|
189
|
+
return "TOKEN_INVALID";
|
|
190
|
+
case 404:
|
|
191
|
+
return "USER_NOT_FOUND";
|
|
192
|
+
case 409:
|
|
193
|
+
return "USER_EXISTS";
|
|
194
|
+
case 500:
|
|
195
|
+
return "SERVER_ERROR";
|
|
196
|
+
default:
|
|
197
|
+
return "UNKNOWN_ERROR";
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function createAuthError(status, message, details) {
|
|
201
|
+
return {
|
|
202
|
+
code: parseErrorCode(status, message),
|
|
203
|
+
message: message || "An unexpected error occurred",
|
|
204
|
+
details
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function createAuthApiClient(config, tokenStorage) {
|
|
208
|
+
let currentAccessToken = null;
|
|
209
|
+
let isRefreshing = false;
|
|
210
|
+
let refreshPromise = null;
|
|
211
|
+
const log = (...args) => {
|
|
212
|
+
if (config.debug) {
|
|
213
|
+
console.log("[PixygonAuth]", ...args);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
async function request(endpoint, options = {}) {
|
|
217
|
+
const url = `${config.baseUrl}${endpoint}`;
|
|
218
|
+
const headers = {
|
|
219
|
+
"Content-Type": "application/json",
|
|
220
|
+
...options.headers
|
|
221
|
+
};
|
|
222
|
+
if (currentAccessToken) {
|
|
223
|
+
headers["Authorization"] = `Bearer ${currentAccessToken}`;
|
|
224
|
+
}
|
|
225
|
+
log(`Request: ${options.method || "GET"} ${endpoint}`);
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(url, {
|
|
228
|
+
...options,
|
|
229
|
+
headers
|
|
230
|
+
});
|
|
231
|
+
if (response.status === 401 || response.status === 403) {
|
|
232
|
+
if (currentAccessToken && !isRefreshing) {
|
|
233
|
+
log("Token expired, attempting refresh...");
|
|
234
|
+
try {
|
|
235
|
+
await refreshTokens();
|
|
236
|
+
headers["Authorization"] = `Bearer ${currentAccessToken}`;
|
|
237
|
+
const retryResponse = await fetch(url, { ...options, headers });
|
|
238
|
+
if (!retryResponse.ok) {
|
|
239
|
+
const errorData = await retryResponse.json().catch(() => ({}));
|
|
240
|
+
throw createAuthError(retryResponse.status, errorData.message);
|
|
241
|
+
}
|
|
242
|
+
return retryResponse.json();
|
|
243
|
+
} catch (refreshError) {
|
|
244
|
+
log("Token refresh failed", refreshError);
|
|
245
|
+
throw createAuthError(401, "Session expired. Please log in again.");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
throw createAuthError(response.status, "Authentication required");
|
|
249
|
+
}
|
|
250
|
+
if (!response.ok) {
|
|
251
|
+
const errorData = await response.json().catch(() => ({}));
|
|
252
|
+
throw createAuthError(response.status, errorData.message, errorData);
|
|
253
|
+
}
|
|
254
|
+
const data = await response.json();
|
|
255
|
+
return data;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (error.code) {
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
log("Network error:", error);
|
|
261
|
+
throw {
|
|
262
|
+
code: "NETWORK_ERROR",
|
|
263
|
+
message: "Unable to connect to the server",
|
|
264
|
+
details: { originalError: error }
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async function refreshTokens() {
|
|
269
|
+
if (isRefreshing && refreshPromise) {
|
|
270
|
+
return refreshPromise;
|
|
271
|
+
}
|
|
272
|
+
isRefreshing = true;
|
|
273
|
+
refreshPromise = (async () => {
|
|
274
|
+
try {
|
|
275
|
+
const refreshToken = await tokenStorage.getRefreshToken();
|
|
276
|
+
if (!refreshToken) {
|
|
277
|
+
throw new Error("No refresh token available");
|
|
278
|
+
}
|
|
279
|
+
const response = await fetch(`${config.baseUrl}/auth/refresh`, {
|
|
280
|
+
method: "POST",
|
|
281
|
+
headers: { "Content-Type": "application/json" },
|
|
282
|
+
body: JSON.stringify({ refreshToken })
|
|
283
|
+
});
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
throw new Error("Refresh failed");
|
|
286
|
+
}
|
|
287
|
+
const data = await response.json();
|
|
288
|
+
currentAccessToken = data.token;
|
|
289
|
+
await tokenStorage.updateTokens({
|
|
290
|
+
accessToken: data.token,
|
|
291
|
+
refreshToken: data.refreshToken,
|
|
292
|
+
expiresIn: data.expiresIn,
|
|
293
|
+
refreshExpiresIn: data.expiresIn * 24
|
|
294
|
+
// Approximate
|
|
295
|
+
});
|
|
296
|
+
config.onTokenRefresh?.({
|
|
297
|
+
accessToken: data.token,
|
|
298
|
+
refreshToken: data.refreshToken,
|
|
299
|
+
expiresIn: data.expiresIn,
|
|
300
|
+
refreshExpiresIn: data.expiresIn * 24
|
|
301
|
+
});
|
|
302
|
+
log("Token refreshed successfully");
|
|
303
|
+
} finally {
|
|
304
|
+
isRefreshing = false;
|
|
305
|
+
refreshPromise = null;
|
|
306
|
+
}
|
|
307
|
+
})();
|
|
308
|
+
return refreshPromise;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
// ========================================================================
|
|
312
|
+
// Auth Endpoints
|
|
313
|
+
// ========================================================================
|
|
314
|
+
async login(data) {
|
|
315
|
+
const response = await request("/auth/login", {
|
|
316
|
+
method: "POST",
|
|
317
|
+
body: JSON.stringify(data)
|
|
318
|
+
});
|
|
319
|
+
currentAccessToken = response.token;
|
|
320
|
+
await tokenStorage.setTokens(
|
|
321
|
+
response.token,
|
|
322
|
+
response.refreshToken || null,
|
|
323
|
+
response.expiresIn || null,
|
|
324
|
+
response.user
|
|
325
|
+
);
|
|
326
|
+
config.onLogin?.(response.user);
|
|
327
|
+
log("Login successful:", response.user.userName);
|
|
328
|
+
return response;
|
|
329
|
+
},
|
|
330
|
+
async register(data) {
|
|
331
|
+
const response = await request("/auth/register", {
|
|
332
|
+
method: "POST",
|
|
333
|
+
body: JSON.stringify(data)
|
|
334
|
+
});
|
|
335
|
+
log("Registration successful:", response.user.userName);
|
|
336
|
+
return response;
|
|
337
|
+
},
|
|
338
|
+
async verify(data) {
|
|
339
|
+
const response = await request("/auth/verify", {
|
|
340
|
+
method: "POST",
|
|
341
|
+
body: JSON.stringify(data)
|
|
342
|
+
});
|
|
343
|
+
currentAccessToken = response.token;
|
|
344
|
+
await tokenStorage.setTokens(
|
|
345
|
+
response.token,
|
|
346
|
+
response.refreshToken || null,
|
|
347
|
+
null,
|
|
348
|
+
response.user
|
|
349
|
+
);
|
|
350
|
+
config.onLogin?.(response.user);
|
|
351
|
+
log("Verification successful:", response.user.userName);
|
|
352
|
+
return response;
|
|
353
|
+
},
|
|
354
|
+
async resendVerification(data) {
|
|
355
|
+
const response = await request("/auth/resendVerificationEmail", {
|
|
356
|
+
method: "POST",
|
|
357
|
+
body: JSON.stringify(data)
|
|
358
|
+
});
|
|
359
|
+
log("Verification email resent");
|
|
360
|
+
return response;
|
|
361
|
+
},
|
|
362
|
+
async forgotPassword(data) {
|
|
363
|
+
const response = await request("/auth/forgotPassword", {
|
|
364
|
+
method: "POST",
|
|
365
|
+
body: JSON.stringify(data)
|
|
366
|
+
});
|
|
367
|
+
log("Password reset email sent");
|
|
368
|
+
return response;
|
|
369
|
+
},
|
|
370
|
+
async recoverPassword(data) {
|
|
371
|
+
const response = await request("/auth/recoverPassword", {
|
|
372
|
+
method: "POST",
|
|
373
|
+
body: JSON.stringify(data)
|
|
374
|
+
});
|
|
375
|
+
log("Password recovered successfully");
|
|
376
|
+
return response;
|
|
377
|
+
},
|
|
378
|
+
async refreshToken(data) {
|
|
379
|
+
const response = await request("/auth/refresh", {
|
|
380
|
+
method: "POST",
|
|
381
|
+
body: JSON.stringify(data)
|
|
382
|
+
});
|
|
383
|
+
currentAccessToken = response.token;
|
|
384
|
+
await tokenStorage.updateTokens({
|
|
385
|
+
accessToken: response.token,
|
|
386
|
+
refreshToken: response.refreshToken,
|
|
387
|
+
expiresIn: response.expiresIn,
|
|
388
|
+
refreshExpiresIn: response.expiresIn * 24
|
|
389
|
+
});
|
|
390
|
+
return response;
|
|
391
|
+
},
|
|
392
|
+
// ========================================================================
|
|
393
|
+
// User Endpoints
|
|
394
|
+
// ========================================================================
|
|
395
|
+
async getMe() {
|
|
396
|
+
const response = await request("/users/me");
|
|
397
|
+
await tokenStorage.updateUser(response);
|
|
398
|
+
return response;
|
|
399
|
+
},
|
|
400
|
+
async updateProfile(data) {
|
|
401
|
+
const response = await request("/users/me", {
|
|
402
|
+
method: "PATCH",
|
|
403
|
+
body: JSON.stringify(data)
|
|
404
|
+
});
|
|
405
|
+
await tokenStorage.updateUser(response);
|
|
406
|
+
return response;
|
|
407
|
+
},
|
|
408
|
+
// ========================================================================
|
|
409
|
+
// Utilities
|
|
410
|
+
// ========================================================================
|
|
411
|
+
setAccessToken(token) {
|
|
412
|
+
currentAccessToken = token;
|
|
413
|
+
},
|
|
414
|
+
getAccessToken() {
|
|
415
|
+
return currentAccessToken;
|
|
416
|
+
},
|
|
417
|
+
request
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/providers/AuthProvider.tsx
|
|
422
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
423
|
+
var AuthContext = (0, import_react.createContext)(null);
|
|
424
|
+
var defaultConfig = {
|
|
425
|
+
autoRefresh: true,
|
|
426
|
+
refreshThreshold: 300,
|
|
427
|
+
// 5 minutes before expiry
|
|
428
|
+
debug: false
|
|
429
|
+
};
|
|
430
|
+
function AuthProvider({ config: userConfig, children }) {
|
|
431
|
+
const config = (0, import_react.useMemo)(() => ({ ...defaultConfig, ...userConfig }), [userConfig]);
|
|
432
|
+
const tokenStorage = (0, import_react.useMemo)(
|
|
433
|
+
() => createTokenStorage(config.appId, config.storage),
|
|
434
|
+
[config.appId, config.storage]
|
|
435
|
+
);
|
|
436
|
+
const apiClient = (0, import_react.useMemo)(
|
|
437
|
+
() => createAuthApiClient(config, tokenStorage),
|
|
438
|
+
[config, tokenStorage]
|
|
439
|
+
);
|
|
440
|
+
const [state, setState] = (0, import_react.useState)({
|
|
441
|
+
user: null,
|
|
442
|
+
accessToken: null,
|
|
443
|
+
refreshToken: null,
|
|
444
|
+
status: "idle",
|
|
445
|
+
isLoading: true,
|
|
446
|
+
error: null
|
|
447
|
+
});
|
|
448
|
+
const log = (0, import_react.useCallback)(
|
|
449
|
+
(...args) => {
|
|
450
|
+
if (config.debug) {
|
|
451
|
+
console.log("[PixygonAuth]", ...args);
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
[config.debug]
|
|
455
|
+
);
|
|
456
|
+
(0, import_react.useEffect)(() => {
|
|
457
|
+
let mounted = true;
|
|
458
|
+
async function initializeAuth() {
|
|
459
|
+
log("Initializing auth...");
|
|
460
|
+
try {
|
|
461
|
+
const stored = await tokenStorage.getAll();
|
|
462
|
+
if (!stored.accessToken || !stored.user) {
|
|
463
|
+
log("No stored auth found");
|
|
464
|
+
if (mounted) {
|
|
465
|
+
setState((prev) => ({
|
|
466
|
+
...prev,
|
|
467
|
+
status: "unauthenticated",
|
|
468
|
+
isLoading: false
|
|
469
|
+
}));
|
|
470
|
+
}
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const isExpired = await tokenStorage.isTokenExpired();
|
|
474
|
+
if (isExpired && stored.refreshToken) {
|
|
475
|
+
log("Token expired, attempting refresh...");
|
|
476
|
+
try {
|
|
477
|
+
const response = await apiClient.refreshToken({
|
|
478
|
+
refreshToken: stored.refreshToken
|
|
479
|
+
});
|
|
480
|
+
if (mounted) {
|
|
481
|
+
setState({
|
|
482
|
+
user: stored.user,
|
|
483
|
+
accessToken: response.token,
|
|
484
|
+
refreshToken: response.refreshToken,
|
|
485
|
+
status: "authenticated",
|
|
486
|
+
isLoading: false,
|
|
487
|
+
error: null
|
|
488
|
+
});
|
|
489
|
+
apiClient.setAccessToken(response.token);
|
|
490
|
+
}
|
|
491
|
+
} catch (error) {
|
|
492
|
+
log("Token refresh failed:", error);
|
|
493
|
+
await tokenStorage.clear();
|
|
494
|
+
if (mounted) {
|
|
495
|
+
setState({
|
|
496
|
+
user: null,
|
|
497
|
+
accessToken: null,
|
|
498
|
+
refreshToken: null,
|
|
499
|
+
status: "unauthenticated",
|
|
500
|
+
isLoading: false,
|
|
501
|
+
error: null
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} else if (!isExpired) {
|
|
506
|
+
log("Restoring auth from storage");
|
|
507
|
+
apiClient.setAccessToken(stored.accessToken);
|
|
508
|
+
if (mounted) {
|
|
509
|
+
setState({
|
|
510
|
+
user: stored.user,
|
|
511
|
+
accessToken: stored.accessToken,
|
|
512
|
+
refreshToken: stored.refreshToken,
|
|
513
|
+
status: stored.user.isVerified ? "authenticated" : "verifying",
|
|
514
|
+
isLoading: false,
|
|
515
|
+
error: null
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
log("Token expired and no refresh token available");
|
|
520
|
+
await tokenStorage.clear();
|
|
521
|
+
if (mounted) {
|
|
522
|
+
setState({
|
|
523
|
+
user: null,
|
|
524
|
+
accessToken: null,
|
|
525
|
+
refreshToken: null,
|
|
526
|
+
status: "unauthenticated",
|
|
527
|
+
isLoading: false,
|
|
528
|
+
error: null
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
log("Auth initialization error:", error);
|
|
534
|
+
if (mounted) {
|
|
535
|
+
setState((prev) => ({
|
|
536
|
+
...prev,
|
|
537
|
+
status: "unauthenticated",
|
|
538
|
+
isLoading: false,
|
|
539
|
+
error: {
|
|
540
|
+
code: "UNKNOWN_ERROR",
|
|
541
|
+
message: "Failed to initialize authentication"
|
|
542
|
+
}
|
|
543
|
+
}));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
initializeAuth();
|
|
548
|
+
return () => {
|
|
549
|
+
mounted = false;
|
|
550
|
+
};
|
|
551
|
+
}, [apiClient, tokenStorage, log]);
|
|
552
|
+
(0, import_react.useEffect)(() => {
|
|
553
|
+
if (!config.autoRefresh || state.status !== "authenticated") {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const checkAndRefresh = async () => {
|
|
557
|
+
const willExpire = await tokenStorage.willExpireSoon(config.refreshThreshold || 300);
|
|
558
|
+
if (willExpire && state.refreshToken) {
|
|
559
|
+
log("Token expiring soon, refreshing...");
|
|
560
|
+
try {
|
|
561
|
+
const response = await apiClient.refreshToken({
|
|
562
|
+
refreshToken: state.refreshToken
|
|
563
|
+
});
|
|
564
|
+
setState((prev) => ({
|
|
565
|
+
...prev,
|
|
566
|
+
accessToken: response.token,
|
|
567
|
+
refreshToken: response.refreshToken
|
|
568
|
+
}));
|
|
569
|
+
apiClient.setAccessToken(response.token);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
log("Auto-refresh failed:", error);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
const interval = setInterval(checkAndRefresh, 6e4);
|
|
576
|
+
return () => clearInterval(interval);
|
|
577
|
+
}, [
|
|
578
|
+
config.autoRefresh,
|
|
579
|
+
config.refreshThreshold,
|
|
580
|
+
state.status,
|
|
581
|
+
state.refreshToken,
|
|
582
|
+
apiClient,
|
|
583
|
+
tokenStorage,
|
|
584
|
+
log
|
|
585
|
+
]);
|
|
586
|
+
const login = (0, import_react.useCallback)(
|
|
587
|
+
async (credentials) => {
|
|
588
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
589
|
+
try {
|
|
590
|
+
const response = await apiClient.login(credentials);
|
|
591
|
+
const newStatus = response.user.isVerified ? "authenticated" : "verifying";
|
|
592
|
+
setState({
|
|
593
|
+
user: response.user,
|
|
594
|
+
accessToken: response.token,
|
|
595
|
+
refreshToken: response.refreshToken || null,
|
|
596
|
+
status: newStatus,
|
|
597
|
+
isLoading: false,
|
|
598
|
+
error: null
|
|
599
|
+
});
|
|
600
|
+
return response;
|
|
601
|
+
} catch (error) {
|
|
602
|
+
const authError = error;
|
|
603
|
+
setState((prev) => ({
|
|
604
|
+
...prev,
|
|
605
|
+
isLoading: false,
|
|
606
|
+
error: authError
|
|
607
|
+
}));
|
|
608
|
+
config.onError?.(authError);
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
[apiClient, config]
|
|
613
|
+
);
|
|
614
|
+
const register = (0, import_react.useCallback)(
|
|
615
|
+
async (data) => {
|
|
616
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
617
|
+
try {
|
|
618
|
+
const response = await apiClient.register(data);
|
|
619
|
+
setState((prev) => ({
|
|
620
|
+
...prev,
|
|
621
|
+
isLoading: false
|
|
622
|
+
}));
|
|
623
|
+
return response;
|
|
624
|
+
} catch (error) {
|
|
625
|
+
const authError = error;
|
|
626
|
+
setState((prev) => ({
|
|
627
|
+
...prev,
|
|
628
|
+
isLoading: false,
|
|
629
|
+
error: authError
|
|
630
|
+
}));
|
|
631
|
+
config.onError?.(authError);
|
|
632
|
+
throw error;
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
[apiClient, config]
|
|
636
|
+
);
|
|
637
|
+
const verify = (0, import_react.useCallback)(
|
|
638
|
+
async (data) => {
|
|
639
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
640
|
+
try {
|
|
641
|
+
const response = await apiClient.verify(data);
|
|
642
|
+
setState({
|
|
643
|
+
user: response.user,
|
|
644
|
+
accessToken: response.token,
|
|
645
|
+
refreshToken: response.refreshToken || null,
|
|
646
|
+
status: "authenticated",
|
|
647
|
+
isLoading: false,
|
|
648
|
+
error: null
|
|
649
|
+
});
|
|
650
|
+
return response;
|
|
651
|
+
} catch (error) {
|
|
652
|
+
const authError = error;
|
|
653
|
+
setState((prev) => ({
|
|
654
|
+
...prev,
|
|
655
|
+
isLoading: false,
|
|
656
|
+
error: authError
|
|
657
|
+
}));
|
|
658
|
+
config.onError?.(authError);
|
|
659
|
+
throw error;
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
[apiClient, config]
|
|
663
|
+
);
|
|
664
|
+
const resendVerification = (0, import_react.useCallback)(
|
|
665
|
+
async (data) => {
|
|
666
|
+
return apiClient.resendVerification(data);
|
|
667
|
+
},
|
|
668
|
+
[apiClient]
|
|
669
|
+
);
|
|
670
|
+
const forgotPassword = (0, import_react.useCallback)(
|
|
671
|
+
async (data) => {
|
|
672
|
+
return apiClient.forgotPassword(data);
|
|
673
|
+
},
|
|
674
|
+
[apiClient]
|
|
675
|
+
);
|
|
676
|
+
const recoverPassword = (0, import_react.useCallback)(
|
|
677
|
+
async (data) => {
|
|
678
|
+
return apiClient.recoverPassword(data);
|
|
679
|
+
},
|
|
680
|
+
[apiClient]
|
|
681
|
+
);
|
|
682
|
+
const logout = (0, import_react.useCallback)(async () => {
|
|
683
|
+
log("Logging out...");
|
|
684
|
+
await tokenStorage.clear();
|
|
685
|
+
apiClient.setAccessToken(null);
|
|
686
|
+
setState({
|
|
687
|
+
user: null,
|
|
688
|
+
accessToken: null,
|
|
689
|
+
refreshToken: null,
|
|
690
|
+
status: "unauthenticated",
|
|
691
|
+
isLoading: false,
|
|
692
|
+
error: null
|
|
693
|
+
});
|
|
694
|
+
config.onLogout?.();
|
|
695
|
+
}, [tokenStorage, apiClient, config, log]);
|
|
696
|
+
const refreshTokens = (0, import_react.useCallback)(async () => {
|
|
697
|
+
if (!state.refreshToken) {
|
|
698
|
+
throw new Error("No refresh token available");
|
|
699
|
+
}
|
|
700
|
+
const response = await apiClient.refreshToken({
|
|
701
|
+
refreshToken: state.refreshToken
|
|
702
|
+
});
|
|
703
|
+
setState((prev) => ({
|
|
704
|
+
...prev,
|
|
705
|
+
accessToken: response.token,
|
|
706
|
+
refreshToken: response.refreshToken
|
|
707
|
+
}));
|
|
708
|
+
}, [apiClient, state.refreshToken]);
|
|
709
|
+
const getAccessToken = (0, import_react.useCallback)(() => state.accessToken, [state.accessToken]);
|
|
710
|
+
const hasRole = (0, import_react.useCallback)(
|
|
711
|
+
(role) => {
|
|
712
|
+
if (!state.user) return false;
|
|
713
|
+
const roles = Array.isArray(role) ? role : [role];
|
|
714
|
+
return roles.includes(state.user.role);
|
|
715
|
+
},
|
|
716
|
+
[state.user]
|
|
717
|
+
);
|
|
718
|
+
const contextValue = (0, import_react.useMemo)(
|
|
719
|
+
() => ({
|
|
720
|
+
// State
|
|
721
|
+
...state,
|
|
722
|
+
isAuthenticated: state.status === "authenticated",
|
|
723
|
+
isVerified: state.user?.isVerified ?? false,
|
|
724
|
+
// Actions
|
|
725
|
+
login,
|
|
726
|
+
register,
|
|
727
|
+
logout,
|
|
728
|
+
verify,
|
|
729
|
+
resendVerification,
|
|
730
|
+
forgotPassword,
|
|
731
|
+
recoverPassword,
|
|
732
|
+
refreshTokens,
|
|
733
|
+
// Utilities
|
|
734
|
+
getAccessToken,
|
|
735
|
+
hasRole,
|
|
736
|
+
// Config
|
|
737
|
+
config
|
|
738
|
+
}),
|
|
739
|
+
[
|
|
740
|
+
state,
|
|
741
|
+
login,
|
|
742
|
+
register,
|
|
743
|
+
logout,
|
|
744
|
+
verify,
|
|
745
|
+
resendVerification,
|
|
746
|
+
forgotPassword,
|
|
747
|
+
recoverPassword,
|
|
748
|
+
refreshTokens,
|
|
749
|
+
getAccessToken,
|
|
750
|
+
hasRole,
|
|
751
|
+
config
|
|
752
|
+
]
|
|
753
|
+
);
|
|
754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: contextValue, children });
|
|
755
|
+
}
|
|
756
|
+
function useAuthContext() {
|
|
757
|
+
const context = (0, import_react.useContext)(AuthContext);
|
|
758
|
+
if (!context) {
|
|
759
|
+
throw new Error("useAuthContext must be used within an AuthProvider");
|
|
760
|
+
}
|
|
761
|
+
return context;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/hooks/index.ts
|
|
765
|
+
var import_react3 = require("react");
|
|
766
|
+
|
|
767
|
+
// src/hooks/useProfileSync.ts
|
|
768
|
+
var import_react2 = require("react");
|
|
769
|
+
function useProfileSync() {
|
|
770
|
+
const { user, config, getAccessToken } = useAuthContext();
|
|
771
|
+
const [profile, setProfile] = (0, import_react2.useState)(null);
|
|
772
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
773
|
+
const [isSyncing, setIsSyncing] = (0, import_react2.useState)(false);
|
|
774
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
775
|
+
const fetchProfile = (0, import_react2.useCallback)(async () => {
|
|
776
|
+
const token = getAccessToken();
|
|
777
|
+
if (!token || !user) {
|
|
778
|
+
setProfile(null);
|
|
779
|
+
setIsLoading(false);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
try {
|
|
783
|
+
const response = await fetch(`${config.baseUrl}/users/profile`, {
|
|
784
|
+
headers: {
|
|
785
|
+
"Authorization": `Bearer ${token}`,
|
|
786
|
+
"Content-Type": "application/json",
|
|
787
|
+
"X-App-Id": config.appId
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
if (!response.ok) {
|
|
791
|
+
throw new Error("Failed to fetch profile");
|
|
792
|
+
}
|
|
793
|
+
const data = await response.json();
|
|
794
|
+
setProfile({
|
|
795
|
+
...user,
|
|
796
|
+
...data.user,
|
|
797
|
+
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
798
|
+
});
|
|
799
|
+
setError(null);
|
|
800
|
+
} catch (err) {
|
|
801
|
+
setError(err instanceof Error ? err : new Error("Failed to fetch profile"));
|
|
802
|
+
setProfile(user ? { ...user, lastSyncedAt: void 0 } : null);
|
|
803
|
+
} finally {
|
|
804
|
+
setIsLoading(false);
|
|
805
|
+
}
|
|
806
|
+
}, [config.baseUrl, config.appId, getAccessToken, user]);
|
|
807
|
+
(0, import_react2.useEffect)(() => {
|
|
808
|
+
if (user) {
|
|
809
|
+
fetchProfile();
|
|
810
|
+
} else {
|
|
811
|
+
setProfile(null);
|
|
812
|
+
setIsLoading(false);
|
|
813
|
+
}
|
|
814
|
+
}, [user, fetchProfile]);
|
|
815
|
+
const syncProfile = (0, import_react2.useCallback)(async () => {
|
|
816
|
+
if (!user) return;
|
|
817
|
+
setIsSyncing(true);
|
|
818
|
+
await fetchProfile();
|
|
819
|
+
setIsSyncing(false);
|
|
820
|
+
}, [user, fetchProfile]);
|
|
821
|
+
const updateProfile = (0, import_react2.useCallback)(async (updates) => {
|
|
822
|
+
const token = getAccessToken();
|
|
823
|
+
if (!token || !profile) {
|
|
824
|
+
throw new Error("Not authenticated");
|
|
825
|
+
}
|
|
826
|
+
setIsSyncing(true);
|
|
827
|
+
try {
|
|
828
|
+
const response = await fetch(`${config.baseUrl}/users/profile`, {
|
|
829
|
+
method: "PATCH",
|
|
830
|
+
headers: {
|
|
831
|
+
"Authorization": `Bearer ${token}`,
|
|
832
|
+
"Content-Type": "application/json",
|
|
833
|
+
"X-App-Id": config.appId
|
|
834
|
+
},
|
|
835
|
+
body: JSON.stringify(updates)
|
|
836
|
+
});
|
|
837
|
+
if (!response.ok) {
|
|
838
|
+
const errorData = await response.json().catch(() => ({}));
|
|
839
|
+
throw new Error(errorData.message || "Failed to update profile");
|
|
840
|
+
}
|
|
841
|
+
const data = await response.json();
|
|
842
|
+
setProfile({
|
|
843
|
+
...profile,
|
|
844
|
+
...data.user,
|
|
845
|
+
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
846
|
+
});
|
|
847
|
+
setError(null);
|
|
848
|
+
} catch (err) {
|
|
849
|
+
const error2 = err instanceof Error ? err : new Error("Failed to update profile");
|
|
850
|
+
setError(error2);
|
|
851
|
+
throw error2;
|
|
852
|
+
} finally {
|
|
853
|
+
setIsSyncing(false);
|
|
854
|
+
}
|
|
855
|
+
}, [config.baseUrl, config.appId, getAccessToken, profile]);
|
|
856
|
+
const updatePreferences = (0, import_react2.useCallback)(async (preferences) => {
|
|
857
|
+
await updateProfile({
|
|
858
|
+
preferences: {
|
|
859
|
+
...profile?.preferences,
|
|
860
|
+
...preferences
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}, [updateProfile, profile?.preferences]);
|
|
864
|
+
const linkApp = (0, import_react2.useCallback)(async (appId) => {
|
|
865
|
+
const token = getAccessToken();
|
|
866
|
+
if (!token) {
|
|
867
|
+
throw new Error("Not authenticated");
|
|
868
|
+
}
|
|
869
|
+
setIsSyncing(true);
|
|
870
|
+
try {
|
|
871
|
+
const response = await fetch(`${config.baseUrl}/users/link-app`, {
|
|
872
|
+
method: "POST",
|
|
873
|
+
headers: {
|
|
874
|
+
"Authorization": `Bearer ${token}`,
|
|
875
|
+
"Content-Type": "application/json",
|
|
876
|
+
"X-App-Id": config.appId
|
|
877
|
+
},
|
|
878
|
+
body: JSON.stringify({ appId })
|
|
879
|
+
});
|
|
880
|
+
if (!response.ok) {
|
|
881
|
+
const errorData = await response.json().catch(() => ({}));
|
|
882
|
+
throw new Error(errorData.message || "Failed to link app");
|
|
883
|
+
}
|
|
884
|
+
await fetchProfile();
|
|
885
|
+
setError(null);
|
|
886
|
+
} catch (err) {
|
|
887
|
+
const error2 = err instanceof Error ? err : new Error("Failed to link app");
|
|
888
|
+
setError(error2);
|
|
889
|
+
throw error2;
|
|
890
|
+
} finally {
|
|
891
|
+
setIsSyncing(false);
|
|
892
|
+
}
|
|
893
|
+
}, [config.baseUrl, config.appId, getAccessToken, fetchProfile]);
|
|
894
|
+
return {
|
|
895
|
+
profile,
|
|
896
|
+
isLoading,
|
|
897
|
+
isSyncing,
|
|
898
|
+
error,
|
|
899
|
+
updateProfile,
|
|
900
|
+
syncProfile,
|
|
901
|
+
updatePreferences,
|
|
902
|
+
linkApp
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/hooks/index.ts
|
|
907
|
+
function useAuth() {
|
|
908
|
+
const context = useAuthContext();
|
|
909
|
+
return (0, import_react3.useMemo)(
|
|
910
|
+
() => ({
|
|
911
|
+
// State
|
|
912
|
+
user: context.user,
|
|
913
|
+
status: context.status,
|
|
914
|
+
isLoading: context.isLoading,
|
|
915
|
+
isAuthenticated: context.isAuthenticated,
|
|
916
|
+
isVerified: context.isVerified,
|
|
917
|
+
error: context.error,
|
|
918
|
+
// Actions
|
|
919
|
+
login: context.login,
|
|
920
|
+
register: context.register,
|
|
921
|
+
logout: context.logout,
|
|
922
|
+
verify: context.verify,
|
|
923
|
+
resendVerification: context.resendVerification,
|
|
924
|
+
forgotPassword: context.forgotPassword,
|
|
925
|
+
recoverPassword: context.recoverPassword,
|
|
926
|
+
// Utilities
|
|
927
|
+
hasRole: context.hasRole
|
|
928
|
+
}),
|
|
929
|
+
[context]
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
function useUser() {
|
|
933
|
+
const { user, isAuthenticated, isVerified, hasRole } = useAuthContext();
|
|
934
|
+
return (0, import_react3.useMemo)(() => {
|
|
935
|
+
const dailyTokens = user?.dailyTokens ?? 0;
|
|
936
|
+
const purchasedTokens = user?.purchasedTokens ?? 0;
|
|
937
|
+
const bonusTokens = user?.bonusTokens ?? 0;
|
|
938
|
+
const subscriptionTokens = user?.subscriptionTokens ?? 0;
|
|
939
|
+
return {
|
|
940
|
+
user,
|
|
941
|
+
isAuthenticated,
|
|
942
|
+
isVerified,
|
|
943
|
+
role: user?.role ?? null,
|
|
944
|
+
hasRole,
|
|
945
|
+
// Shortcuts
|
|
946
|
+
userId: user?._id ?? null,
|
|
947
|
+
userName: user?.userName ?? null,
|
|
948
|
+
email: user?.email ?? null,
|
|
949
|
+
profilePicture: user?.profilePicture ?? null,
|
|
950
|
+
subscriptionTier: user?.subscriptionTier ?? null,
|
|
951
|
+
// Tokens
|
|
952
|
+
dailyTokens,
|
|
953
|
+
purchasedTokens,
|
|
954
|
+
bonusTokens,
|
|
955
|
+
subscriptionTokens,
|
|
956
|
+
totalTokens: dailyTokens + purchasedTokens + bonusTokens + subscriptionTokens
|
|
957
|
+
};
|
|
958
|
+
}, [user, isAuthenticated, isVerified, hasRole]);
|
|
959
|
+
}
|
|
960
|
+
function useToken() {
|
|
961
|
+
const { accessToken, refreshToken, getAccessToken, refreshTokens, isAuthenticated } = useAuthContext();
|
|
962
|
+
return (0, import_react3.useMemo)(
|
|
963
|
+
() => ({
|
|
964
|
+
accessToken,
|
|
965
|
+
refreshToken,
|
|
966
|
+
getAccessToken,
|
|
967
|
+
refreshTokens,
|
|
968
|
+
isAuthenticated
|
|
969
|
+
}),
|
|
970
|
+
[accessToken, refreshToken, getAccessToken, refreshTokens, isAuthenticated]
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
function useAuthStatus() {
|
|
974
|
+
const { status, isLoading } = useAuthContext();
|
|
975
|
+
return (0, import_react3.useMemo)(
|
|
976
|
+
() => ({
|
|
977
|
+
status,
|
|
978
|
+
isIdle: status === "idle",
|
|
979
|
+
isLoading,
|
|
980
|
+
isAuthenticated: status === "authenticated",
|
|
981
|
+
isUnauthenticated: status === "unauthenticated",
|
|
982
|
+
isVerifying: status === "verifying"
|
|
983
|
+
}),
|
|
984
|
+
[status, isLoading]
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
function useRequireAuth(options = {}) {
|
|
988
|
+
const { roles, onUnauthorized } = options;
|
|
989
|
+
const { user, isAuthenticated, isLoading, hasRole } = useAuthContext();
|
|
990
|
+
const isAuthorized = (0, import_react3.useMemo)(() => {
|
|
991
|
+
if (!isAuthenticated) return false;
|
|
992
|
+
if (roles && roles.length > 0 && !hasRole(roles)) return false;
|
|
993
|
+
return true;
|
|
994
|
+
}, [isAuthenticated, roles, hasRole]);
|
|
995
|
+
(0, import_react3.useEffect)(() => {
|
|
996
|
+
if (!isLoading && !isAuthorized && onUnauthorized) {
|
|
997
|
+
onUnauthorized();
|
|
998
|
+
}
|
|
999
|
+
}, [isLoading, isAuthorized, onUnauthorized]);
|
|
1000
|
+
return (0, import_react3.useMemo)(
|
|
1001
|
+
() => ({
|
|
1002
|
+
isAuthorized,
|
|
1003
|
+
isLoading,
|
|
1004
|
+
user
|
|
1005
|
+
}),
|
|
1006
|
+
[isAuthorized, isLoading, user]
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
function useAuthError() {
|
|
1010
|
+
const context = useAuthContext();
|
|
1011
|
+
return (0, import_react3.useMemo)(
|
|
1012
|
+
() => ({
|
|
1013
|
+
error: context.error,
|
|
1014
|
+
hasError: !!context.error,
|
|
1015
|
+
errorMessage: context.error?.message ?? null,
|
|
1016
|
+
errorCode: context.error?.code ?? null,
|
|
1017
|
+
clearError: () => {
|
|
1018
|
+
}
|
|
1019
|
+
}),
|
|
1020
|
+
[context.error]
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1024
|
+
0 && (module.exports = {
|
|
1025
|
+
AuthContext,
|
|
1026
|
+
AuthProvider,
|
|
1027
|
+
createAuthApiClient,
|
|
1028
|
+
createTokenStorage,
|
|
1029
|
+
useAuth,
|
|
1030
|
+
useAuthContext,
|
|
1031
|
+
useAuthError,
|
|
1032
|
+
useAuthStatus,
|
|
1033
|
+
useProfileSync,
|
|
1034
|
+
useRequireAuth,
|
|
1035
|
+
useToken,
|
|
1036
|
+
useUser
|
|
1037
|
+
});
|