@payez/next-mvp 3.9.0 → 4.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.
Files changed (149) hide show
  1. package/dist/api/auth-handler.d.ts +1 -2
  2. package/dist/api/auth-handler.js +9 -9
  3. package/dist/api-handlers/account/change-password.js +110 -112
  4. package/dist/api-handlers/admin/analytics.d.ts +19 -20
  5. package/dist/api-handlers/admin/analytics.js +378 -379
  6. package/dist/api-handlers/admin/audit.d.ts +19 -20
  7. package/dist/api-handlers/admin/audit.js +213 -214
  8. package/dist/api-handlers/admin/index.d.ts +21 -22
  9. package/dist/api-handlers/admin/index.js +42 -43
  10. package/dist/api-handlers/admin/redis-sessions.d.ts +35 -36
  11. package/dist/api-handlers/admin/redis-sessions.js +203 -204
  12. package/dist/api-handlers/admin/sessions.d.ts +20 -21
  13. package/dist/api-handlers/admin/sessions.js +283 -284
  14. package/dist/api-handlers/admin/site-logs.d.ts +45 -46
  15. package/dist/api-handlers/admin/site-logs.js +317 -318
  16. package/dist/api-handlers/admin/stats.d.ts +20 -21
  17. package/dist/api-handlers/admin/stats.js +239 -240
  18. package/dist/api-handlers/admin/users.d.ts +19 -20
  19. package/dist/api-handlers/admin/users.js +221 -222
  20. package/dist/api-handlers/admin/vibe-data.d.ts +79 -80
  21. package/dist/api-handlers/admin/vibe-data.js +267 -268
  22. package/dist/api-handlers/auth/refresh.js +633 -635
  23. package/dist/api-handlers/auth/signout.js +186 -187
  24. package/dist/api-handlers/auth/status.js +4 -7
  25. package/dist/api-handlers/auth/update-session.d.ts +1 -1
  26. package/dist/api-handlers/auth/update-session.js +12 -14
  27. package/dist/api-handlers/auth/verify-code.d.ts +43 -43
  28. package/dist/api-handlers/auth/verify-code.js +90 -94
  29. package/dist/api-handlers/session/viability.js +114 -146
  30. package/dist/api-handlers/test/force-expire.js +59 -65
  31. package/dist/auth/auth-decision.js +182 -182
  32. package/dist/auth/better-auth.d.ts +3 -6
  33. package/dist/auth/better-auth.js +3 -6
  34. package/dist/auth/route-config.js +2 -2
  35. package/dist/auth/utils/token-utils.d.ts +83 -84
  36. package/dist/auth/utils/token-utils.js +218 -219
  37. package/dist/client/AuthContext.js +115 -112
  38. package/dist/client/better-auth-client.d.ts +1020 -961
  39. package/dist/client/better-auth-client.js +54 -7
  40. package/dist/client/fetch-with-auth.js +2 -2
  41. package/dist/components/SessionSync.js +121 -119
  42. package/dist/components/account/MobileNavDrawer.js +64 -64
  43. package/dist/components/account/UserAvatarMenu.js +91 -88
  44. package/dist/components/admin/VibeAdminLayout.js +71 -69
  45. package/dist/hooks/useAuth.js +9 -7
  46. package/dist/hooks/useAuthSettings.js +93 -93
  47. package/dist/hooks/useAvailableProviders.d.ts +43 -45
  48. package/dist/hooks/useAvailableProviders.js +112 -108
  49. package/dist/hooks/useSessionExpiration.d.ts +2 -3
  50. package/dist/hooks/useSessionExpiration.js +2 -2
  51. package/dist/hooks/useViabilitySession.js +3 -2
  52. package/dist/index.js +4 -6
  53. package/dist/lib/app-slug.d.ts +95 -95
  54. package/dist/lib/app-slug.js +172 -172
  55. package/dist/lib/standardized-client-api.js +10 -5
  56. package/dist/lib/startup-init.js +21 -25
  57. package/dist/lib/test-aware-get-token.js +86 -81
  58. package/dist/lib/token-lifecycle.d.ts +78 -52
  59. package/dist/lib/token-lifecycle.js +360 -398
  60. package/dist/pages/admin-login/page.js +73 -83
  61. package/dist/pages/client-admin/ClientSiteAdminPage.js +179 -177
  62. package/dist/pages/login/page.js +202 -211
  63. package/dist/pages/showcase/ShowcasePage.js +142 -140
  64. package/dist/pages/test-env/EmergencyLogoutPage.js +99 -98
  65. package/dist/pages/test-env/JwtInspectPage.js +116 -114
  66. package/dist/pages/test-env/RefreshTokenPage.js +4 -2
  67. package/dist/pages/test-env/TestEnvPage.js +51 -49
  68. package/dist/pages/verify-code/page.js +412 -408
  69. package/dist/routes/auth/logout.d.ts +31 -31
  70. package/dist/routes/auth/logout.js +98 -113
  71. package/dist/routes/auth/nextauth.d.ts +14 -11
  72. package/dist/routes/auth/nextauth.js +25 -57
  73. package/dist/routes/auth/session.js +157 -179
  74. package/dist/routes/auth/viability.js +190 -201
  75. package/dist/server/auth.d.ts +50 -0
  76. package/dist/server/auth.js +62 -0
  77. package/dist/stores/authStore.js +19 -23
  78. package/dist/utils/logout.js +5 -5
  79. package/package.json +1 -3
  80. package/src/api/auth-handler.ts +550 -549
  81. package/src/api-handlers/account/change-password.ts +5 -8
  82. package/src/api-handlers/admin/analytics.ts +4 -6
  83. package/src/api-handlers/admin/audit.ts +5 -7
  84. package/src/api-handlers/admin/index.ts +1 -2
  85. package/src/api-handlers/admin/redis-sessions.ts +6 -8
  86. package/src/api-handlers/admin/sessions.ts +5 -7
  87. package/src/api-handlers/admin/site-logs.ts +8 -10
  88. package/src/api-handlers/admin/stats.ts +4 -6
  89. package/src/api-handlers/admin/users.ts +5 -7
  90. package/src/api-handlers/admin/vibe-data.ts +10 -12
  91. package/src/api-handlers/auth/refresh.ts +5 -7
  92. package/src/api-handlers/auth/signout.ts +5 -6
  93. package/src/api-handlers/auth/status.ts +4 -7
  94. package/src/api-handlers/auth/update-session.ts +123 -125
  95. package/src/api-handlers/auth/verify-code.ts +9 -13
  96. package/src/api-handlers/session/viability.ts +10 -47
  97. package/src/api-handlers/test/force-expire.ts +4 -11
  98. package/src/auth/auth-decision.ts +1 -1
  99. package/src/auth/better-auth.ts +138 -141
  100. package/src/auth/route-config.ts +219 -219
  101. package/src/auth/utils/token-utils.ts +0 -1
  102. package/src/client/AuthContext.tsx +6 -2
  103. package/src/client/better-auth-client.ts +54 -7
  104. package/src/client/fetch-with-auth.ts +47 -47
  105. package/src/components/SessionSync.tsx +6 -5
  106. package/src/components/account/MobileNavDrawer.tsx +3 -3
  107. package/src/components/account/UserAvatarMenu.tsx +6 -3
  108. package/src/components/admin/VibeAdminLayout.tsx +4 -2
  109. package/src/config/logger.ts +1 -1
  110. package/src/hooks/useAuth.ts +117 -115
  111. package/src/hooks/useAuthSettings.ts +2 -2
  112. package/src/hooks/useAvailableProviders.ts +9 -5
  113. package/src/hooks/useSessionExpiration.ts +101 -102
  114. package/src/hooks/useViabilitySession.ts +336 -335
  115. package/src/index.ts +60 -63
  116. package/src/lib/api-handler.ts +0 -1
  117. package/src/lib/app-slug.ts +6 -6
  118. package/src/lib/standardized-client-api.ts +901 -895
  119. package/src/lib/startup-init.ts +243 -247
  120. package/src/lib/test-aware-get-token.ts +22 -12
  121. package/src/lib/token-lifecycle.ts +12 -53
  122. package/src/pages/admin-login/page.tsx +9 -17
  123. package/src/pages/client-admin/ClientSiteAdminPage.tsx +4 -2
  124. package/src/pages/login/page.tsx +21 -28
  125. package/src/pages/showcase/ShowcasePage.tsx +4 -2
  126. package/src/pages/test-env/EmergencyLogoutPage.tsx +7 -6
  127. package/src/pages/test-env/JwtInspectPage.tsx +5 -3
  128. package/src/pages/test-env/RefreshTokenPage.tsx +157 -155
  129. package/src/pages/test-env/TestEnvPage.tsx +4 -2
  130. package/src/pages/verify-code/page.tsx +10 -6
  131. package/src/routes/auth/logout.ts +7 -25
  132. package/src/routes/auth/nextauth.ts +45 -71
  133. package/src/routes/auth/session.ts +25 -50
  134. package/src/routes/auth/viability.ts +7 -19
  135. package/src/server/auth.ts +60 -0
  136. package/src/stores/authStore.ts +1899 -1904
  137. package/src/utils/logout.ts +30 -30
  138. package/src/auth/auth-options.ts +0 -237
  139. package/src/auth/callbacks/index.ts +0 -7
  140. package/src/auth/callbacks/jwt.ts +0 -382
  141. package/src/auth/callbacks/session.ts +0 -243
  142. package/src/auth/callbacks/signin.ts +0 -56
  143. package/src/auth/events/index.ts +0 -5
  144. package/src/auth/events/signout.ts +0 -33
  145. package/src/auth/providers/credentials.ts +0 -256
  146. package/src/auth/providers/index.ts +0 -6
  147. package/src/auth/providers/oauth.ts +0 -114
  148. package/src/lib/nextauth-secret.ts +0 -121
  149. package/src/types/next-auth.d.ts +0 -15
@@ -21,10 +21,8 @@
21
21
  */
22
22
 
23
23
  import { NextRequest } from 'next/server';
24
- import { getToken } from 'next-auth/jwt';
25
- import { getSession, SessionData } from './session-store';
26
- import { getJwtCookieName } from './app-slug';
27
- import { getIDPClientConfig } from './idp-client-config';
24
+ import { getSession as getRedisSession, SessionData } from './session-store';
25
+ import { getSession as getBetterAuthSession } from '../server/auth';
28
26
 
29
27
  // 5 minute threshold for "needs refresh" - matches refresh handler pattern
30
28
  const REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
@@ -79,7 +77,7 @@ async function waitForConcurrentRefresh(
79
77
  while (Date.now() - startTime < maxWaitMs) {
80
78
  await delay(CONCURRENT_REFRESH_POLL_INTERVAL_MS);
81
79
 
82
- const sessionData = await getSession(sessionToken);
80
+ const sessionData = await getRedisSession(sessionToken);
83
81
 
84
82
  if (!sessionData) {
85
83
  return { success: false };
@@ -208,7 +206,7 @@ async function triggerRefresh(
208
206
  if (response.status === 401 && retryCount < maxRetries) {
209
207
  await delay(REFRESH_RETRY_DELAY_MS);
210
208
 
211
- const sessionData = await getSession(sessionToken);
209
+ const sessionData = await getRedisSession(sessionToken);
212
210
  if (sessionData && !needsRefresh(sessionData.idpAccessTokenExpires)) {
213
211
  return { success: true };
214
212
  }
@@ -230,7 +228,7 @@ async function triggerRefresh(
230
228
  // On network error, check if maybe another request refreshed the token
231
229
  if (retryCount < maxRetries) {
232
230
  await delay(REFRESH_RETRY_DELAY_MS);
233
- const sessionData = await getSession(sessionToken);
231
+ const sessionData = await getRedisSession(sessionToken);
234
232
  if (sessionData && !needsRefresh(sessionData.idpAccessTokenExpires)) {
235
233
  return { success: true };
236
234
  }
@@ -266,54 +264,15 @@ async function triggerRefresh(
266
264
  * }
267
265
  * ```
268
266
  */
269
- /**
270
- * Get NextAuth secret from IDP config (cached).
271
- * NEVER use process.env.NEXTAUTH_SECRET directly - it may not be set.
272
- */
273
- async function getNextAuthSecret(): Promise<string> {
274
- const config = await getIDPClientConfig();
275
- return config.nextAuthSecret || '';
276
- }
277
-
278
267
  export async function ensureFreshToken(
279
268
  request: NextRequest
280
269
  ): Promise<EnsureFreshTokenResult> {
281
270
  try {
282
- // 1. Get NextAuth JWT to extract sessionToken
283
- // Use IDP config to get the secret (same as viability.ts)
284
- const secret = await getNextAuthSecret();
285
- if (!secret) {
286
- console.error('[TOKEN_LIFECYCLE] NEXTAUTH_SECRET not available from IDP config');
287
- return { success: false, error: 'NO_SESSION', message: 'Auth not configured' };
288
- }
289
- const cookieName = getJwtCookieName();
290
- const token = await getToken({
291
- req: request,
292
- secret,
293
- cookieName,
294
- });
271
+ // 1. Get Better Auth session to extract sessionToken
272
+ const betterAuthSession = await getBetterAuthSession(request);
295
273
 
296
- // Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
297
- const sessionTokenFromJwt = (token?.sessionToken || token?.redisSessionId) as string | undefined;
298
- if (!sessionTokenFromJwt) {
299
- // Debug: log what we got including cookie presence and value info
300
- const cookieHeader = request.headers.get('cookie') || '';
301
- const hasCookie = cookieHeader.includes(cookieName);
302
-
303
- // Extract the actual cookie value to check if it's empty
304
- const cookieMatch = cookieHeader.match(new RegExp(`${cookieName}=([^;]*)`));
305
- const cookieValue = cookieMatch ? cookieMatch[1] : null;
306
- const cookieValueLength = cookieValue?.length || 0;
307
-
308
- console.warn('[TOKEN_LIFECYCLE] NO_SESSION -', {
309
- token: token ? 'exists but no sessionToken/redisSessionId' : 'null',
310
- cookieName,
311
- hasCookie,
312
- cookieValueLength,
313
- cookieValuePreview: cookieValue ? cookieValue.substring(0, 20) + '...' : 'EMPTY',
314
- cookieHeaderLength: cookieHeader.length,
315
- secretLength: secret.length,
316
- });
274
+ if (!betterAuthSession?.session?.token) {
275
+ console.warn('[TOKEN_LIFECYCLE] NO_SESSION - Better Auth session not found');
317
276
  return {
318
277
  success: false,
319
278
  error: 'NO_SESSION',
@@ -321,10 +280,10 @@ export async function ensureFreshToken(
321
280
  };
322
281
  }
323
282
 
324
- const sessionToken = sessionTokenFromJwt;
283
+ const sessionToken = betterAuthSession.session.token;
325
284
 
326
285
  // 2. Get session data from Redis
327
- let sessionData = await getSession(sessionToken);
286
+ let sessionData = await getRedisSession(sessionToken);
328
287
 
329
288
  if (!sessionData) {
330
289
  return {
@@ -405,7 +364,7 @@ export async function ensureFreshToken(
405
364
  }
406
365
 
407
366
  // 5. Re-fetch session data after refresh
408
- sessionData = await getSession(sessionToken);
367
+ sessionData = await getRedisSession(sessionToken);
409
368
  console.log('[TOKEN_LIFECYCLE] After refresh:', {
410
369
  hasAccessToken: !!sessionData?.idpAccessToken,
411
370
  newAccessTokenExpires: sessionData?.idpAccessTokenExpires
@@ -17,7 +17,7 @@
17
17
  'use client';
18
18
 
19
19
  import React, { useState } from 'react';
20
- import { signIn } from 'next-auth/react';
20
+ import { authClient } from '../../client/better-auth-client';
21
21
  import { useSearchParams } from 'next/navigation';
22
22
  import { Suspense } from 'react';
23
23
  import { useBranding, useColors } from '../../theme/useTheme';
@@ -57,26 +57,18 @@ function AdminLoginForm({
57
57
  setError(null);
58
58
 
59
59
  try {
60
- const result = await signIn('credentials', {
60
+ const result = await authClient.signIn.email({
61
61
  email,
62
62
  password,
63
- redirect: false,
64
- callbackUrl,
63
+ callbackURL: callbackUrl,
65
64
  });
66
65
 
67
- if (result?.error) {
68
- // Parse structured error if available
69
- try {
70
- const errorData = JSON.parse(result.error);
71
- setError(errorData.message || errorData.error?.message || 'Invalid credentials');
72
- } catch {
73
- if (result.error === 'CredentialsSignin') {
74
- setError('Invalid email or password');
75
- } else {
76
- setError(result.error);
77
- }
78
- }
79
- } else if (result?.ok) {
66
+ if ((result as any)?.error) {
67
+ const errorMsg = typeof (result as any).error === 'object'
68
+ ? ((result as any).error as any).message || 'Invalid credentials'
69
+ : String((result as any).error);
70
+ setError(errorMsg);
71
+ } else if (result?.data) {
80
72
  // Redirect to verify-code for 2FA or directly to callback
81
73
  window.location.href = `/account-auth/verify-code?callbackUrl=${encodeURIComponent(callbackUrl)}`;
82
74
  }
@@ -45,7 +45,7 @@
45
45
  */
46
46
 
47
47
  import React, { useEffect, useState, useCallback } from 'react';
48
- import { useSession } from 'next-auth/react';
48
+ import { authClient } from '../../client/better-auth-client';
49
49
  import { useRouter } from 'next/navigation';
50
50
  import Link from 'next/link';
51
51
 
@@ -122,7 +122,9 @@ export function ClientSiteAdminPage({
122
122
  backUrl = '/',
123
123
  backLabel = 'Back to Site',
124
124
  }: ClientSiteAdminProps) {
125
- const { data: session, status } = useSession();
125
+ const { data: sessionData, isPending } = authClient.useSession();
126
+ const session = sessionData;
127
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
126
128
  const router = useRouter();
127
129
 
128
130
  // Theme detection
@@ -23,7 +23,7 @@
23
23
  'use client';
24
24
 
25
25
  import React, { useState, useEffect } from 'react';
26
- import { signIn, useSession, getSession } from 'next-auth/react';
26
+ import { authClient } from '../../client/better-auth-client';
27
27
  import { useSearchParams } from 'next/navigation';
28
28
  import { Suspense } from 'react';
29
29
  import ReservedStatusBox from '../../components/reserved/ReservedStatusBox';
@@ -35,7 +35,9 @@ function LoginForm() {
35
35
  const callbackUrl = searchParams?.get('callbackUrl') || '/dashboard';
36
36
  const urlError = searchParams?.get('error');
37
37
 
38
- const { data: session, status } = useSession();
38
+ const { data: sessionData, isPending } = authClient.useSession();
39
+ const session = sessionData;
40
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
39
41
  const branding = useBranding();
40
42
  const colors = useColors();
41
43
  const layout = useLayout();
@@ -128,40 +130,31 @@ function LoginForm() {
128
130
 
129
131
  try {
130
132
  console.log('[LOGIN] Starting authentication...');
131
- const result = await signIn('credentials', {
133
+ const result = await authClient.signIn.email({
132
134
  email,
133
135
  password,
134
- redirect: false,
135
- callbackUrl,
136
+ callbackURL: callbackUrl,
136
137
  });
137
138
 
138
- if (result?.error) {
139
- console.log('[LOGIN] Authentication failed:', result.error);
139
+ if ((result as any)?.error) {
140
+ console.log('[LOGIN] Authentication failed:', (result as any).error);
140
141
 
141
142
  setIsSubmitting(false);
142
143
  setLoading(false);
143
144
  setLoginSuccess(false);
144
145
 
145
- try {
146
- const errorData = JSON.parse(result.error);
147
- const passwordError = errorData.error?.details?.errors?.find((e: any) => e.field_name === 'password');
148
-
149
- if (passwordError) {
150
- // Show recovery options on ANY password error
151
- console.log('[LOGIN] Password error detected - showing recovery options');
152
- setShowRecoveryOptions(true);
153
- setLoginError(passwordError.message);
154
- } else {
155
- setLoginError(result.error);
156
- }
157
- } catch {
158
- if (result.error.includes('Unable to connect')) {
159
- setLoginError('The authentication service is currently unavailable. Please try again later.');
160
- } else if (result.error === 'CredentialsSignin') {
161
- setLoginError('Invalid email or password. Please try again.');
162
- } else {
163
- setLoginError(result.error);
164
- }
146
+ const errorMsg = typeof (result as any).error === 'object'
147
+ ? ((result as any).error as any).message || 'Authentication failed'
148
+ : String((result as any).error);
149
+
150
+ if (errorMsg.includes('password') || errorMsg.includes('Password')) {
151
+ console.log('[LOGIN] Password error detected - showing recovery options');
152
+ setShowRecoveryOptions(true);
153
+ setLoginError(errorMsg);
154
+ } else if (errorMsg.includes('Unable to connect')) {
155
+ setLoginError('The authentication service is currently unavailable. Please try again later.');
156
+ } else {
157
+ setLoginError(errorMsg);
165
158
  }
166
159
  return;
167
160
  }
@@ -173,7 +166,7 @@ function LoginForm() {
173
166
  setIsSubmitting(false);
174
167
 
175
168
  // Get updated session
176
- const freshSession = await getSession();
169
+ const freshSession = await authClient.getSession();
177
170
  console.log('[LOGIN] Fresh session obtained, redirecting to 2FA...');
178
171
 
179
172
  // Redirect to verify-code for 2FA
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useSession } from 'next-auth/react';
3
+ import { authClient } from '../../client/better-auth-client';
4
4
  import { useState, useEffect, useRef, useCallback } from 'react';
5
5
  import Link from 'next/link';
6
6
 
@@ -113,7 +113,9 @@ function DemoButton({ variant, children, onClick }: DemoButtonProps) {
113
113
  * ```
114
114
  */
115
115
  export function ShowcasePage() {
116
- const { data: session, status } = useSession();
116
+ const { data: sessionData, isPending } = authClient.useSession();
117
+ const session = sessionData;
118
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
117
119
  const isDarkMode = useDarkMode();
118
120
  const [toastVisible, setToastVisible] = useState(false);
119
121
  const [toastMessage, setToastMessage] = useState('');
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { signOut, useSession } from 'next-auth/react';
3
+ import { authClient } from '../../client/better-auth-client';
4
4
  import { useState, useEffect } from 'react';
5
5
 
6
6
  /**
@@ -16,7 +16,8 @@ import { useState, useEffect } from 'react';
16
16
  * ```
17
17
  */
18
18
  export function EmergencyLogoutPage() {
19
- const { status } = useSession();
19
+ const { data: sessionData, isPending } = authClient.useSession();
20
+ const status = isPending ? 'loading' : sessionData ? 'authenticated' : 'unauthenticated';
20
21
  const [isDarkMode, setIsDarkMode] = useState(false);
21
22
  const [isLoggingOut, setIsLoggingOut] = useState(false);
22
23
  const [logoutComplete, setLogoutComplete] = useState(false);
@@ -70,10 +71,10 @@ export function EmergencyLogoutPage() {
70
71
  sessionStorage.clear();
71
72
  addLog('sessionStorage cleared');
72
73
 
73
- // Step 4: Call NextAuth signOut
74
- addLog('Calling NextAuth signOut...');
75
- await signOut({ redirect: false });
76
- addLog('NextAuth signOut complete');
74
+ // Step 4: Call Better Auth signOut
75
+ addLog('Calling Better Auth signOut...');
76
+ await authClient.signOut();
77
+ addLog('Better Auth signOut complete');
77
78
 
78
79
  // Step 5: Clear any auth-related fetch cache
79
80
  addLog('Invalidating caches...');
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useSession } from 'next-auth/react';
3
+ import { authClient } from '../../client/better-auth-client';
4
4
  import { useState, useEffect } from 'react';
5
5
 
6
6
  // Decode JWT header (contains kid, alg, typ)
@@ -42,7 +42,9 @@ function decodeJwtPayload(token: string): any {
42
42
  * ```
43
43
  */
44
44
  export function JwtInspectPage() {
45
- const { data: session, status } = useSession();
45
+ const { data: sessionData, isPending } = authClient.useSession();
46
+ const session = sessionData;
47
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
46
48
  const [copied, setCopied] = useState<string | null>(null);
47
49
  const [isDarkMode, setIsDarkMode] = useState(false);
48
50
  const [jwtHeader, setJwtHeader] = useState<any>(null);
@@ -319,7 +321,7 @@ export function JwtInspectPage() {
319
321
  <div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
320
322
  <InfoRow
321
323
  label="Session Expires"
322
- value={session?.expires ? new Date(session.expires).toISOString() : undefined}
324
+ value={(session as any)?.expires ? new Date((session as any).expires).toISOString() : undefined}
323
325
  labelClass={labelClass}
324
326
  valueClass={valueClass}
325
327
  />