@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,155 +1,157 @@
1
- "use client";
2
- import React, { useState, useEffect } from "react";
3
- import { useSession } from "next-auth/react";
4
-
5
- /**
6
- * Refresh Token Test Page
7
- *
8
- * Debug page for testing OAuth refresh token flow.
9
- * Shows current session state, allows manual refresh trigger,
10
- * and can force-expire tokens for testing.
11
- *
12
- * Usage:
13
- * ```typescript
14
- * // app/test-env/refresh-token/page.tsx
15
- * export { RefreshTokenPage as default } from '@payez/next-mvp/pages/test-env';
16
- * ```
17
- */
18
- export function RefreshTokenPage() {
19
- const { data: session, update } = useSession();
20
- const [result, setResult] = useState<any>(null);
21
- const [loading, setLoading] = useState(false);
22
- const [sessionDetails, setSessionDetails] = useState<any>(null);
23
-
24
- // Fetch detailed session info on mount
25
- useEffect(() => {
26
- async function fetchSessionDetails() {
27
- try {
28
- const res = await fetch("/api/auth/session", { credentials: "include" });
29
- const data = await res.json();
30
- setSessionDetails(data);
31
- } catch (e) {
32
- console.error("Failed to fetch session details", e);
33
- }
34
- }
35
- fetchSessionDetails();
36
- }, [result]); // Refetch after refresh
37
-
38
- async function handleRefresh() {
39
- setLoading(true);
40
- setResult(null);
41
- try {
42
- const res = await fetch("/api/auth/refresh", {
43
- method: "POST",
44
- headers: { "Content-Type": "application/json" },
45
- credentials: "include",
46
- });
47
- const data = await res.json();
48
- setResult({ status: res.status, ...data });
49
- // Update NextAuth session
50
- await update();
51
- } catch (e: any) {
52
- setResult({ error: e.message });
53
- } finally {
54
- setLoading(false);
55
- }
56
- }
57
-
58
- async function handleForceExpire() {
59
- setLoading(true);
60
- setResult(null);
61
- try {
62
- const res = await fetch("/api/test/force-expire", {
63
- method: "POST",
64
- credentials: "include",
65
- });
66
- const data = await res.json();
67
- setResult({ action: "force_expire", status: res.status, ...data });
68
- } catch (e: any) {
69
- setResult({ error: e.message });
70
- } finally {
71
- setLoading(false);
72
- }
73
- }
74
-
75
- const formatExpiry = (exp: number | string | undefined) => {
76
- if (!exp) return "N/A";
77
- const date = new Date(typeof exp === "string" ? exp : exp);
78
- const now = new Date();
79
- const diffMs = date.getTime() - now.getTime();
80
- const diffMins = Math.floor(diffMs / 60000);
81
- const diffSecs = Math.floor((diffMs % 60000) / 1000);
82
- return `${date.toLocaleTimeString()} (${diffMins}m ${diffSecs}s remaining)`;
83
- };
84
-
85
- return (
86
- <div className="p-8 max-w-2xl mx-auto bg-gray-900 min-h-screen text-white">
87
- <h1 className="text-2xl font-bold mb-4">Refresh Token Test</h1>
88
-
89
- {/* Session Info */}
90
- <div className="mb-6 rounded border border-blue-500 bg-blue-900/30 p-4">
91
- <h2 className="font-semibold mb-2 text-blue-300">Current Session</h2>
92
- <div className="text-sm space-y-1 font-mono">
93
- <div><span className="text-gray-400">User:</span> {session?.user?.email || "Not logged in"}</div>
94
- <div><span className="text-gray-400">2FA Complete:</span> {String((session?.user as any)?.twoFactorSessionVerified ?? "unknown")}</div>
95
- <div>
96
- <span className="text-gray-400">Access Token:</span>{" "}
97
- {sessionDetails?.accessToken ? `${sessionDetails.accessToken.substring(0, 40)}...` : "N/A"}
98
- </div>
99
- <div>
100
- <span className="text-gray-400">Refresh Token:</span>{" "}
101
- {sessionDetails?.refreshToken ? `${sessionDetails.refreshToken.substring(0, 40)}...` : "N/A"}
102
- </div>
103
- <div>
104
- <span className="text-gray-400">Access Expires:</span>{" "}
105
- {formatExpiry(sessionDetails?.accessTokenExpires)}
106
- </div>
107
- </div>
108
- </div>
109
-
110
- {/* Actions */}
111
- <div className="flex gap-2 mb-4">
112
- <button
113
- className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded disabled:opacity-50"
114
- onClick={handleRefresh}
115
- disabled={loading}
116
- >
117
- {loading ? "Refreshing..." : "Test Refresh Token"}
118
- </button>
119
- <button
120
- className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded disabled:opacity-50"
121
- onClick={handleForceExpire}
122
- disabled={loading}
123
- >
124
- Force Expire Token
125
- </button>
126
- <button
127
- className="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded"
128
- onClick={() => window.location.reload()}
129
- >
130
- Reload Page
131
- </button>
132
- </div>
133
-
134
- {/* Result */}
135
- {result && (
136
- <div className="rounded border border-gray-600 bg-gray-800 p-4">
137
- <h3 className="font-semibold mb-2 text-gray-300">Result:</h3>
138
- <pre className="text-xs overflow-x-auto whitespace-pre-wrap text-green-400">
139
- {JSON.stringify(result, null, 2)}
140
- </pre>
141
- </div>
142
- )}
143
-
144
- {/* Raw Session Details */}
145
- <details className="mt-4">
146
- <summary className="cursor-pointer text-gray-400 hover:text-white">Raw Session Details</summary>
147
- <pre className="mt-2 text-xs bg-gray-800 p-2 rounded overflow-x-auto text-gray-300">
148
- {JSON.stringify(sessionDetails, null, 2)}
149
- </pre>
150
- </details>
151
- </div>
152
- );
153
- }
154
-
155
- export default RefreshTokenPage;
1
+ "use client";
2
+ import React, { useState, useEffect } from "react";
3
+ import { authClient } from '../../client/better-auth-client';
4
+
5
+ /**
6
+ * Refresh Token Test Page
7
+ *
8
+ * Debug page for testing OAuth refresh token flow.
9
+ * Shows current session state, allows manual refresh trigger,
10
+ * and can force-expire tokens for testing.
11
+ *
12
+ * Usage:
13
+ * ```typescript
14
+ * // app/test-env/refresh-token/page.tsx
15
+ * export { RefreshTokenPage as default } from '@payez/next-mvp/pages/test-env';
16
+ * ```
17
+ */
18
+ export function RefreshTokenPage() {
19
+ const { data: session } = authClient.useSession();
20
+ // TODO: Better Auth session refresh
21
+ const update = async () => { /* no-op: Better Auth handles session refresh internally */ };
22
+ const [result, setResult] = useState<any>(null);
23
+ const [loading, setLoading] = useState(false);
24
+ const [sessionDetails, setSessionDetails] = useState<any>(null);
25
+
26
+ // Fetch detailed session info on mount
27
+ useEffect(() => {
28
+ async function fetchSessionDetails() {
29
+ try {
30
+ const res = await fetch("/api/auth/session", { credentials: "include" });
31
+ const data = await res.json();
32
+ setSessionDetails(data);
33
+ } catch (e) {
34
+ console.error("Failed to fetch session details", e);
35
+ }
36
+ }
37
+ fetchSessionDetails();
38
+ }, [result]); // Refetch after refresh
39
+
40
+ async function handleRefresh() {
41
+ setLoading(true);
42
+ setResult(null);
43
+ try {
44
+ const res = await fetch("/api/auth/refresh", {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ credentials: "include",
48
+ });
49
+ const data = await res.json();
50
+ setResult({ status: res.status, ...data });
51
+ // Update NextAuth session
52
+ await update();
53
+ } catch (e: any) {
54
+ setResult({ error: e.message });
55
+ } finally {
56
+ setLoading(false);
57
+ }
58
+ }
59
+
60
+ async function handleForceExpire() {
61
+ setLoading(true);
62
+ setResult(null);
63
+ try {
64
+ const res = await fetch("/api/test/force-expire", {
65
+ method: "POST",
66
+ credentials: "include",
67
+ });
68
+ const data = await res.json();
69
+ setResult({ action: "force_expire", status: res.status, ...data });
70
+ } catch (e: any) {
71
+ setResult({ error: e.message });
72
+ } finally {
73
+ setLoading(false);
74
+ }
75
+ }
76
+
77
+ const formatExpiry = (exp: number | string | undefined) => {
78
+ if (!exp) return "N/A";
79
+ const date = new Date(typeof exp === "string" ? exp : exp);
80
+ const now = new Date();
81
+ const diffMs = date.getTime() - now.getTime();
82
+ const diffMins = Math.floor(diffMs / 60000);
83
+ const diffSecs = Math.floor((diffMs % 60000) / 1000);
84
+ return `${date.toLocaleTimeString()} (${diffMins}m ${diffSecs}s remaining)`;
85
+ };
86
+
87
+ return (
88
+ <div className="p-8 max-w-2xl mx-auto bg-gray-900 min-h-screen text-white">
89
+ <h1 className="text-2xl font-bold mb-4">Refresh Token Test</h1>
90
+
91
+ {/* Session Info */}
92
+ <div className="mb-6 rounded border border-blue-500 bg-blue-900/30 p-4">
93
+ <h2 className="font-semibold mb-2 text-blue-300">Current Session</h2>
94
+ <div className="text-sm space-y-1 font-mono">
95
+ <div><span className="text-gray-400">User:</span> {session?.user?.email || "Not logged in"}</div>
96
+ <div><span className="text-gray-400">2FA Complete:</span> {String((session?.user as any)?.twoFactorSessionVerified ?? "unknown")}</div>
97
+ <div>
98
+ <span className="text-gray-400">Access Token:</span>{" "}
99
+ {sessionDetails?.accessToken ? `${sessionDetails.accessToken.substring(0, 40)}...` : "N/A"}
100
+ </div>
101
+ <div>
102
+ <span className="text-gray-400">Refresh Token:</span>{" "}
103
+ {sessionDetails?.refreshToken ? `${sessionDetails.refreshToken.substring(0, 40)}...` : "N/A"}
104
+ </div>
105
+ <div>
106
+ <span className="text-gray-400">Access Expires:</span>{" "}
107
+ {formatExpiry(sessionDetails?.accessTokenExpires)}
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ {/* Actions */}
113
+ <div className="flex gap-2 mb-4">
114
+ <button
115
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded disabled:opacity-50"
116
+ onClick={handleRefresh}
117
+ disabled={loading}
118
+ >
119
+ {loading ? "Refreshing..." : "Test Refresh Token"}
120
+ </button>
121
+ <button
122
+ className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded disabled:opacity-50"
123
+ onClick={handleForceExpire}
124
+ disabled={loading}
125
+ >
126
+ Force Expire Token
127
+ </button>
128
+ <button
129
+ className="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded"
130
+ onClick={() => window.location.reload()}
131
+ >
132
+ Reload Page
133
+ </button>
134
+ </div>
135
+
136
+ {/* Result */}
137
+ {result && (
138
+ <div className="rounded border border-gray-600 bg-gray-800 p-4">
139
+ <h3 className="font-semibold mb-2 text-gray-300">Result:</h3>
140
+ <pre className="text-xs overflow-x-auto whitespace-pre-wrap text-green-400">
141
+ {JSON.stringify(result, null, 2)}
142
+ </pre>
143
+ </div>
144
+ )}
145
+
146
+ {/* Raw Session Details */}
147
+ <details className="mt-4">
148
+ <summary className="cursor-pointer text-gray-400 hover:text-white">Raw Session Details</summary>
149
+ <pre className="mt-2 text-xs bg-gray-800 p-2 rounded overflow-x-auto text-gray-300">
150
+ {JSON.stringify(sessionDetails, null, 2)}
151
+ </pre>
152
+ </details>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ export default RefreshTokenPage;
@@ -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
  import Link from 'next/link';
6
6
 
@@ -16,7 +16,9 @@ import Link from 'next/link';
16
16
  * ```
17
17
  */
18
18
  export function TestEnvPage() {
19
- const { data: session, status } = useSession();
19
+ const { data: sessionData, isPending } = authClient.useSession();
20
+ const session = sessionData;
21
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
20
22
  const [isDarkMode, setIsDarkMode] = useState(false);
21
23
 
22
24
  useEffect(() => {
@@ -32,7 +32,7 @@
32
32
 
33
33
  import React, { useState, useEffect, useRef } from 'react';
34
34
  import { useRouter, useSearchParams } from 'next/navigation';
35
- import { useSession, signOut, getSession } from 'next-auth/react';
35
+ import { authClient } from '../../client/better-auth-client';
36
36
  import { Suspense } from 'react';
37
37
  import { useColors } from '../../theme/useTheme';
38
38
 
@@ -54,7 +54,11 @@ function VerifyCodeForm() {
54
54
  const searchParams = useSearchParams();
55
55
  const callbackUrl = searchParams?.get('callbackUrl') || '/dashboard';
56
56
 
57
- const { data: session, status, update: updateSession } = useSession();
57
+ const { data: sessionData, isPending } = authClient.useSession();
58
+ const session = sessionData;
59
+ const status = isPending ? 'loading' : session ? 'authenticated' : 'unauthenticated';
60
+ // TODO: Better Auth session refresh
61
+ const updateSession = async () => { return session; };
58
62
  const colors = useColors();
59
63
 
60
64
  // Method selection
@@ -146,7 +150,7 @@ function VerifyCodeForm() {
146
150
  // Session expired - redirect to login
147
151
  setError('Your session has expired. Redirecting to login...');
148
152
  setTimeout(async () => {
149
- await signOut({ redirect: false });
153
+ await authClient.signOut();
150
154
  const safeCallback = callbackUrl.startsWith('/account-auth/') ? '/dashboard' : callbackUrl;
151
155
  router.push(`/account-auth/login?callbackUrl=${encodeURIComponent(safeCallback)}`);
152
156
  }, 1200);
@@ -197,7 +201,7 @@ function VerifyCodeForm() {
197
201
  if (data.valid === false || data.mfaExpired === true) {
198
202
  setError('Your session has expired. Redirecting to login...');
199
203
  setTimeout(async () => {
200
- await signOut({ redirect: false });
204
+ await authClient.signOut();
201
205
  if (typeof window !== 'undefined') {
202
206
  sessionStorage.removeItem(VERIFY_IN_PROGRESS_KEY);
203
207
  }
@@ -243,7 +247,7 @@ function VerifyCodeForm() {
243
247
  );
244
248
 
245
249
  setTimeout(async () => {
246
- await signOut({ redirect: false });
250
+ await authClient.signOut();
247
251
  if (typeof window !== 'undefined') {
248
252
  sessionStorage.removeItem(VERIFY_IN_PROGRESS_KEY);
249
253
  }
@@ -318,7 +322,7 @@ function VerifyCodeForm() {
318
322
  );
319
323
 
320
324
  setTimeout(async () => {
321
- await signOut({ redirect: false });
325
+ await authClient.signOut();
322
326
  if (typeof window !== 'undefined') {
323
327
  sessionStorage.removeItem(VERIFY_IN_PROGRESS_KEY);
324
328
  }
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import { NextRequest, NextResponse } from 'next/server';
18
- import { getToken } from 'next-auth/jwt';
18
+ import { getSession } from '../../server/auth';
19
19
  import { deleteSession } from '../../lib/session-store';
20
20
  import {
21
21
  getSessionCookieName,
@@ -23,39 +23,22 @@ import {
23
23
  getCsrfCookieName,
24
24
  getSecureCsrfCookieName,
25
25
  getCallbackUrlCookieName,
26
- getJwtCookieName
27
26
  } from '../../lib/app-slug';
28
- import { getIDPClientConfig } from '../../lib/idp-client-config';
29
27
  import { siteEvents, getClientIp } from '../../lib/site-logger';
30
28
 
31
- async function getConfig() {
32
- const idpConfig = await getIDPClientConfig();
33
- const idpBaseUrl = process.env.IDP_URL;
34
- if (!idpBaseUrl) {
35
- throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
36
- }
37
- return {
38
- nextAuthSecret: idpConfig.nextAuthSecret || '',
39
- idpBaseUrl,
40
- clientId: process.env.CLIENT_ID || process.env.NEXT_PUBLIC_IDP_CLIENT_ID || '',
41
- };
42
- }
43
-
44
29
  /**
45
30
  * POST /api/auth/logout - Sign out and clean up session
46
31
  *
47
32
  * Performs complete logout:
48
33
  * 1. Revokes tokens at IDP (if refresh token available)
49
34
  * 2. Deletes session from store
50
- * 3. Clears NextAuth session cookie
35
+ * 3. Clears session cookies
51
36
  */
52
37
  export async function POST(req: NextRequest) {
53
- const { nextAuthSecret, idpBaseUrl, clientId } = await getConfig();
54
-
55
38
  try {
56
- const token = await getToken({ req, secret: nextAuthSecret, cookieName: getJwtCookieName() });
39
+ const session = await getSession(req);
57
40
 
58
- if (!token) {
41
+ if (!session) {
59
42
  // Already logged out
60
43
  return NextResponse.json({
61
44
  success: true,
@@ -63,8 +46,7 @@ export async function POST(req: NextRequest) {
63
46
  });
64
47
  }
65
48
 
66
- // Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
67
- const sessionId = (token as any).sessionToken || (token as any).redisSessionId;
49
+ const sessionId = session.session?.token;
68
50
 
69
51
  // Delete session from store (this also removes the refresh token)
70
52
  if (sessionId) {
@@ -77,7 +59,7 @@ export async function POST(req: NextRequest) {
77
59
  }
78
60
 
79
61
  // Log logout event (fire-and-forget)
80
- const userId = (token as any).sub || (token as any).idpUserId;
62
+ const userId = session.user?.id;
81
63
  if (userId) {
82
64
  siteEvents.logout({
83
65
  user_id: userId,
@@ -89,7 +71,7 @@ export async function POST(req: NextRequest) {
89
71
  });
90
72
  }
91
73
 
92
- // Build response that clears NextAuth cookies
74
+ // Build response that clears session cookies
93
75
  const response = NextResponse.json({
94
76
  success: true,
95
77
  message: 'Logged out successfully'
@@ -1,71 +1,45 @@
1
- /**
2
- * Ready-to-Use NextAuth Route Handler
3
- *
4
- * Provides a pre-configured NextAuth handler that uses dynamic OAuth providers
5
- * loaded from IDP at startup via getAuthOptions().
6
- *
7
- * @version 2.2.0 - Dynamic provider loading from IDP
8
- * @since auth-ready-v2-hotfix
9
- */
10
-
11
- import NextAuth from 'next-auth';
12
- import { authOptions, getAuthOptions } from '../../auth/auth-options';
13
-
14
- // Cached handler - built once with dynamic providers
15
- let cachedHandler: ReturnType<typeof NextAuth> | null = null;
16
- let handlerPromise: Promise<ReturnType<typeof NextAuth>> | null = null;
17
-
18
- /**
19
- * Get or build the NextAuth handler with dynamic providers.
20
- * Uses caching to avoid rebuilding on every request.
21
- */
22
- async function getHandler(): Promise<ReturnType<typeof NextAuth>> {
23
- // Return cached if available
24
- if (cachedHandler) {
25
- return cachedHandler;
26
- }
27
-
28
- // Prevent concurrent builds
29
- if (handlerPromise) {
30
- return handlerPromise;
31
- }
32
-
33
- handlerPromise = (async () => {
34
- try {
35
- // Try to get dynamic auth options from IDP
36
- const options = await getAuthOptions();
37
- console.log('[NEXTAUTH_ROUTE] Built handler with dynamic providers');
38
- cachedHandler = NextAuth(options);
39
- return cachedHandler;
40
- } catch (error) {
41
- // Fallback to static options if IDP unavailable
42
- console.warn('[NEXTAUTH_ROUTE] Failed to get dynamic options, using static fallback:', {
43
- error: error instanceof Error ? error.message : String(error)
44
- });
45
- cachedHandler = NextAuth(authOptions);
46
- return cachedHandler;
47
- } finally {
48
- handlerPromise = null;
49
- }
50
- })();
51
-
52
- return handlerPromise;
53
- }
54
-
55
- /**
56
- * GET handler for NextAuth
57
- * Uses async factory to get dynamic providers from IDP
58
- */
59
- export async function GET(request: Request, context: any) {
60
- const handler = await getHandler();
61
- return handler(request, context);
62
- }
63
-
64
- /**
65
- * POST handler for NextAuth
66
- * Uses async factory to get dynamic providers from IDP
67
- */
68
- export async function POST(request: Request, context: any) {
69
- const handler = await getHandler();
70
- return handler(request, context);
71
- }
1
+ /**
2
+ * Ready-to-Use Auth Route Handler (Better Auth)
3
+ *
4
+ * Provides a pre-configured Better Auth handler that uses dynamic OAuth providers
5
+ * loaded from IDP at startup.
6
+ *
7
+ * Replaces the former NextAuth handler. The file name is kept as nextauth.ts
8
+ * to avoid breaking re-exports in routes/auth/index.ts.
9
+ *
10
+ * @version 4.0.0 - Better Auth migration
11
+ * @since better-auth-4.0
12
+ */
13
+
14
+ import { getBetterAuthHandler } from '../../auth/better-auth';
15
+ import { NextResponse } from 'next/server';
16
+
17
+ /**
18
+ * GET handler for auth routes
19
+ * Delegates to Better Auth instance.
20
+ */
21
+ export async function GET(request: Request) {
22
+ const handler = await getBetterAuthHandler();
23
+ if (!handler) {
24
+ return NextResponse.json(
25
+ { error: 'Auth handler not available' },
26
+ { status: 503 }
27
+ );
28
+ }
29
+ return handler.GET(request);
30
+ }
31
+
32
+ /**
33
+ * POST handler for auth routes
34
+ * Delegates to Better Auth instance.
35
+ */
36
+ export async function POST(request: Request) {
37
+ const handler = await getBetterAuthHandler();
38
+ if (!handler) {
39
+ return NextResponse.json(
40
+ { error: 'Auth handler not available' },
41
+ { status: 503 }
42
+ );
43
+ }
44
+ return handler.POST(request);
45
+ }