@payez/next-mvp 4.4.6 → 4.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-handlers/account/change-password.js +28 -6
- package/dist/auth/better-auth.d.ts +62 -15
- package/dist/auth/better-auth.js +180 -19
- package/dist/client/better-auth-client.d.ts +101 -956
- package/dist/config/env.d.ts +0 -2
- package/dist/config/env.js +0 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/dist/lib/idp-client-config.js +8 -1
- package/dist/lib/never-in-response.d.ts +30 -0
- package/dist/lib/never-in-response.js +71 -0
- package/dist/lib/nextauth-secret.d.ts +51 -0
- package/dist/lib/nextauth-secret.js +141 -0
- package/dist/lib/safe-error-response.d.ts +6 -6
- package/dist/lib/safe-error-response.js +6 -6
- package/dist/lib/two-factor-verify-failure.d.ts +36 -3
- package/dist/lib/two-factor-verify-failure.js +79 -2
- package/dist/middleware/create-middleware.d.ts +2 -0
- package/dist/middleware/create-middleware.js +36 -0
- package/dist/pages/login/page.js +5 -0
- package/dist/pages/verify-code/page.js +4 -2
- package/dist/routes/auth/settings.js +4 -1
- package/dist/server/auth.d.ts +36 -294
- package/dist/server/auth.js +22 -25
- package/dist/vibe/client.d.ts +132 -16
- package/dist/vibe/client.js +288 -82
- package/dist/vibe/document-unwrap.d.ts +50 -0
- package/dist/vibe/document-unwrap.js +78 -0
- package/dist/vibe/errors.d.ts +17 -0
- package/dist/vibe/errors.js +21 -1
- package/dist/vibe/generic.d.ts +2 -49
- package/dist/vibe/generic.js +17 -78
- package/dist/vibe/index.d.ts +1 -1
- package/dist/vibe/index.js +2 -1
- package/package.json +17 -2
- package/src/api-handlers/account/change-password.ts +31 -6
- package/src/auth/better-auth.ts +188 -21
- package/src/config/env.ts +0 -2
- package/src/index.ts +4 -0
- package/src/lib/idp-client-config.ts +8 -1
- package/src/lib/never-in-response.ts +64 -0
- package/src/lib/nextauth-secret.ts +168 -0
- package/src/lib/safe-error-response.ts +6 -6
- package/src/lib/two-factor-verify-failure.ts +86 -5
- package/src/middleware/create-middleware.ts +42 -0
- package/src/pages/login/page.tsx +4 -0
- package/src/pages/verify-code/page.tsx +4 -2
- package/src/routes/auth/settings.ts +5 -1
- package/src/server/auth.ts +23 -29
- package/src/vibe/client.ts +378 -91
- package/src/vibe/document-unwrap.ts +103 -0
- package/src/vibe/errors.ts +24 -0
- package/src/vibe/generic.ts +8 -100
- package/src/vibe/index.ts +1 -0
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* });
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.TWO_FACTOR_TIMEOUT_ERROR = void 0;
|
|
21
22
|
exports.createMvpMiddleware = createMvpMiddleware;
|
|
22
23
|
const server_1 = require("next/server");
|
|
23
24
|
const route_config_1 = require("../auth/route-config");
|
|
@@ -143,6 +144,26 @@ function refreshFailureResponse(request, location, errorCode, clearCookies = fal
|
|
|
143
144
|
function makeAuthDecision(ctx) {
|
|
144
145
|
const { pathname, isPublicRoute, isLoginPage: onLoginPage, searchParams, sessionPointer, sessionStatus, circuitBreakerOpen } = ctx;
|
|
145
146
|
const safeCallback = getSafeCallbackUrl(pathname);
|
|
147
|
+
// --- PAY-884: a provisional (pre-2FA) session whose token has expired can never finish 2FA ---
|
|
148
|
+
// The IdP issues no refresh token until 2FA completes, and it validates token lifetime, so
|
|
149
|
+
// once the provisional access token expires neither refresh nor code verification can
|
|
150
|
+
// succeed. Sending the user to the verify-code page (or to error=SessionExpired, which the
|
|
151
|
+
// login page deliberately shows as NO message) strands them mid-2FA with no explanation.
|
|
152
|
+
// Say what happened instead, and clear the dead session so the next sign-in starts clean.
|
|
153
|
+
// Scoped to the verify-code page and protected pages: /api/ keeps its 401-JSON carve-out
|
|
154
|
+
// (307290), the login page stays reachable, and public pages are left to the existing arms.
|
|
155
|
+
if (isExpiredProvisionalSession(sessionPointer, sessionStatus) &&
|
|
156
|
+
!pathname.startsWith('/api/') &&
|
|
157
|
+
!onLoginPage &&
|
|
158
|
+
(pathname === VERIFY_CODE_PAGE || !isPublicRoute)) {
|
|
159
|
+
const callback = pathname === VERIFY_CODE_PAGE ? getSafeCallbackUrl(pathname, searchParams) : safeCallback;
|
|
160
|
+
return {
|
|
161
|
+
type: 'redirect',
|
|
162
|
+
location: `${LOGIN_PAGE}?callbackUrl=${encodeURIComponent(callback)}&error=${exports.TWO_FACTOR_TIMEOUT_ERROR}`,
|
|
163
|
+
reason: 'pre_2fa_token_expired',
|
|
164
|
+
clearCookies: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
146
167
|
// --- Public routes: Allow, but enforce 2FA for authenticated users ---
|
|
147
168
|
if (isPublicRoute && !onLoginPage) {
|
|
148
169
|
if (needsTwoFactorRedirect(pathname, sessionPointer, sessionStatus)) {
|
|
@@ -249,6 +270,21 @@ function makeAuthDecision(ctx) {
|
|
|
249
270
|
// --- All checks passed ---
|
|
250
271
|
return { type: 'allow' };
|
|
251
272
|
}
|
|
273
|
+
/** Login error code for a pre-2FA session that expired before the code was verified (PAY-884). */
|
|
274
|
+
exports.TWO_FACTOR_TIMEOUT_ERROR = 'TwoFactorTimeout';
|
|
275
|
+
/**
|
|
276
|
+
* PAY-884: 2FA outstanding, access token expired, and no refresh token to renew it.
|
|
277
|
+
* Only a viability answer that positively says the session exists qualifies (null = check failed).
|
|
278
|
+
*/
|
|
279
|
+
function isExpiredProvisionalSession(session, status) {
|
|
280
|
+
return (session.exists &&
|
|
281
|
+
status.exists === true &&
|
|
282
|
+
status.forceInvalid === false &&
|
|
283
|
+
status.requires2FA &&
|
|
284
|
+
!status.twoFactorComplete &&
|
|
285
|
+
session.expired === true &&
|
|
286
|
+
!session.hasRefreshToken);
|
|
287
|
+
}
|
|
252
288
|
/** Check if user needs 2FA redirect */
|
|
253
289
|
function needsTwoFactorRedirect(pathname, session, status) {
|
|
254
290
|
if (!session.exists || !status.exists)
|
package/dist/pages/login/page.js
CHANGED
|
@@ -131,6 +131,11 @@ function LoginForm({ altAuthSlot }) {
|
|
|
131
131
|
console.log('[LOGIN] SessionExpired detected - normal behavior');
|
|
132
132
|
friendlyError = null;
|
|
133
133
|
}
|
|
134
|
+
else if (urlError === 'TwoFactorTimeout') {
|
|
135
|
+
// PAY-884: the sign-in expired while the verification code was outstanding. Unlike
|
|
136
|
+
// SessionExpired this is NOT silent - the user was mid-2FA and must know why they are here.
|
|
137
|
+
friendlyError = 'Your verification step timed out before the code was confirmed. Please sign in again.';
|
|
138
|
+
}
|
|
134
139
|
else if (urlError === 'ServiceUnavailable') {
|
|
135
140
|
friendlyError = 'Authentication service is temporarily unavailable. Please try again in a few minutes.';
|
|
136
141
|
}
|
|
@@ -302,7 +302,8 @@ function VerifyCodeForm({ enrollHref }) {
|
|
|
302
302
|
sessionStorage.removeItem(VERIFY_IN_PROGRESS_KEY);
|
|
303
303
|
}
|
|
304
304
|
const safeCallback = callbackUrl.startsWith('/account-auth/') ? '/dashboard' : callbackUrl;
|
|
305
|
-
|
|
305
|
+
// PAY-884: an expired JWT here is the provisional pre-2FA token - say so on the login page.
|
|
306
|
+
const errorParam = isJwtExpired ? '&error=TwoFactorTimeout' : '';
|
|
306
307
|
router.push(`/account-auth/login?callbackUrl=${encodeURIComponent(safeCallback)}${errorParam}`);
|
|
307
308
|
}, 1500);
|
|
308
309
|
return;
|
|
@@ -366,7 +367,8 @@ function VerifyCodeForm({ enrollHref }) {
|
|
|
366
367
|
sessionStorage.removeItem(VERIFY_IN_PROGRESS_KEY);
|
|
367
368
|
}
|
|
368
369
|
const safeCallback = callbackUrl.startsWith('/account-auth/') ? '/dashboard' : callbackUrl;
|
|
369
|
-
|
|
370
|
+
// PAY-884: an expired JWT here is the provisional pre-2FA token - say so on the login page.
|
|
371
|
+
const errorParam = isJwtExpired ? '&error=TwoFactorTimeout' : '';
|
|
370
372
|
router.push(`/account-auth/login?callbackUrl=${encodeURIComponent(safeCallback)}${errorParam}`);
|
|
371
373
|
}, 1500);
|
|
372
374
|
return;
|
|
@@ -31,6 +31,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
31
31
|
exports.GET = GET;
|
|
32
32
|
const server_1 = require("next/server");
|
|
33
33
|
const idp_client_config_1 = require("../../lib/idp-client-config");
|
|
34
|
+
const never_in_response_1 = require("../../lib/never-in-response");
|
|
34
35
|
/**
|
|
35
36
|
* Fail closed and LOUD. 503 + success:false + a code, and CRUCIALLY no policy
|
|
36
37
|
* fields at all - not defaults, not partials. A consumer cannot mistake an
|
|
@@ -105,5 +106,7 @@ async function GET() {
|
|
|
105
106
|
require2FA: authSettings.require2FA,
|
|
106
107
|
allowed2FAMethods: authSettings.allowed2FAMethods,
|
|
107
108
|
};
|
|
108
|
-
|
|
109
|
+
// PAY-1529: the public policy is built from an allowlist above; secretSafeJson is the runtime backstop
|
|
110
|
+
// that answers 500 instead of serving it if a registered secret ever reaches this body anyway.
|
|
111
|
+
return (0, never_in_response_1.secretSafeJson)({ success: true, data: settings }, { headers: { 'Cache-Control': 'no-store' } }, 'GET /api/auth/settings');
|
|
109
112
|
}
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -24,300 +24,42 @@ export type IdpTokenResult = {
|
|
|
24
24
|
terminal: false;
|
|
25
25
|
sessionData: SessionData;
|
|
26
26
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
}, ...(import("@better-auth/core").BetterAuthPlugin | {
|
|
65
|
-
id: "magic-link";
|
|
66
|
-
endpoints: {
|
|
67
|
-
signInMagicLink: import("better-call").StrictEndpoint<"/sign-in/magic-link", {
|
|
68
|
-
method: "POST";
|
|
69
|
-
requireHeaders: true;
|
|
70
|
-
body: import("zod").ZodObject<{
|
|
71
|
-
email: import("zod").ZodEmail;
|
|
72
|
-
name: import("zod").ZodOptional<import("zod").ZodString>;
|
|
73
|
-
callbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
74
|
-
newUserCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
75
|
-
errorCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
76
|
-
metadata: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
|
|
77
|
-
}, import("better-auth/*").$strip>;
|
|
78
|
-
metadata: {
|
|
79
|
-
openapi: {
|
|
80
|
-
operationId: string;
|
|
81
|
-
description: string;
|
|
82
|
-
responses: {
|
|
83
|
-
200: {
|
|
84
|
-
description: string;
|
|
85
|
-
content: {
|
|
86
|
-
"application/json": {
|
|
87
|
-
schema: {
|
|
88
|
-
type: "object";
|
|
89
|
-
properties: {
|
|
90
|
-
status: {
|
|
91
|
-
type: string;
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
};
|
|
101
|
-
}, {
|
|
102
|
-
status: boolean;
|
|
103
|
-
}>;
|
|
104
|
-
magicLinkVerify: import("better-call").StrictEndpoint<"/magic-link/verify", {
|
|
105
|
-
method: "GET";
|
|
106
|
-
query: import("zod").ZodObject<{
|
|
107
|
-
token: import("zod").ZodString;
|
|
108
|
-
callbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
109
|
-
errorCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
110
|
-
newUserCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
111
|
-
}, import("better-auth/*").$strip>;
|
|
112
|
-
use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<void>)[];
|
|
113
|
-
requireHeaders: true;
|
|
114
|
-
metadata: {
|
|
115
|
-
openapi: {
|
|
116
|
-
operationId: string;
|
|
117
|
-
description: string;
|
|
118
|
-
responses: {
|
|
119
|
-
200: {
|
|
120
|
-
description: string;
|
|
121
|
-
content: {
|
|
122
|
-
"application/json": {
|
|
123
|
-
schema: {
|
|
124
|
-
type: "object";
|
|
125
|
-
properties: {
|
|
126
|
-
session: {
|
|
127
|
-
$ref: string;
|
|
128
|
-
};
|
|
129
|
-
user: {
|
|
130
|
-
$ref: string;
|
|
131
|
-
};
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
}, {
|
|
141
|
-
token: string;
|
|
142
|
-
user: {
|
|
143
|
-
id: string;
|
|
144
|
-
createdAt: Date;
|
|
145
|
-
updatedAt: Date;
|
|
146
|
-
email: string;
|
|
147
|
-
emailVerified: boolean;
|
|
148
|
-
name: string;
|
|
149
|
-
image?: string | null | undefined;
|
|
150
|
-
};
|
|
151
|
-
}>;
|
|
152
|
-
};
|
|
153
|
-
rateLimit: {
|
|
154
|
-
pathMatcher(path: string): boolean;
|
|
155
|
-
window: number;
|
|
156
|
-
max: number;
|
|
157
|
-
}[];
|
|
158
|
-
options: import("better-auth/plugins/magic-link").MagicLinkOptions;
|
|
159
|
-
} | {
|
|
160
|
-
id: "generic-oauth";
|
|
161
|
-
init: (ctx: import("@better-auth/core").AuthContext) => {
|
|
162
|
-
context: {
|
|
163
|
-
socialProviders: import("better-auth/*").OAuthProvider<Record<string, any>, Partial<import("better-auth/*").ProviderOptions<any>>>[];
|
|
164
|
-
};
|
|
165
|
-
};
|
|
166
|
-
endpoints: {
|
|
167
|
-
signInWithOAuth2: import("better-call").StrictEndpoint<"/sign-in/oauth2", {
|
|
168
|
-
method: "POST";
|
|
169
|
-
body: import("zod").ZodObject<{
|
|
170
|
-
providerId: import("zod").ZodString;
|
|
171
|
-
callbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
172
|
-
errorCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
173
|
-
newUserCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
174
|
-
disableRedirect: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
175
|
-
scopes: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
176
|
-
requestSignUp: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
177
|
-
additionalData: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
|
|
178
|
-
}, import("better-auth/*").$strip>;
|
|
179
|
-
metadata: {
|
|
180
|
-
openapi: {
|
|
181
|
-
description: string;
|
|
182
|
-
responses: {
|
|
183
|
-
200: {
|
|
184
|
-
description: string;
|
|
185
|
-
content: {
|
|
186
|
-
"application/json": {
|
|
187
|
-
schema: {
|
|
188
|
-
type: "object";
|
|
189
|
-
properties: {
|
|
190
|
-
url: {
|
|
191
|
-
type: string;
|
|
192
|
-
};
|
|
193
|
-
redirect: {
|
|
194
|
-
type: string;
|
|
195
|
-
};
|
|
196
|
-
};
|
|
197
|
-
};
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
};
|
|
201
|
-
};
|
|
202
|
-
};
|
|
203
|
-
};
|
|
204
|
-
}, {
|
|
205
|
-
url: string;
|
|
206
|
-
redirect: boolean;
|
|
207
|
-
}>;
|
|
208
|
-
oAuth2Callback: import("better-call").StrictEndpoint<"/oauth2/callback/:providerId", {
|
|
209
|
-
method: "GET";
|
|
210
|
-
query: import("zod").ZodObject<{
|
|
211
|
-
code: import("zod").ZodOptional<import("zod").ZodString>;
|
|
212
|
-
error: import("zod").ZodOptional<import("zod").ZodString>;
|
|
213
|
-
error_description: import("zod").ZodOptional<import("zod").ZodString>;
|
|
214
|
-
state: import("zod").ZodOptional<import("zod").ZodString>;
|
|
215
|
-
iss: import("zod").ZodOptional<import("zod").ZodString>;
|
|
216
|
-
}, import("better-auth/*").$strip>;
|
|
217
|
-
metadata: {
|
|
218
|
-
allowedMediaTypes: string[];
|
|
219
|
-
openapi: {
|
|
220
|
-
description: string;
|
|
221
|
-
responses: {
|
|
222
|
-
200: {
|
|
223
|
-
description: string;
|
|
224
|
-
content: {
|
|
225
|
-
"application/json": {
|
|
226
|
-
schema: {
|
|
227
|
-
type: "object";
|
|
228
|
-
properties: {
|
|
229
|
-
url: {
|
|
230
|
-
type: string;
|
|
231
|
-
};
|
|
232
|
-
};
|
|
233
|
-
};
|
|
234
|
-
};
|
|
235
|
-
};
|
|
236
|
-
};
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
scope: "server";
|
|
240
|
-
};
|
|
241
|
-
}, void>;
|
|
242
|
-
oAuth2LinkAccount: import("better-call").StrictEndpoint<"/oauth2/link", {
|
|
243
|
-
method: "POST";
|
|
244
|
-
body: import("zod").ZodObject<{
|
|
245
|
-
providerId: import("zod").ZodString;
|
|
246
|
-
callbackURL: import("zod").ZodString;
|
|
247
|
-
scopes: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
248
|
-
errorCallbackURL: import("zod").ZodOptional<import("zod").ZodString>;
|
|
249
|
-
}, import("better-auth/*").$strip>;
|
|
250
|
-
use: ((inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<{
|
|
251
|
-
session: {
|
|
252
|
-
session: Record<string, any> & {
|
|
253
|
-
id: string;
|
|
254
|
-
createdAt: Date;
|
|
255
|
-
updatedAt: Date;
|
|
256
|
-
userId: string;
|
|
257
|
-
expiresAt: Date;
|
|
258
|
-
token: string;
|
|
259
|
-
ipAddress?: string | null | undefined;
|
|
260
|
-
userAgent?: string | null | undefined;
|
|
261
|
-
};
|
|
262
|
-
user: Record<string, any> & {
|
|
263
|
-
id: string;
|
|
264
|
-
createdAt: Date;
|
|
265
|
-
updatedAt: Date;
|
|
266
|
-
email: string;
|
|
267
|
-
emailVerified: boolean;
|
|
268
|
-
name: string;
|
|
269
|
-
image?: string | null | undefined;
|
|
270
|
-
};
|
|
271
|
-
};
|
|
272
|
-
}>)[];
|
|
273
|
-
metadata: {
|
|
274
|
-
openapi: {
|
|
275
|
-
description: string;
|
|
276
|
-
responses: {
|
|
277
|
-
"200": {
|
|
278
|
-
description: string;
|
|
279
|
-
content: {
|
|
280
|
-
"application/json": {
|
|
281
|
-
schema: {
|
|
282
|
-
type: "object";
|
|
283
|
-
properties: {
|
|
284
|
-
url: {
|
|
285
|
-
type: string;
|
|
286
|
-
format: string;
|
|
287
|
-
description: string;
|
|
288
|
-
};
|
|
289
|
-
redirect: {
|
|
290
|
-
type: string;
|
|
291
|
-
description: string;
|
|
292
|
-
enum: boolean[];
|
|
293
|
-
};
|
|
294
|
-
};
|
|
295
|
-
required: string[];
|
|
296
|
-
};
|
|
297
|
-
};
|
|
298
|
-
};
|
|
299
|
-
};
|
|
300
|
-
};
|
|
301
|
-
};
|
|
302
|
-
};
|
|
303
|
-
}, {
|
|
304
|
-
url: string;
|
|
305
|
-
redirect: boolean;
|
|
306
|
-
}>;
|
|
307
|
-
};
|
|
308
|
-
options: import("better-auth/plugins/generic-oauth").GenericOAuthOptions;
|
|
309
|
-
$ERROR_CODES: {
|
|
310
|
-
INVALID_OAUTH_CONFIGURATION: import("better-auth/*").RawError<"INVALID_OAUTH_CONFIGURATION">;
|
|
311
|
-
TOKEN_URL_NOT_FOUND: import("better-auth/*").RawError<"TOKEN_URL_NOT_FOUND">;
|
|
312
|
-
PROVIDER_CONFIG_NOT_FOUND: import("better-auth/*").RawError<"PROVIDER_CONFIG_NOT_FOUND">;
|
|
313
|
-
PROVIDER_ID_REQUIRED: import("better-auth/*").RawError<"PROVIDER_ID_REQUIRED">;
|
|
314
|
-
INVALID_OAUTH_CONFIG: import("better-auth/*").RawError<"INVALID_OAUTH_CONFIG">;
|
|
315
|
-
SESSION_REQUIRED: import("better-auth/*").RawError<"SESSION_REQUIRED">;
|
|
316
|
-
ISSUER_MISMATCH: import("better-auth/*").RawError<"ISSUER_MISMATCH">;
|
|
317
|
-
ISSUER_MISSING: import("better-auth/*").RawError<"ISSUER_MISSING">;
|
|
318
|
-
};
|
|
319
|
-
})[]];
|
|
320
|
-
}>>;
|
|
27
|
+
/**
|
|
28
|
+
* Get the initialized Better Auth instance (singleton).
|
|
29
|
+
*
|
|
30
|
+
* 244549: the single-flight memoisation lives in lib/single-flight.ts rather
|
|
31
|
+
* than inline here, and that placement is deliberate rather than tidiness.
|
|
32
|
+
* The bug being fixed -- a REJECTED init promise cached for the life of the
|
|
33
|
+
* process, so recovery was restart-only -- went unnoticed precisely because
|
|
34
|
+
* this module cannot be loaded in a unit test: importing it pulls in
|
|
35
|
+
* better-auth and the whole IDP config stack. Extracted into a zero-import
|
|
36
|
+
* module, the retry semantics are covered by
|
|
37
|
+
* test/single-flight-retry.test.cjs, which imports dist/ and therefore
|
|
38
|
+
* exercises the artefact that actually ships.
|
|
39
|
+
*
|
|
40
|
+
* PAY-1623 (package half, BAPert 62449): THIS FUNNEL NO LONGER BUILDS ITS OWN
|
|
41
|
+
* INSTANCE. It used to call `createBetterAuthInstance(config)` with no opts, so
|
|
42
|
+
* it carried NONE of the app-supplied plugins registered by the host app at
|
|
43
|
+
* startup (`configureBetterAuth()` -> `state.configuredOpts`). `samlAcs()` and
|
|
44
|
+
* `passkeySignIn()` arrive only that way, so `getAuthInstance()` returned an
|
|
45
|
+
* instance on which `auth.api.samlAcs` was `undefined` — the exact package twin
|
|
46
|
+
* of the portal ACS crash `t.api.samlAcs is not a function` (Jon's walk,
|
|
47
|
+
* 2026-09-21; Portal PR 517 fixed the portal by importing the CONFIGURED
|
|
48
|
+
* instance, `getBetterAuthInstance`, which is why this file must now resolve the
|
|
49
|
+
* same one). It also silently created a SECOND, divergent instance beside the
|
|
50
|
+
* one every /api/auth/* route uses — two instances, two secrets, one process.
|
|
51
|
+
*
|
|
52
|
+
* It now DELEGATES to that configured funnel, which is the single source of
|
|
53
|
+
* truth for the process's Better Auth instance:
|
|
54
|
+
* - it carries `state.configuredOpts` (better-auth.ts:930), so the app
|
|
55
|
+
* plugins are present;
|
|
56
|
+
* - its init is already single-flighted with the retry semantics described in
|
|
57
|
+
* lib/single-flight.ts (see getBetterAuthInstance, card 244549/338215), so
|
|
58
|
+
* this function keeps the same no-latch recovery contract;
|
|
59
|
+
* - there is now ONE instance, so the double-init / divergent-secret hazard
|
|
60
|
+
* this path used to create is gone by construction.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getAuthInstance(): Promise<any>;
|
|
321
63
|
/**
|
|
322
64
|
* Get the current session from a request.
|
|
323
65
|
*
|
package/dist/server/auth.js
CHANGED
|
@@ -47,11 +47,8 @@ exports.getFreshIdpToken = getFreshIdpToken;
|
|
|
47
47
|
exports.requireSession = requireSession;
|
|
48
48
|
require("server-only");
|
|
49
49
|
const better_auth_1 = require("../auth/better-auth");
|
|
50
|
-
const idp_client_config_1 = require("../lib/idp-client-config");
|
|
51
50
|
const session_store_1 = require("../lib/session-store");
|
|
52
51
|
const verified_session_token_1 = require("./verified-session-token");
|
|
53
|
-
const single_flight_1 = require("../lib/single-flight");
|
|
54
|
-
let authInstance = null;
|
|
55
52
|
function buildSessionDataFromAuthSession(session) {
|
|
56
53
|
const user = session?.user;
|
|
57
54
|
if (!user?.id && !user?.email) {
|
|
@@ -116,30 +113,30 @@ function attachSessionData(session, sessionData, sessionToken) {
|
|
|
116
113
|
* test/single-flight-retry.test.cjs, which imports dist/ and therefore
|
|
117
114
|
* exercises the artefact that actually ships.
|
|
118
115
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
116
|
+
* PAY-1623 (package half, BAPert 62449): THIS FUNNEL NO LONGER BUILDS ITS OWN
|
|
117
|
+
* INSTANCE. It used to call `createBetterAuthInstance(config)` with no opts, so
|
|
118
|
+
* it carried NONE of the app-supplied plugins registered by the host app at
|
|
119
|
+
* startup (`configureBetterAuth()` -> `state.configuredOpts`). `samlAcs()` and
|
|
120
|
+
* `passkeySignIn()` arrive only that way, so `getAuthInstance()` returned an
|
|
121
|
+
* instance on which `auth.api.samlAcs` was `undefined` — the exact package twin
|
|
122
|
+
* of the portal ACS crash `t.api.samlAcs is not a function` (Jon's walk,
|
|
123
|
+
* 2026-09-21; Portal PR 517 fixed the portal by importing the CONFIGURED
|
|
124
|
+
* instance, `getBetterAuthInstance`, which is why this file must now resolve the
|
|
125
|
+
* same one). It also silently created a SECOND, divergent instance beside the
|
|
126
|
+
* one every /api/auth/* route uses — two instances, two secrets, one process.
|
|
127
|
+
*
|
|
128
|
+
* It now DELEGATES to that configured funnel, which is the single source of
|
|
129
|
+
* truth for the process's Better Auth instance:
|
|
130
|
+
* - it carries `state.configuredOpts` (better-auth.ts:930), so the app
|
|
131
|
+
* plugins are present;
|
|
132
|
+
* - its init is already single-flighted with the retry semantics described in
|
|
133
|
+
* lib/single-flight.ts (see getBetterAuthInstance, card 244549/338215), so
|
|
134
|
+
* this function keeps the same no-latch recovery contract;
|
|
135
|
+
* - there is now ONE instance, so the double-init / divergent-secret hazard
|
|
136
|
+
* this path used to create is gone by construction.
|
|
124
137
|
*/
|
|
125
|
-
const authInit = (0, single_flight_1.createSingleFlight)(async () => {
|
|
126
|
-
const config = await (0, idp_client_config_1.getIDPClientConfig)(true);
|
|
127
|
-
authInstance = (0, better_auth_1.createBetterAuthInstance)(config);
|
|
128
|
-
// 185121 line 1, path 2: this module-scope instance had NO creation log
|
|
129
|
-
// line — first-called during a config transition window it pinned a
|
|
130
|
-
// divergent secret INVISIBLY. Name the path and the secret fingerprint
|
|
131
|
-
// (the createBetterAuthInstance line already logged the same hash;
|
|
132
|
-
// this line is what attributes it to THIS init path).
|
|
133
|
-
// Guard matches createBetterAuthInstance's: getIDPClientConfig currently
|
|
134
|
-
// throws FATAL on a missing authSecret (QAPert 27328 verified), but that
|
|
135
|
-
// guarantee is one refactor away — log ABSENT rather than ever throw here.
|
|
136
|
-
console.log(`[AUTH] getAuthInstance: created BA instance via server/auth.ts — authSecret ${config.authSecret ? `sha256:${(0, better_auth_1.secretFingerprint)(config.authSecret)}` : 'ABSENT'}`);
|
|
137
|
-
return authInstance;
|
|
138
|
-
});
|
|
139
138
|
async function getAuthInstance() {
|
|
140
|
-
|
|
141
|
-
return authInstance;
|
|
142
|
-
return authInit.get();
|
|
139
|
+
return (0, better_auth_1.getBetterAuthInstance)();
|
|
143
140
|
}
|
|
144
141
|
/**
|
|
145
142
|
* Get the current session from a request.
|