@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
@@ -1,114 +1,116 @@
1
- "use strict";
2
- 'use client';
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.JwtInspectPage = JwtInspectPage;
5
- const jsx_runtime_1 = require("react/jsx-runtime");
6
- const react_1 = require("next-auth/react");
7
- const react_2 = require("react");
8
- // Decode JWT header (contains kid, alg, typ)
9
- function decodeJwtHeader(token) {
10
- try {
11
- const parts = token.split('.');
12
- if (parts.length !== 3)
13
- return null;
14
- const header = parts[0].replace(/-/g, '+').replace(/_/g, '/');
15
- const decoded = atob(header);
16
- return JSON.parse(decoded);
17
- }
18
- catch {
19
- return null;
20
- }
21
- }
22
- // Decode JWT payload (contains claims)
23
- function decodeJwtPayload(token) {
24
- try {
25
- const parts = token.split('.');
26
- if (parts.length !== 3)
27
- return null;
28
- const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
29
- const decoded = atob(payload);
30
- return JSON.parse(decoded);
31
- }
32
- catch {
33
- return null;
34
- }
35
- }
36
- /**
37
- * Session Inspector Page
38
- *
39
- * Debug page for inspecting session data from Redis.
40
- * Shows user info, roles, 2FA status, and tokens.
41
- *
42
- * Usage in consuming app:
43
- * ```typescript
44
- * // app/test-env/jwt-inspect/page.tsx
45
- * export { JwtInspectPage as default } from '@payez/next-mvp/pages/test-env';
46
- * ```
47
- */
48
- function JwtInspectPage() {
49
- const { data: session, status } = (0, react_1.useSession)();
50
- const [copied, setCopied] = (0, react_2.useState)(null);
51
- const [isDarkMode, setIsDarkMode] = (0, react_2.useState)(false);
52
- const [jwtHeader, setJwtHeader] = (0, react_2.useState)(null);
53
- const [jwtPayload, setJwtPayload] = (0, react_2.useState)(null);
54
- // Detect dark mode
55
- (0, react_2.useEffect)(() => {
56
- const checkDarkMode = () => {
57
- const isDark = document.documentElement.classList.contains('dark') ||
58
- window.matchMedia('(prefers-color-scheme: dark)').matches;
59
- setIsDarkMode(isDark);
60
- };
61
- checkDarkMode();
62
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
63
- mediaQuery.addEventListener('change', checkDarkMode);
64
- return () => mediaQuery.removeEventListener('change', checkDarkMode);
65
- }, []);
66
- // Decode JWT header and payload when accessToken changes
67
- (0, react_2.useEffect)(() => {
68
- const ext = session;
69
- if (ext?.accessToken) {
70
- setJwtHeader(decodeJwtHeader(ext.accessToken));
71
- setJwtPayload(decodeJwtPayload(ext.accessToken));
72
- }
73
- }, [session]);
74
- const copyToClipboard = (text, label) => {
75
- navigator.clipboard.writeText(text);
76
- setCopied(label);
77
- setTimeout(() => setCopied(null), 2000);
78
- };
79
- if (status === 'loading') {
80
- return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: "Loading session..." }));
81
- }
82
- if (status === 'unauthenticated') {
83
- return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-red-900/30 border border-red-800' : 'bg-red-50 border border-red-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-2", children: "Not Authenticated" }), (0, jsx_runtime_1.jsx)("p", { children: "Please log in to inspect session data." })] }) }));
84
- }
85
- // Extended session with all custom fields
86
- const ext = session;
87
- const user = ext?.user || {};
88
- // Card styling helpers
89
- const cardClass = isDarkMode ? 'bg-slate-900 border border-slate-700' : 'bg-white border border-gray-200';
90
- const labelClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
91
- const valueClass = isDarkMode ? 'text-white' : 'text-gray-900';
92
- return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto space-y-6", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold", children: "Session Inspector" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${labelClass}`, children: "Session data from Redis (via NextAuth session callback)" }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "User Identity" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "User ID", value: user.id, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Email", value: user.email, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Name", value: user.name, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "OAuth Provider", value: user.oauthProvider, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "IDP Client ID", value: user.idpClientId, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Merchant ID", value: user.merchantId, labelClass: labelClass, valueClass: valueClass })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-purple-900/30 border border-purple-700' : 'bg-purple-50 border border-purple-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Roles" }), user.roles && user.roles.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-2", children: user.roles.map((role) => ((0, jsx_runtime_1.jsx)("span", { className: `px-3 py-1 rounded-full text-sm font-medium ${isDarkMode ? 'bg-purple-800 text-purple-100' : 'bg-purple-200 text-purple-800'}`, children: role }, role))) })) : ((0, jsx_runtime_1.jsx)("p", { className: labelClass, children: "No roles assigned" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-yellow-900/30 border border-yellow-700' : 'bg-yellow-50 border border-yellow-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "2FA Status" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "2FA Verified:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: user.twoFactorSessionVerified, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Requires 2FA:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: user.requiresTwoFactor, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode, invertColors: true })] }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Auth Methods (AMR)", value: user.authenticationMethods?.join(', '), labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Auth Level (ACR)", value: user.authenticationLevel, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "MFA Completed At", value: user.mfaCompletedAt ? new Date(user.mfaCompletedAt).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "MFA Expires At", value: user.mfaExpiresAt ? new Date(user.mfaExpiresAt).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-blue-900/30 border border-blue-700' : 'bg-blue-50 border border-blue-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Tokens" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Session Token (Redis Key):" }), ext.sessionToken && ((0, jsx_runtime_1.jsx)(CopyButton, { onClick: () => copyToClipboard(ext.sessionToken, 'session'), copied: copied === 'session', isDarkMode: isDarkMode }))] }), (0, jsx_runtime_1.jsx)("code", { className: `block p-2 rounded text-xs break-all ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: ext.sessionToken || 'N/A' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Access Token (IDP):" }), ext.accessToken && ((0, jsx_runtime_1.jsx)(CopyButton, { onClick: () => copyToClipboard(ext.accessToken, 'access'), copied: copied === 'access', isDarkMode: isDarkMode }))] }), (0, jsx_runtime_1.jsx)("code", { className: `block p-2 rounded text-xs break-all max-h-24 overflow-auto ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: ext.accessToken || 'N/A' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Has Refresh Token:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: !!ext.refreshToken, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode })] }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Access Token Expires", value: ext.accessTokenExpires ? new Date(ext.accessTokenExpires).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass })] })] })] }), jwtHeader && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-orange-900/30 border border-orange-700' : 'bg-orange-50 border border-orange-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "JWT Header" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Algorithm (alg)", value: jwtHeader.alg, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Type (typ)", value: jwtHeader.typ, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Key ID (kid):" }), ' ', (0, jsx_runtime_1.jsx)("span", { className: `font-mono ${jwtHeader.kid ? (isDarkMode ? 'text-green-400' : 'text-green-600') : (isDarkMode ? 'text-red-400' : 'text-red-600')}`, children: jwtHeader.kid || 'NOT PRESENT' })] }), Object.entries(jwtHeader)
93
- .filter(([key]) => !['alg', 'typ', 'kid'].includes(key))
94
- .map(([key, value]) => ((0, jsx_runtime_1.jsx)(InfoRow, { label: key, value: typeof value === 'object' ? JSON.stringify(value) : String(value), labelClass: labelClass, valueClass: valueClass }, key)))] })] })), jwtPayload && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-green-900/30 border border-green-700' : 'bg-green-50 border border-green-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "JWT Payload Claims" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Subject (sub)", value: jwtPayload.sub, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Issuer (iss)", value: jwtPayload.iss, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Audience (aud)", value: Array.isArray(jwtPayload.aud) ? jwtPayload.aud.join(', ') : jwtPayload.aud, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Client ID", value: jwtPayload.client_id, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Expires (exp)", value: jwtPayload.exp ? new Date(jwtPayload.exp * 1000).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Issued At (iat)", value: jwtPayload.iat ? new Date(jwtPayload.iat * 1000).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "AMR (Auth Methods)", value: Array.isArray(jwtPayload.amr) ? jwtPayload.amr.join(', ') : jwtPayload.amr, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "ACR (Auth Context)", value: jwtPayload.acr, labelClass: labelClass, valueClass: valueClass })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Session Metadata" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Session Expires", value: session?.expires ? new Date(session.expires).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Error", value: ext.error, labelClass: labelClass, valueClass: ext.error ? 'text-red-500' : valueClass })] })] }), (0, jsx_runtime_1.jsxs)("details", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("summary", { className: "font-semibold cursor-pointer", children: "Raw Session Data (Click to expand)" }), (0, jsx_runtime_1.jsx)("pre", { className: `mt-4 text-xs overflow-auto p-3 rounded ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: JSON.stringify(session, null, 2) })] })] }) }));
95
- }
96
- // Helper Components
97
- function InfoRow({ label, value, labelClass, valueClass, }) {
98
- return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("span", { className: labelClass, children: [label, ":"] }), ' ', (0, jsx_runtime_1.jsx)("span", { className: valueClass, children: value || 'N/A' })] }));
99
- }
100
- function StatusBadge({ value, trueText, falseText, isDarkMode, invertColors = false, }) {
101
- const isPositive = invertColors ? !value : value;
102
- const colorClass = isPositive
103
- ? isDarkMode ? 'bg-green-800 text-green-100' : 'bg-green-200 text-green-800'
104
- : isDarkMode ? 'bg-red-800 text-red-100' : 'bg-red-200 text-red-800';
105
- return ((0, jsx_runtime_1.jsx)("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${colorClass}`, children: value ? trueText : falseText }));
106
- }
107
- function CopyButton({ onClick, copied, isDarkMode, }) {
108
- return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, className: `px-2 py-1 rounded text-xs ${copied
109
- ? 'bg-green-600 text-white'
110
- : isDarkMode
111
- ? 'bg-slate-700 hover:bg-slate-600 text-white'
112
- : 'bg-gray-200 hover:bg-gray-300 text-gray-700'}`, children: copied ? 'Copied!' : 'Copy' }));
113
- }
114
- exports.default = JwtInspectPage;
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.JwtInspectPage = JwtInspectPage;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const better_auth_client_1 = require("../../client/better-auth-client");
7
+ const react_1 = require("react");
8
+ // Decode JWT header (contains kid, alg, typ)
9
+ function decodeJwtHeader(token) {
10
+ try {
11
+ const parts = token.split('.');
12
+ if (parts.length !== 3)
13
+ return null;
14
+ const header = parts[0].replace(/-/g, '+').replace(/_/g, '/');
15
+ const decoded = atob(header);
16
+ return JSON.parse(decoded);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ // Decode JWT payload (contains claims)
23
+ function decodeJwtPayload(token) {
24
+ try {
25
+ const parts = token.split('.');
26
+ if (parts.length !== 3)
27
+ return null;
28
+ const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
29
+ const decoded = atob(payload);
30
+ return JSON.parse(decoded);
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /**
37
+ * Session Inspector Page
38
+ *
39
+ * Debug page for inspecting session data from Redis.
40
+ * Shows user info, roles, 2FA status, and tokens.
41
+ *
42
+ * Usage in consuming app:
43
+ * ```typescript
44
+ * // app/test-env/jwt-inspect/page.tsx
45
+ * export { JwtInspectPage as default } from '@payez/next-mvp/pages/test-env';
46
+ * ```
47
+ */
48
+ function JwtInspectPage() {
49
+ const { data: sessionData, isPending } = better_auth_client_1.authClient.useSession();
50
+ const session = sessionData;
51
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
52
+ const [copied, setCopied] = (0, react_1.useState)(null);
53
+ const [isDarkMode, setIsDarkMode] = (0, react_1.useState)(false);
54
+ const [jwtHeader, setJwtHeader] = (0, react_1.useState)(null);
55
+ const [jwtPayload, setJwtPayload] = (0, react_1.useState)(null);
56
+ // Detect dark mode
57
+ (0, react_1.useEffect)(() => {
58
+ const checkDarkMode = () => {
59
+ const isDark = document.documentElement.classList.contains('dark') ||
60
+ window.matchMedia('(prefers-color-scheme: dark)').matches;
61
+ setIsDarkMode(isDark);
62
+ };
63
+ checkDarkMode();
64
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
65
+ mediaQuery.addEventListener('change', checkDarkMode);
66
+ return () => mediaQuery.removeEventListener('change', checkDarkMode);
67
+ }, []);
68
+ // Decode JWT header and payload when accessToken changes
69
+ (0, react_1.useEffect)(() => {
70
+ const ext = session;
71
+ if (ext?.accessToken) {
72
+ setJwtHeader(decodeJwtHeader(ext.accessToken));
73
+ setJwtPayload(decodeJwtPayload(ext.accessToken));
74
+ }
75
+ }, [session]);
76
+ const copyToClipboard = (text, label) => {
77
+ navigator.clipboard.writeText(text);
78
+ setCopied(label);
79
+ setTimeout(() => setCopied(null), 2000);
80
+ };
81
+ if (status === 'loading') {
82
+ return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: "Loading session..." }));
83
+ }
84
+ if (status === 'unauthenticated') {
85
+ return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-red-900/30 border border-red-800' : 'bg-red-50 border border-red-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-2", children: "Not Authenticated" }), (0, jsx_runtime_1.jsx)("p", { children: "Please log in to inspect session data." })] }) }));
86
+ }
87
+ // Extended session with all custom fields
88
+ const ext = session;
89
+ const user = ext?.user || {};
90
+ // Card styling helpers
91
+ const cardClass = isDarkMode ? 'bg-slate-900 border border-slate-700' : 'bg-white border border-gray-200';
92
+ const labelClass = isDarkMode ? 'text-slate-400' : 'text-gray-500';
93
+ const valueClass = isDarkMode ? 'text-white' : 'text-gray-900';
94
+ return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto space-y-6", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold", children: "Session Inspector" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${labelClass}`, children: "Session data from Redis (via NextAuth session callback)" }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "User Identity" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "User ID", value: user.id, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Email", value: user.email, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Name", value: user.name, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "OAuth Provider", value: user.oauthProvider, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "IDP Client ID", value: user.idpClientId, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Merchant ID", value: user.merchantId, labelClass: labelClass, valueClass: valueClass })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-purple-900/30 border border-purple-700' : 'bg-purple-50 border border-purple-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Roles" }), user.roles && user.roles.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-2", children: user.roles.map((role) => ((0, jsx_runtime_1.jsx)("span", { className: `px-3 py-1 rounded-full text-sm font-medium ${isDarkMode ? 'bg-purple-800 text-purple-100' : 'bg-purple-200 text-purple-800'}`, children: role }, role))) })) : ((0, jsx_runtime_1.jsx)("p", { className: labelClass, children: "No roles assigned" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-yellow-900/30 border border-yellow-700' : 'bg-yellow-50 border border-yellow-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "2FA Status" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "2FA Verified:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: user.twoFactorSessionVerified, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Requires 2FA:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: user.requiresTwoFactor, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode, invertColors: true })] }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Auth Methods (AMR)", value: user.authenticationMethods?.join(', '), labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Auth Level (ACR)", value: user.authenticationLevel, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "MFA Completed At", value: user.mfaCompletedAt ? new Date(user.mfaCompletedAt).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "MFA Expires At", value: user.mfaExpiresAt ? new Date(user.mfaExpiresAt).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-blue-900/30 border border-blue-700' : 'bg-blue-50 border border-blue-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Tokens" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Session Token (Redis Key):" }), ext.sessionToken && ((0, jsx_runtime_1.jsx)(CopyButton, { onClick: () => copyToClipboard(ext.sessionToken, 'session'), copied: copied === 'session', isDarkMode: isDarkMode }))] }), (0, jsx_runtime_1.jsx)("code", { className: `block p-2 rounded text-xs break-all ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: ext.sessionToken || 'N/A' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Access Token (IDP):" }), ext.accessToken && ((0, jsx_runtime_1.jsx)(CopyButton, { onClick: () => copyToClipboard(ext.accessToken, 'access'), copied: copied === 'access', isDarkMode: isDarkMode }))] }), (0, jsx_runtime_1.jsx)("code", { className: `block p-2 rounded text-xs break-all max-h-24 overflow-auto ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: ext.accessToken || 'N/A' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Has Refresh Token:" }), ' ', (0, jsx_runtime_1.jsx)(StatusBadge, { value: !!ext.refreshToken, trueText: "Yes", falseText: "No", isDarkMode: isDarkMode })] }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Access Token Expires", value: ext.accessTokenExpires ? new Date(ext.accessTokenExpires).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass })] })] })] }), jwtHeader && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-orange-900/30 border border-orange-700' : 'bg-orange-50 border border-orange-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "JWT Header" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Algorithm (alg)", value: jwtHeader.alg, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Type (typ)", value: jwtHeader.typ, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: labelClass, children: "Key ID (kid):" }), ' ', (0, jsx_runtime_1.jsx)("span", { className: `font-mono ${jwtHeader.kid ? (isDarkMode ? 'text-green-400' : 'text-green-600') : (isDarkMode ? 'text-red-400' : 'text-red-600')}`, children: jwtHeader.kid || 'NOT PRESENT' })] }), Object.entries(jwtHeader)
95
+ .filter(([key]) => !['alg', 'typ', 'kid'].includes(key))
96
+ .map(([key, value]) => ((0, jsx_runtime_1.jsx)(InfoRow, { label: key, value: typeof value === 'object' ? JSON.stringify(value) : String(value), labelClass: labelClass, valueClass: valueClass }, key)))] })] })), jwtPayload && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-green-900/30 border border-green-700' : 'bg-green-50 border border-green-200'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "JWT Payload Claims" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Subject (sub)", value: jwtPayload.sub, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Issuer (iss)", value: jwtPayload.iss, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Audience (aud)", value: Array.isArray(jwtPayload.aud) ? jwtPayload.aud.join(', ') : jwtPayload.aud, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Client ID", value: jwtPayload.client_id, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Expires (exp)", value: jwtPayload.exp ? new Date(jwtPayload.exp * 1000).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Issued At (iat)", value: jwtPayload.iat ? new Date(jwtPayload.iat * 1000).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "AMR (Auth Methods)", value: Array.isArray(jwtPayload.amr) ? jwtPayload.amr.join(', ') : jwtPayload.amr, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "ACR (Auth Context)", value: jwtPayload.acr, labelClass: labelClass, valueClass: valueClass })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Session Metadata" }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm", children: [(0, jsx_runtime_1.jsx)(InfoRow, { label: "Session Expires", value: session?.expires ? new Date(session.expires).toISOString() : undefined, labelClass: labelClass, valueClass: valueClass }), (0, jsx_runtime_1.jsx)(InfoRow, { label: "Error", value: ext.error, labelClass: labelClass, valueClass: ext.error ? 'text-red-500' : valueClass })] })] }), (0, jsx_runtime_1.jsxs)("details", { className: `p-4 rounded-lg ${cardClass}`, children: [(0, jsx_runtime_1.jsx)("summary", { className: "font-semibold cursor-pointer", children: "Raw Session Data (Click to expand)" }), (0, jsx_runtime_1.jsx)("pre", { className: `mt-4 text-xs overflow-auto p-3 rounded ${isDarkMode ? 'bg-slate-800' : 'bg-gray-100'}`, children: JSON.stringify(session, null, 2) })] })] }) }));
97
+ }
98
+ // Helper Components
99
+ function InfoRow({ label, value, labelClass, valueClass, }) {
100
+ return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("span", { className: labelClass, children: [label, ":"] }), ' ', (0, jsx_runtime_1.jsx)("span", { className: valueClass, children: value || 'N/A' })] }));
101
+ }
102
+ function StatusBadge({ value, trueText, falseText, isDarkMode, invertColors = false, }) {
103
+ const isPositive = invertColors ? !value : value;
104
+ const colorClass = isPositive
105
+ ? isDarkMode ? 'bg-green-800 text-green-100' : 'bg-green-200 text-green-800'
106
+ : isDarkMode ? 'bg-red-800 text-red-100' : 'bg-red-200 text-red-800';
107
+ return ((0, jsx_runtime_1.jsx)("span", { className: `px-2 py-0.5 rounded text-xs font-medium ${colorClass}`, children: value ? trueText : falseText }));
108
+ }
109
+ function CopyButton({ onClick, copied, isDarkMode, }) {
110
+ return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, className: `px-2 py-1 rounded text-xs ${copied
111
+ ? 'bg-green-600 text-white'
112
+ : isDarkMode
113
+ ? 'bg-slate-700 hover:bg-slate-600 text-white'
114
+ : 'bg-gray-200 hover:bg-gray-300 text-gray-700'}`, children: copied ? 'Copied!' : 'Copy' }));
115
+ }
116
+ exports.default = JwtInspectPage;
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.RefreshTokenPage = RefreshTokenPage;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const react_1 = require("react");
7
- const react_2 = require("next-auth/react");
7
+ const better_auth_client_1 = require("../../client/better-auth-client");
8
8
  /**
9
9
  * Refresh Token Test Page
10
10
  *
@@ -19,7 +19,9 @@ const react_2 = require("next-auth/react");
19
19
  * ```
20
20
  */
21
21
  function RefreshTokenPage() {
22
- const { data: session, update } = (0, react_2.useSession)();
22
+ const { data: session } = better_auth_client_1.authClient.useSession();
23
+ // TODO: Better Auth session refresh
24
+ const update = async () => { };
23
25
  const [result, setResult] = (0, react_1.useState)(null);
24
26
  const [loading, setLoading] = (0, react_1.useState)(false);
25
27
  const [sessionDetails, setSessionDetails] = (0, react_1.useState)(null);
@@ -1,49 +1,51 @@
1
- "use strict";
2
- 'use client';
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TestEnvPage = TestEnvPage;
8
- const jsx_runtime_1 = require("react/jsx-runtime");
9
- const react_1 = require("next-auth/react");
10
- const react_2 = require("react");
11
- const link_1 = __importDefault(require("next/link"));
12
- /**
13
- * Test Environment Index Page
14
- *
15
- * Debug tools index showing session status and links to debug pages.
16
- *
17
- * Usage in consuming app:
18
- * ```typescript
19
- * // app/test-env/page.tsx
20
- * export { TestEnvPage as default } from '@payez/next-mvp/pages/test-env';
21
- * ```
22
- */
23
- function TestEnvPage() {
24
- const { data: session, status } = (0, react_1.useSession)();
25
- const [isDarkMode, setIsDarkMode] = (0, react_2.useState)(false);
26
- (0, react_2.useEffect)(() => {
27
- const checkDarkMode = () => {
28
- const isDark = document.documentElement.classList.contains('dark') ||
29
- window.matchMedia('(prefers-color-scheme: dark)').matches;
30
- setIsDarkMode(isDark);
31
- };
32
- checkDarkMode();
33
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
34
- mediaQuery.addEventListener('change', checkDarkMode);
35
- return () => mediaQuery.removeEventListener('change', checkDarkMode);
36
- }, []);
37
- const testPages = [
38
- {
39
- name: 'JWT Inspector',
40
- url: '/test-env/jwt-inspect',
41
- description: 'Decode and inspect JWT tokens, view all claims',
42
- },
43
- ];
44
- const extSession = session;
45
- return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold mb-6", children: "Test Environment" }), (0, jsx_runtime_1.jsxs)("div", { className: `mb-8 p-4 rounded-lg ${isDarkMode ? 'bg-slate-900' : 'bg-white border'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-3", children: "Current Session" }), status === 'loading' ? ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500", children: "Loading..." })) : status === 'unauthenticated' ? ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: "Not logged in" })) : ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Email:" }) }), (0, jsx_runtime_1.jsx)("div", { children: session?.user?.email || 'N/A' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Has Access Token:" }) }), (0, jsx_runtime_1.jsx)("div", { className: extSession?.accessToken ? 'text-green-500' : 'text-red-500', children: extSession?.accessToken ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Has Refresh Token:" }) }), (0, jsx_runtime_1.jsx)("div", { className: extSession?.refreshToken ? 'text-green-500' : 'text-red-500', children: extSession?.refreshToken ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Token Expires:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.accessTokenExpires ? new Date(extSession.accessTokenExpires).toLocaleString() : 'N/A' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "2FA Required:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.user?.requiresTwoFactor ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "2FA Verified:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.user?.twoFactorSessionVerified ? 'Yes' : 'No' })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Debug Tools" }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: testPages.map((page) => ((0, jsx_runtime_1.jsxs)(link_1.default, { href: page.url, className: `p-4 rounded-lg transition-colors ${isDarkMode
46
- ? 'bg-slate-900 hover:bg-slate-800 border border-slate-700'
47
- : 'bg-white hover:bg-gray-50 border'}`, children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-semibold mb-2", children: page.name }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`, children: page.description })] }, page.url))) })] }), session && !extSession.refreshToken && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-amber-900/30 border border-amber-700' : 'bg-amber-50 border border-amber-200'}`, children: [(0, jsx_runtime_1.jsx)("h3", { className: `font-semibold mb-2 ${isDarkMode ? 'text-amber-400' : 'text-amber-600'}`, children: "No Refresh Token" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${isDarkMode ? 'text-gray-300' : 'text-gray-600'}`, children: "This session does not have a refresh token. This typically means 2FA has not been completed. Token refresh will fail when the access token expires." })] }))] }) }));
48
- }
49
- exports.default = TestEnvPage;
1
+ "use strict";
2
+ 'use client';
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TestEnvPage = TestEnvPage;
8
+ const jsx_runtime_1 = require("react/jsx-runtime");
9
+ const better_auth_client_1 = require("../../client/better-auth-client");
10
+ const react_1 = require("react");
11
+ const link_1 = __importDefault(require("next/link"));
12
+ /**
13
+ * Test Environment Index Page
14
+ *
15
+ * Debug tools index showing session status and links to debug pages.
16
+ *
17
+ * Usage in consuming app:
18
+ * ```typescript
19
+ * // app/test-env/page.tsx
20
+ * export { TestEnvPage as default } from '@payez/next-mvp/pages/test-env';
21
+ * ```
22
+ */
23
+ function TestEnvPage() {
24
+ const { data: sessionData, isPending } = better_auth_client_1.authClient.useSession();
25
+ const session = sessionData;
26
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
27
+ const [isDarkMode, setIsDarkMode] = (0, react_1.useState)(false);
28
+ (0, react_1.useEffect)(() => {
29
+ const checkDarkMode = () => {
30
+ const isDark = document.documentElement.classList.contains('dark') ||
31
+ window.matchMedia('(prefers-color-scheme: dark)').matches;
32
+ setIsDarkMode(isDark);
33
+ };
34
+ checkDarkMode();
35
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
36
+ mediaQuery.addEventListener('change', checkDarkMode);
37
+ return () => mediaQuery.removeEventListener('change', checkDarkMode);
38
+ }, []);
39
+ const testPages = [
40
+ {
41
+ name: 'JWT Inspector',
42
+ url: '/test-env/jwt-inspect',
43
+ description: 'Decode and inspect JWT tokens, view all claims',
44
+ },
45
+ ];
46
+ const extSession = session;
47
+ return ((0, jsx_runtime_1.jsx)("div", { className: `min-h-screen p-8 ${isDarkMode ? 'bg-slate-950 text-white' : 'bg-gray-50 text-gray-900'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-4xl mx-auto", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold mb-6", children: "Test Environment" }), (0, jsx_runtime_1.jsxs)("div", { className: `mb-8 p-4 rounded-lg ${isDarkMode ? 'bg-slate-900' : 'bg-white border'}`, children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-3", children: "Current Session" }), status === 'loading' ? ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500", children: "Loading..." })) : status === 'unauthenticated' ? ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: "Not logged in" })) : ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Email:" }) }), (0, jsx_runtime_1.jsx)("div", { children: session?.user?.email || 'N/A' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Has Access Token:" }) }), (0, jsx_runtime_1.jsx)("div", { className: extSession?.accessToken ? 'text-green-500' : 'text-red-500', children: extSession?.accessToken ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Has Refresh Token:" }) }), (0, jsx_runtime_1.jsx)("div", { className: extSession?.refreshToken ? 'text-green-500' : 'text-red-500', children: extSession?.refreshToken ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "Token Expires:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.accessTokenExpires ? new Date(extSession.accessTokenExpires).toLocaleString() : 'N/A' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "2FA Required:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.user?.requiresTwoFactor ? 'Yes' : 'No' }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("strong", { children: "2FA Verified:" }) }), (0, jsx_runtime_1.jsx)("div", { children: extSession?.user?.twoFactorSessionVerified ? 'Yes' : 'No' })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-8", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-lg font-semibold mb-4", children: "Debug Tools" }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: testPages.map((page) => ((0, jsx_runtime_1.jsxs)(link_1.default, { href: page.url, className: `p-4 rounded-lg transition-colors ${isDarkMode
48
+ ? 'bg-slate-900 hover:bg-slate-800 border border-slate-700'
49
+ : 'bg-white hover:bg-gray-50 border'}`, children: [(0, jsx_runtime_1.jsx)("h3", { className: "font-semibold mb-2", children: page.name }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${isDarkMode ? 'text-gray-400' : 'text-gray-600'}`, children: page.description })] }, page.url))) })] }), session && !extSession.refreshToken && ((0, jsx_runtime_1.jsxs)("div", { className: `p-4 rounded-lg ${isDarkMode ? 'bg-amber-900/30 border border-amber-700' : 'bg-amber-50 border border-amber-200'}`, children: [(0, jsx_runtime_1.jsx)("h3", { className: `font-semibold mb-2 ${isDarkMode ? 'text-amber-400' : 'text-amber-600'}`, children: "No Refresh Token" }), (0, jsx_runtime_1.jsx)("p", { className: `text-sm ${isDarkMode ? 'text-gray-300' : 'text-gray-600'}`, children: "This session does not have a refresh token. This typically means 2FA has not been completed. Token refresh will fail when the access token expires." })] }))] }) }));
50
+ }
51
+ exports.default = TestEnvPage;