@payez/next-mvp 4.1.10 → 4.1.20

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.
Files changed (181) hide show
  1. package/dist/api-handlers/account/profile.d.ts +38 -0
  2. package/dist/api-handlers/account/profile.js +73 -45
  3. package/dist/api-handlers/account/recovery/send-code.d.ts +33 -0
  4. package/dist/api-handlers/account/recovery/send-code.js +52 -18
  5. package/dist/api-handlers/account/recovery/verify-code.d.ts +33 -0
  6. package/dist/api-handlers/account/recovery/verify-code.js +52 -18
  7. package/dist/api-handlers/account/verify-email.js +15 -1
  8. package/dist/api-handlers/account/verify-sms.js +15 -1
  9. package/dist/api-handlers/auth/passkey-signin.d.ts +29 -0
  10. package/dist/api-handlers/auth/passkey-signin.js +97 -0
  11. package/dist/api-handlers/mfa/backup-code-verify.d.ts +73 -0
  12. package/dist/api-handlers/mfa/backup-code-verify.js +153 -0
  13. package/dist/api-handlers/mfa/backup-codes.d.ts +7 -0
  14. package/dist/api-handlers/mfa/backup-codes.js +40 -0
  15. package/dist/api-handlers/mfa/methods.d.ts +3 -0
  16. package/dist/api-handlers/mfa/methods.js +41 -0
  17. package/dist/api-handlers/mfa/totp-confirm.d.ts +8 -0
  18. package/dist/api-handlers/mfa/totp-confirm.js +41 -0
  19. package/dist/api-handlers/mfa/totp-enroll.d.ts +8 -0
  20. package/dist/api-handlers/mfa/totp-enroll.js +41 -0
  21. package/dist/api-handlers/mfa/totp-verify.d.ts +47 -0
  22. package/dist/api-handlers/mfa/totp-verify.js +134 -0
  23. package/dist/api-handlers/passkeys/list.d.ts +7 -0
  24. package/dist/api-handlers/passkeys/list.js +29 -0
  25. package/dist/api-handlers/passkeys/manage.d.ts +3 -0
  26. package/dist/api-handlers/passkeys/manage.js +54 -0
  27. package/dist/api-handlers/passkeys/register-options.d.ts +8 -0
  28. package/dist/api-handlers/passkeys/register-options.js +41 -0
  29. package/dist/api-handlers/passkeys/register-verify.d.ts +7 -0
  30. package/dist/api-handlers/passkeys/register-verify.js +40 -0
  31. package/dist/api-handlers/session/viability.js +19 -0
  32. package/dist/auth/better-auth.d.ts +68 -5
  33. package/dist/auth/better-auth.js +271 -45
  34. package/dist/auth/utils/idp-client.d.ts +13 -22
  35. package/dist/auth/utils/idp-client.js +12 -82
  36. package/dist/client/better-auth-client.d.ts +14 -2
  37. package/dist/lib/ensure-fresh-access-token.js +13 -2
  38. package/dist/lib/session-store.js +28 -21
  39. package/dist/lib/startup-init.js +19 -19
  40. package/dist/lib/token-lifecycle.d.ts +2 -0
  41. package/dist/lib/token-lifecycle.js +61 -7
  42. package/dist/models/SessionModel.d.ts +17 -0
  43. package/dist/pages/login/page.d.ts +10 -1
  44. package/dist/pages/login/page.js +69 -80
  45. package/dist/pages/verify-code/page.d.ts +10 -1
  46. package/dist/pages/verify-code/page.js +72 -4
  47. package/dist/routes/auth/passkey-signin.d.ts +13 -0
  48. package/dist/routes/auth/passkey-signin.js +18 -0
  49. package/dist/routes/auth/viability.d.ts +2 -0
  50. package/dist/routes/auth/viability.js +88 -0
  51. package/dist/routes/mfa/backup-code-verify.d.ts +1 -0
  52. package/dist/routes/mfa/backup-code-verify.js +5 -0
  53. package/dist/routes/mfa/backup-codes.d.ts +10 -0
  54. package/dist/routes/mfa/backup-codes.js +14 -0
  55. package/dist/routes/mfa/methods.d.ts +21 -0
  56. package/dist/routes/mfa/methods.js +10 -0
  57. package/dist/routes/mfa/totp-confirm.d.ts +10 -0
  58. package/dist/routes/mfa/totp-confirm.js +14 -0
  59. package/dist/routes/mfa/totp-enroll.d.ts +10 -0
  60. package/dist/routes/mfa/totp-enroll.js +14 -0
  61. package/dist/routes/mfa/totp-verify.d.ts +14 -0
  62. package/dist/routes/mfa/totp-verify.js +18 -0
  63. package/dist/routes/passkeys/list.d.ts +10 -0
  64. package/dist/routes/passkeys/list.js +14 -0
  65. package/dist/routes/passkeys/manage.d.ts +18 -0
  66. package/dist/routes/passkeys/manage.js +13 -0
  67. package/dist/routes/passkeys/register-options.d.ts +10 -0
  68. package/dist/routes/passkeys/register-options.js +14 -0
  69. package/dist/routes/passkeys/register-verify.d.ts +10 -0
  70. package/dist/routes/passkeys/register-verify.js +14 -0
  71. package/dist/server/auth.d.ts +2 -2
  72. package/dist/server/decode-session.js +37 -3
  73. package/package.json +958 -893
  74. package/scripts/check-internal-url-usage.sh +73 -73
  75. package/src/api/index.ts +17 -17
  76. package/src/api-handlers/account/masked-info.ts +45 -45
  77. package/src/api-handlers/account/profile.ts +99 -80
  78. package/src/api-handlers/account/recovery/initiate.ts +23 -23
  79. package/src/api-handlers/account/recovery/send-code.ts +73 -25
  80. package/src/api-handlers/account/recovery/verify-code.ts +73 -25
  81. package/src/api-handlers/account/reset-password.ts +23 -23
  82. package/src/api-handlers/account/send-code.ts +76 -76
  83. package/src/api-handlers/account/validate-password.ts +118 -118
  84. package/src/api-handlers/account/verify-email.ts +15 -1
  85. package/src/api-handlers/account/verify-sms.ts +15 -1
  86. package/src/api-handlers/admin/stats.ts +249 -249
  87. package/src/api-handlers/anon/preferences.ts +123 -123
  88. package/src/api-handlers/auth/jwks.ts +20 -20
  89. package/src/api-handlers/auth/login.ts +240 -240
  90. package/src/api-handlers/auth/passkey-signin.ts +98 -0
  91. package/src/api-handlers/auth/validate.ts +43 -43
  92. package/src/api-handlers/mfa/backup-code-verify.ts +175 -0
  93. package/src/api-handlers/mfa/backup-codes.ts +41 -0
  94. package/src/api-handlers/mfa/methods.ts +46 -0
  95. package/src/api-handlers/mfa/totp-confirm.ts +42 -0
  96. package/src/api-handlers/mfa/totp-enroll.ts +42 -0
  97. package/src/api-handlers/mfa/totp-verify.ts +156 -0
  98. package/src/api-handlers/passkeys/list.ts +31 -0
  99. package/src/api-handlers/passkeys/manage.ts +54 -0
  100. package/src/api-handlers/passkeys/register-options.ts +42 -0
  101. package/src/api-handlers/passkeys/register-verify.ts +41 -0
  102. package/src/api-handlers/session/viability.ts +161 -142
  103. package/src/auth/better-auth.ts +708 -455
  104. package/src/auth/unauthenticated-routes.ts +2 -2
  105. package/src/auth/utils/idp-client.ts +13 -95
  106. package/src/client/better-auth-client.ts +70 -70
  107. package/src/client/fetchWithSession.ts +21 -21
  108. package/src/client/index.ts +13 -13
  109. package/src/client/useAnonSession.ts +131 -131
  110. package/src/components/admin/VibeAdminContext.tsx +87 -87
  111. package/src/config/logging-config.ts +82 -82
  112. package/src/config/unauthenticated-routes.ts +19 -19
  113. package/src/edge/internal-api-url.ts +65 -65
  114. package/src/edge/middleware.ts +42 -42
  115. package/src/hooks/useProfile.ts +75 -75
  116. package/src/lib/anon-session.ts +213 -213
  117. package/src/lib/auth-secret.ts +137 -137
  118. package/src/lib/demo-mode.ts +17 -17
  119. package/src/lib/ensure-fresh-access-token.ts +13 -2
  120. package/src/lib/idp-client-config.ts +559 -559
  121. package/src/lib/idp-fetch.ts +126 -126
  122. package/src/lib/internal-api.ts +171 -171
  123. package/src/lib/jwt-decode-client.ts +45 -45
  124. package/src/lib/rate-limit-service.ts +9 -9
  125. package/src/lib/redis.ts +26 -26
  126. package/src/lib/refresh-token-validator.ts +64 -64
  127. package/src/lib/secret-validation.ts +8 -8
  128. package/src/lib/session-store.ts +702 -695
  129. package/src/lib/session.ts +34 -34
  130. package/src/lib/startup-init.ts +251 -251
  131. package/src/lib/token-expiry.ts +40 -40
  132. package/src/lib/token-lifecycle.ts +533 -470
  133. package/src/lib/types/api-responses.ts +336 -336
  134. package/src/middleware/auth-decision.ts +43 -43
  135. package/src/models/DecodedAccessToken.ts +16 -16
  136. package/src/models/SessionModel.ts +282 -264
  137. package/src/pages/admin-roles/index.ts +9 -9
  138. package/src/pages/login/page.tsx +123 -228
  139. package/src/pages/security/EnhancedSecurityPage.tsx +9 -9
  140. package/src/pages/verify-code/page.tsx +71 -5
  141. package/src/routes/account/index.ts +31 -31
  142. package/src/routes/account/masked-info.ts +36 -36
  143. package/src/routes/account/send-code.ts +39 -39
  144. package/src/routes/account/verify-email.ts +40 -40
  145. package/src/routes/account/verify-sms.ts +40 -40
  146. package/src/routes/auth/index.ts +22 -22
  147. package/src/routes/auth/passkey-signin.ts +13 -0
  148. package/src/routes/auth/refresh.ts +48 -48
  149. package/src/routes/auth/session.ts +166 -166
  150. package/src/routes/auth/viability.ts +89 -0
  151. package/src/routes/index.ts +17 -17
  152. package/src/routes/mfa/backup-code-verify.ts +1 -0
  153. package/src/routes/mfa/backup-codes.ts +10 -0
  154. package/src/routes/mfa/methods.ts +22 -0
  155. package/src/routes/mfa/totp-confirm.ts +10 -0
  156. package/src/routes/mfa/totp-enroll.ts +10 -0
  157. package/src/routes/mfa/totp-verify.ts +14 -0
  158. package/src/routes/passkeys/list.ts +10 -0
  159. package/src/routes/passkeys/manage.ts +23 -0
  160. package/src/routes/passkeys/register-options.ts +10 -0
  161. package/src/routes/passkeys/register-verify.ts +10 -0
  162. package/src/routes/session/index.ts +7 -7
  163. package/src/routes/session/refresh-viability.ts +17 -17
  164. package/src/server/auth.ts +373 -373
  165. package/src/server/decode-session.ts +235 -202
  166. package/src/services/signalrActivityService.ts +258 -258
  167. package/src/theme/ThemeProvider.tsx +39 -39
  168. package/src/theme/default.ts +33 -33
  169. package/src/theme/index.ts +31 -31
  170. package/src/theme/types.ts +69 -69
  171. package/src/theme/useTheme.ts +57 -57
  172. package/src/theme/utils.ts +40 -40
  173. package/src/types/api.ts +12 -12
  174. package/src/types/auth.d.ts +23 -23
  175. package/src/types/auth.ts +22 -22
  176. package/src/types/logging.ts +11 -11
  177. package/src/types/recovery.ts +54 -54
  178. package/src/utils/api.ts +353 -353
  179. package/src/utils/circuitBreaker.ts +40 -40
  180. package/src/utils/error-message.ts +108 -108
  181. package/src/utils/layout/reservedSpace.ts +124 -124
@@ -1,3 +1,3 @@
1
- // src/auth/unauthenticated-routes.ts
2
- // Re-exporting from route-config for backward compatibility
1
+ // src/auth/unauthenticated-routes.ts
2
+ // Re-exporting from route-config for backward compatibility
3
3
  export * from './route-config';
@@ -150,105 +150,23 @@ function getLoginErrorMessage(status: number, responseData: any): string {
150
150
  // ============================================================================
151
151
 
152
152
  /**
153
- * Register/authenticate OAuth user with IDP.
153
+ * idpOAuthCallback REMOVED 2026-08-05 (NextPert, BAPert 20168 / QAPert 20166).
154
154
  *
155
- * Called after OAuth provider (Google, etc.) redirects back.
156
- * Creates or retrieves IDP user and returns IDP tokens.
155
+ * It POSTed to /api/ExternalAuth/oauth-callback with provider, email, name,
156
+ * tokens and client_id — ALL ASSERTIONS, no id_token, nothing verifiable. After
157
+ * the 170003 fix (13436d56e) that call can only ever return 401.
157
158
  *
158
- * @param oauthData - Data from OAuth provider
159
- * @returns IDP response with tokens and user info
160
- */
161
- export async function idpOAuthCallback(oauthData: {
162
- provider: string;
163
- providerAccountId: string;
164
- email: string;
165
- name?: string;
166
- image?: string;
167
- accessToken?: string;
168
- refreshToken?: string;
169
- expiresAt?: number;
170
- }): Promise<IdpOAuthCallbackResponse> {
171
- const idpUrl = getIdpUrl();
172
- const clientId = getClientId();
173
-
174
- try {
175
- const response = await fetch(`${idpUrl}/api/ExternalAuth/oauth-callback`, {
176
- method: 'POST',
177
- headers: {
178
- 'Content-Type': 'application/json',
179
- 'X-Client-Id': clientId,
180
- },
181
- body: JSON.stringify({
182
- provider: oauthData.provider,
183
- provider_account_id: oauthData.providerAccountId,
184
- email: oauthData.email,
185
- name: oauthData.name || '',
186
- image: oauthData.image || '',
187
- access_token: oauthData.accessToken || '',
188
- refresh_token: oauthData.refreshToken || '',
189
- expires_at: oauthData.expiresAt || 0,
190
- client_id: clientId,
191
- }),
192
- });
193
-
194
- if (!response.ok) {
195
- const errorText = await response.text().catch(() => 'Unknown error');
196
- console.error('[IDP_CLIENT] OAuth callback failed:', response.status, errorText);
197
- return {
198
- success: false,
199
- error: {
200
- code: `HTTP_${response.status}`,
201
- message: 'OAuth registration failed',
202
- },
203
- };
204
- }
205
-
206
- const data = await response.json();
207
- const responseData = data.data || data;
208
-
209
- // Normalize snake_case to camelCase
210
- return {
211
- success: responseData.success !== false,
212
- data: responseData.success !== false
213
- ? {
214
- accessToken: responseData.accessToken || responseData.access_token,
215
- refreshToken: responseData.refreshToken || responseData.refresh_token,
216
- isNewUser: responseData.isNewUser ?? responseData.is_new_user ?? false,
217
- user: responseData.user
218
- ? {
219
- userId: responseData.user.userId || responseData.user.user_id,
220
- email: responseData.user.email || responseData.user.Email,
221
- fullName: responseData.user.fullName || responseData.user.full_name || responseData.user.name,
222
- roles: responseData.user.roles || [],
223
- }
224
- : undefined,
225
- }
226
- : undefined,
227
- error: responseData.error,
228
- };
229
- } catch (error) {
230
- console.error('[IDP_CLIENT] OAuth callback request failed:', error);
231
- return {
232
- success: false,
233
- error: {
234
- code: 'NETWORK_ERROR',
235
- message: 'Failed to connect to authentication service',
236
- },
237
- };
238
- }
239
- }
240
-
241
- // ============================================================================
242
- // TOKEN REFRESH
243
- // ============================================================================
244
-
245
- /**
246
- * Refresh an expired access token using the refresh token.
159
+ * It was a SECOND caller of the same endpoint, and only the first
160
+ * (better-auth.ts, card 138635) was hardened — the same duplicated-integration
161
+ * trap the viability code documents ("adding the relay to just one would leave
162
+ * an app that mounts THIS one still blind").
247
163
  *
248
- * @param refreshToken - The refresh token from previous login
249
- * @param mfaContext - MFA context to preserve across refresh
250
- * @returns New tokens or error
164
+ * It had no call sites and was not reachable through the package exports map
165
+ * (no ./auth/utils key, no wildcard), so removing it breaks nothing. Deleted
166
+ * rather than fixed, because an unreachable function whose only outcome is a
167
+ * 401 that LOOKS like a security success is a trap for whoever finds it next.
251
168
  */
169
+
252
170
  export async function idpRefreshToken(
253
171
  refreshToken: string,
254
172
  mfaContext?: {
@@ -1,70 +1,70 @@
1
- /**
2
- * Better Auth Client.
3
- *
4
- * Import from '@payez/next-mvp/client/better-auth-client'.
5
- *
6
- * Includes useSessionCompat() — returns a { data, status } shape so existing
7
- * components written against the legacy hook don't need destructure changes.
8
- */
9
-
10
- import { createAuthClient } from 'better-auth/react';
11
- import { magicLinkClient } from 'better-auth/client/plugins';
12
- import { useMemo } from 'react';
13
-
14
- export const authClient = createAuthClient({
15
- // baseURL derived from BETTER_AUTH_URL or window.location.origin
16
- plugins: [magicLinkClient()],
17
- });
18
-
19
- // Convenience exports
20
- export const { useSession, signIn, signOut } = authClient;
21
-
22
-
23
- /**
24
- * NextAuth-compatible useSession wrapper.
25
- *
26
- * Maps Better Auth's { data, error, isPending } to NextAuth's { data, status, update }.
27
- * Drop-in replacement — no destructure changes needed in consuming components.
28
- */
29
- export function useSessionCompat() {
30
- const baSession = authClient.useSession();
31
-
32
- const status = useMemo(() => {
33
- if (baSession.isPending) return 'loading' as const;
34
- if (baSession.data) return 'authenticated' as const;
35
- return 'unauthenticated' as const;
36
- }, [baSession.isPending, baSession.data]);
37
-
38
- // Map Better Auth session shape to NextAuth session shape
39
- const data = useMemo(() => {
40
- if (!baSession.data) return null;
41
- return {
42
- ...baSession.data,
43
- user: baSession.data.user,
44
- expires: '', // Better Auth handles expiry differently
45
- };
46
- }, [baSession.data]);
47
-
48
- return {
49
- data,
50
- status,
51
- update: async () => {
52
- // Better Auth doesn't have a direct "refresh session" call.
53
- // Force refetch by invalidating the query.
54
- // TODO: Wire to proper session refresh when available.
55
- return data;
56
- },
57
- };
58
- }
59
-
60
- /**
61
- * NextAuth-compatible signOut wrapper.
62
- *
63
- * Maps NextAuth signOut({ redirect, callbackUrl }) to Better Auth.
64
- */
65
- export async function signOutCompat(options?: { redirect?: boolean; callbackUrl?: string }) {
66
- await authClient.signOut();
67
- if (options?.redirect !== false && options?.callbackUrl) {
68
- window.location.href = options.callbackUrl;
69
- }
70
- }
1
+ /**
2
+ * Better Auth Client.
3
+ *
4
+ * Import from '@payez/next-mvp/client/better-auth-client'.
5
+ *
6
+ * Includes useSessionCompat() — returns a { data, status } shape so existing
7
+ * components written against the legacy hook don't need destructure changes.
8
+ */
9
+
10
+ import { createAuthClient } from 'better-auth/react';
11
+ import { magicLinkClient } from 'better-auth/client/plugins';
12
+ import { useMemo } from 'react';
13
+
14
+ export const authClient = createAuthClient({
15
+ // baseURL derived from BETTER_AUTH_URL or window.location.origin
16
+ plugins: [magicLinkClient()],
17
+ });
18
+
19
+ // Convenience exports
20
+ export const { useSession, signIn, signOut } = authClient;
21
+
22
+
23
+ /**
24
+ * NextAuth-compatible useSession wrapper.
25
+ *
26
+ * Maps Better Auth's { data, error, isPending } to NextAuth's { data, status, update }.
27
+ * Drop-in replacement — no destructure changes needed in consuming components.
28
+ */
29
+ export function useSessionCompat() {
30
+ const baSession = authClient.useSession();
31
+
32
+ const status = useMemo(() => {
33
+ if (baSession.isPending) return 'loading' as const;
34
+ if (baSession.data) return 'authenticated' as const;
35
+ return 'unauthenticated' as const;
36
+ }, [baSession.isPending, baSession.data]);
37
+
38
+ // Map Better Auth session shape to NextAuth session shape
39
+ const data = useMemo(() => {
40
+ if (!baSession.data) return null;
41
+ return {
42
+ ...baSession.data,
43
+ user: baSession.data.user,
44
+ expires: '', // Better Auth handles expiry differently
45
+ };
46
+ }, [baSession.data]);
47
+
48
+ return {
49
+ data,
50
+ status,
51
+ update: async () => {
52
+ // Better Auth doesn't have a direct "refresh session" call.
53
+ // Force refetch by invalidating the query.
54
+ // TODO: Wire to proper session refresh when available.
55
+ return data;
56
+ },
57
+ };
58
+ }
59
+
60
+ /**
61
+ * NextAuth-compatible signOut wrapper.
62
+ *
63
+ * Maps NextAuth signOut({ redirect, callbackUrl }) to Better Auth.
64
+ */
65
+ export async function signOutCompat(options?: { redirect?: boolean; callbackUrl?: string }) {
66
+ await authClient.signOut();
67
+ if (options?.redirect !== false && options?.callbackUrl) {
68
+ window.location.href = options.callbackUrl;
69
+ }
70
+ }
@@ -1,21 +1,21 @@
1
- import { useRef } from 'react';
2
-
3
- export async function fetchWithSession(input: RequestInfo | URL, init: RequestInit = {}, opts: { retry?: number } = {}): Promise<Response> {
4
- const retry = opts.retry ?? 1;
5
- const doFetch = async (): Promise<Response> => {
6
- const res = await fetch(input, { ...init, credentials: 'include' });
7
- if (res.ok) return res;
8
- if (res.status === 401 && retry > 0) {
9
- const rf = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Accept': 'application/json' }, credentials: 'include' });
10
- if (rf.ok) return fetchWithSession(input, init, { retry: retry - 1 });
11
- }
12
- if ((res.status === 409 || res.status === 503) && retry > 0) {
13
- const retryAfter = res.headers.get('Retry-After');
14
- const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 1000;
15
- await new Promise(r => setTimeout(r, Math.min(waitMs, 3000)));
16
- return fetchWithSession(input, init, { retry: retry - 1 });
17
- }
18
- return res;
19
- };
20
- return doFetch();
21
- }
1
+ import { useRef } from 'react';
2
+
3
+ export async function fetchWithSession(input: RequestInfo | URL, init: RequestInit = {}, opts: { retry?: number } = {}): Promise<Response> {
4
+ const retry = opts.retry ?? 1;
5
+ const doFetch = async (): Promise<Response> => {
6
+ const res = await fetch(input, { ...init, credentials: 'include' });
7
+ if (res.ok) return res;
8
+ if (res.status === 401 && retry > 0) {
9
+ const rf = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Accept': 'application/json' }, credentials: 'include' });
10
+ if (rf.ok) return fetchWithSession(input, init, { retry: retry - 1 });
11
+ }
12
+ if ((res.status === 409 || res.status === 503) && retry > 0) {
13
+ const retryAfter = res.headers.get('Retry-After');
14
+ const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 1000;
15
+ await new Promise(r => setTimeout(r, Math.min(waitMs, 3000)));
16
+ return fetchWithSession(input, init, { retry: retry - 1 });
17
+ }
18
+ return res;
19
+ };
20
+ return doFetch();
21
+ }
@@ -1,13 +1,13 @@
1
- /**
2
- * Client-Side Exports
3
- *
4
- * This module exports only client-safe code for use in browser environments.
5
- * Server-only utilities and Node.js dependencies are excluded.
6
- */
7
-
8
- // Client-side fetch utility
9
- export { fetchWithAuth } from './fetch-with-auth';
10
-
11
- // Authentication context and hooks
12
- export { AuthProvider, useAuthConfig, useAuthMode, useFederatedProviders, useFederatedAuthEnabled, useTraditionalAuthEnabled } from './AuthContext';
13
- export type { AuthConfig } from '../types/auth';
1
+ /**
2
+ * Client-Side Exports
3
+ *
4
+ * This module exports only client-safe code for use in browser environments.
5
+ * Server-only utilities and Node.js dependencies are excluded.
6
+ */
7
+
8
+ // Client-side fetch utility
9
+ export { fetchWithAuth } from './fetch-with-auth';
10
+
11
+ // Authentication context and hooks
12
+ export { AuthProvider, useAuthConfig, useAuthMode, useFederatedProviders, useFederatedAuthEnabled, useTraditionalAuthEnabled } from './AuthContext';
13
+ export type { AuthConfig } from '../types/auth';
@@ -1,131 +1,131 @@
1
- /**
2
- * useAnonSession - React hook for anonymous session management
3
- *
4
- * Provides access to anonymous session preferences stored in Redis.
5
- * Works before user logs in, preferences persist across visits.
6
- */
7
-
8
- 'use client';
9
-
10
- import { useState, useEffect, useCallback } from 'react';
11
-
12
- export interface AnonPreferences {
13
- theme?: string;
14
- locale?: string;
15
- [key: string]: any;
16
- }
17
-
18
- export interface AnonMetrics {
19
- resumeGenerationCount?: number;
20
- firstVisit?: number;
21
- lastVisit?: number;
22
- visitCount?: number;
23
- [key: string]: any;
24
- }
25
-
26
- export interface AnonSession {
27
- id: string;
28
- preferences: AnonPreferences;
29
- metrics: AnonMetrics;
30
- }
31
-
32
- export interface UseAnonSessionReturn {
33
- session: AnonSession | null;
34
- isLoading: boolean;
35
- error: string | null;
36
- updatePreferences: (preferences: Partial<AnonPreferences>) => Promise<void>;
37
- setTheme: (theme: string) => Promise<void>;
38
- refresh: () => Promise<void>;
39
- }
40
-
41
- /**
42
- * Hook to manage anonymous session state
43
- */
44
- export function useAnonSession(): UseAnonSessionReturn {
45
- const [session, setSession] = useState<AnonSession | null>(null);
46
- const [isLoading, setIsLoading] = useState(true);
47
- const [error, setError] = useState<string | null>(null);
48
-
49
- // Fetch session on mount
50
- const fetchSession = useCallback(async () => {
51
- try {
52
- setIsLoading(true);
53
- setError(null);
54
-
55
- const response = await fetch('/api/anon/preferences', {
56
- method: 'GET',
57
- credentials: 'include', // Important for cookies
58
- });
59
-
60
- if (!response.ok) {
61
- throw new Error('Failed to fetch preferences');
62
- }
63
-
64
- const data = await response.json();
65
- if (data.success && data.data) {
66
- setSession({
67
- id: data.data.id,
68
- preferences: data.data.preferences || {},
69
- metrics: data.data.metrics || {},
70
- });
71
- }
72
- } catch (err) {
73
- console.error('[useAnonSession] Error fetching session:', err);
74
- setError(err instanceof Error ? err.message : 'Unknown error');
75
- } finally {
76
- setIsLoading(false);
77
- }
78
- }, []);
79
-
80
- useEffect(() => {
81
- fetchSession();
82
- }, [fetchSession]);
83
-
84
- // Update preferences
85
- const updatePreferences = useCallback(async (preferences: Partial<AnonPreferences>) => {
86
- try {
87
- setError(null);
88
-
89
- const response = await fetch('/api/anon/preferences', {
90
- method: 'POST',
91
- credentials: 'include',
92
- headers: {
93
- 'Content-Type': 'application/json',
94
- },
95
- body: JSON.stringify({ preferences }),
96
- });
97
-
98
- if (!response.ok) {
99
- throw new Error('Failed to update preferences');
100
- }
101
-
102
- const data = await response.json();
103
- if (data.success && data.data) {
104
- setSession(prev => prev ? {
105
- ...prev,
106
- preferences: data.data.preferences,
107
- } : null);
108
- }
109
- } catch (err) {
110
- console.error('[useAnonSession] Error updating preferences:', err);
111
- setError(err instanceof Error ? err.message : 'Unknown error');
112
- throw err;
113
- }
114
- }, []);
115
-
116
- // Convenience method to set theme
117
- const setTheme = useCallback(async (theme: string) => {
118
- await updatePreferences({ theme });
119
- }, [updatePreferences]);
120
-
121
- return {
122
- session,
123
- isLoading,
124
- error,
125
- updatePreferences,
126
- setTheme,
127
- refresh: fetchSession,
128
- };
129
- }
130
-
131
- export default useAnonSession;
1
+ /**
2
+ * useAnonSession - React hook for anonymous session management
3
+ *
4
+ * Provides access to anonymous session preferences stored in Redis.
5
+ * Works before user logs in, preferences persist across visits.
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import { useState, useEffect, useCallback } from 'react';
11
+
12
+ export interface AnonPreferences {
13
+ theme?: string;
14
+ locale?: string;
15
+ [key: string]: any;
16
+ }
17
+
18
+ export interface AnonMetrics {
19
+ resumeGenerationCount?: number;
20
+ firstVisit?: number;
21
+ lastVisit?: number;
22
+ visitCount?: number;
23
+ [key: string]: any;
24
+ }
25
+
26
+ export interface AnonSession {
27
+ id: string;
28
+ preferences: AnonPreferences;
29
+ metrics: AnonMetrics;
30
+ }
31
+
32
+ export interface UseAnonSessionReturn {
33
+ session: AnonSession | null;
34
+ isLoading: boolean;
35
+ error: string | null;
36
+ updatePreferences: (preferences: Partial<AnonPreferences>) => Promise<void>;
37
+ setTheme: (theme: string) => Promise<void>;
38
+ refresh: () => Promise<void>;
39
+ }
40
+
41
+ /**
42
+ * Hook to manage anonymous session state
43
+ */
44
+ export function useAnonSession(): UseAnonSessionReturn {
45
+ const [session, setSession] = useState<AnonSession | null>(null);
46
+ const [isLoading, setIsLoading] = useState(true);
47
+ const [error, setError] = useState<string | null>(null);
48
+
49
+ // Fetch session on mount
50
+ const fetchSession = useCallback(async () => {
51
+ try {
52
+ setIsLoading(true);
53
+ setError(null);
54
+
55
+ const response = await fetch('/api/anon/preferences', {
56
+ method: 'GET',
57
+ credentials: 'include', // Important for cookies
58
+ });
59
+
60
+ if (!response.ok) {
61
+ throw new Error('Failed to fetch preferences');
62
+ }
63
+
64
+ const data = await response.json();
65
+ if (data.success && data.data) {
66
+ setSession({
67
+ id: data.data.id,
68
+ preferences: data.data.preferences || {},
69
+ metrics: data.data.metrics || {},
70
+ });
71
+ }
72
+ } catch (err) {
73
+ console.error('[useAnonSession] Error fetching session:', err);
74
+ setError(err instanceof Error ? err.message : 'Unknown error');
75
+ } finally {
76
+ setIsLoading(false);
77
+ }
78
+ }, []);
79
+
80
+ useEffect(() => {
81
+ fetchSession();
82
+ }, [fetchSession]);
83
+
84
+ // Update preferences
85
+ const updatePreferences = useCallback(async (preferences: Partial<AnonPreferences>) => {
86
+ try {
87
+ setError(null);
88
+
89
+ const response = await fetch('/api/anon/preferences', {
90
+ method: 'POST',
91
+ credentials: 'include',
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ },
95
+ body: JSON.stringify({ preferences }),
96
+ });
97
+
98
+ if (!response.ok) {
99
+ throw new Error('Failed to update preferences');
100
+ }
101
+
102
+ const data = await response.json();
103
+ if (data.success && data.data) {
104
+ setSession(prev => prev ? {
105
+ ...prev,
106
+ preferences: data.data.preferences,
107
+ } : null);
108
+ }
109
+ } catch (err) {
110
+ console.error('[useAnonSession] Error updating preferences:', err);
111
+ setError(err instanceof Error ? err.message : 'Unknown error');
112
+ throw err;
113
+ }
114
+ }, []);
115
+
116
+ // Convenience method to set theme
117
+ const setTheme = useCallback(async (theme: string) => {
118
+ await updatePreferences({ theme });
119
+ }, [updatePreferences]);
120
+
121
+ return {
122
+ session,
123
+ isLoading,
124
+ error,
125
+ updatePreferences,
126
+ setTheme,
127
+ refresh: fetchSession,
128
+ };
129
+ }
130
+
131
+ export default useAnonSession;