@payez/next-mvp 4.4.5 → 4.4.7
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/README.md +10 -3
- package/dist/api/auth-handler.js +5 -3
- package/dist/api-handlers/account/change-password.js +33 -8
- package/dist/api-handlers/account/masked-info.js +2 -1
- package/dist/api-handlers/account/recovery/forward.d.ts +14 -0
- package/dist/api-handlers/account/recovery/forward.js +105 -0
- package/dist/api-handlers/account/recovery/initiate.js +4 -0
- package/dist/api-handlers/account/recovery/send-code.d.ts +12 -2
- package/dist/api-handlers/account/recovery/send-code.js +11 -84
- package/dist/api-handlers/account/recovery/verify-code.d.ts +11 -2
- package/dist/api-handlers/account/recovery/verify-code.js +10 -84
- package/dist/api-handlers/account/reset-password.js +24 -1
- package/dist/api-handlers/account/send-code.d.ts +1 -15
- package/dist/api-handlers/account/send-code.js +3 -6
- package/dist/api-handlers/account/update-phone.js +6 -1
- package/dist/api-handlers/account/validate-password.js +3 -1
- package/dist/api-handlers/account/verify-email.d.ts +1 -17
- package/dist/api-handlers/account/verify-email.js +4 -6
- package/dist/api-handlers/account/verify-sms.d.ts +1 -17
- package/dist/api-handlers/account/verify-sms.js +4 -6
- package/dist/api-handlers/admin/analytics.js +12 -4
- package/dist/api-handlers/admin/audit.js +12 -4
- package/dist/api-handlers/admin/redis-sessions.js +16 -8
- package/dist/api-handlers/admin/sessions.js +14 -6
- package/dist/api-handlers/admin/site-logs.js +39 -16
- package/dist/api-handlers/admin/stats.js +14 -4
- package/dist/api-handlers/admin/users.js +14 -6
- package/dist/api-handlers/admin/vibe-data.js +19 -11
- package/dist/api-handlers/auth/jwks.js +2 -1
- package/dist/api-handlers/auth/login.js +13 -4
- package/dist/api-handlers/auth/passkey-signin.js +6 -0
- package/dist/api-handlers/auth/refresh.js +38 -11
- package/dist/api-handlers/auth/signout.js +2 -1
- package/dist/api-handlers/auth/status.js +4 -1
- package/dist/api-handlers/auth/update-session.d.ts +25 -4
- package/dist/api-handlers/auth/update-session.js +38 -11
- package/dist/api-handlers/auth/validate.js +4 -2
- package/dist/api-handlers/auth/verify-code.js +3 -2
- package/dist/api-handlers/mfa/backup-code-verify.d.ts +1 -18
- package/dist/api-handlers/mfa/backup-code-verify.js +4 -6
- package/dist/api-handlers/mfa/backup-codes.js +2 -1
- package/dist/api-handlers/mfa/methods.js +2 -1
- package/dist/api-handlers/mfa/totp-confirm.js +2 -1
- package/dist/api-handlers/mfa/totp-enroll.js +2 -1
- package/dist/api-handlers/mfa/totp-verify.d.ts +1 -19
- package/dist/api-handlers/mfa/totp-verify.js +4 -6
- package/dist/api-handlers/passkeys/list.js +2 -1
- package/dist/api-handlers/passkeys/manage.js +2 -1
- package/dist/api-handlers/passkeys/register-options.js +2 -1
- package/dist/api-handlers/passkeys/register-verify.js +2 -1
- package/dist/api-handlers/session/refresh-viability.d.ts +4 -2
- package/dist/api-handlers/session/refresh-viability.js +10 -7
- package/dist/api-handlers/session/viability.js +4 -3
- package/dist/api-handlers/test/force-expire.js +5 -3
- package/dist/auth/auth-decision.js +2 -1
- package/dist/auth/better-auth.d.ts +58 -14
- package/dist/auth/better-auth.js +125 -54
- package/dist/auth/utils/idp-client.js +2 -1
- package/dist/client/better-auth-client.d.ts +101 -956
- package/dist/components/admin/DataBrowserTab.js +4 -1
- package/dist/components/admin/SessionsTab.js +9 -3
- package/dist/components/recovery/RequestedStep.d.ts +11 -0
- package/dist/components/recovery/RequestedStep.js +14 -0
- package/dist/components/recovery/SetPasswordStep.js +10 -1
- 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/app-slug.d.ts +21 -0
- package/dist/lib/app-slug.js +51 -0
- package/dist/lib/ensure-fresh-access-token.d.ts +26 -0
- package/dist/lib/ensure-fresh-access-token.js +105 -4
- package/dist/lib/idp-client-config.d.ts +20 -0
- package/dist/lib/idp-client-config.js +73 -45
- package/dist/lib/idp-fetch.js +1 -1
- package/dist/lib/never-in-response.d.ts +30 -0
- package/dist/lib/never-in-response.js +71 -0
- package/dist/lib/password-rules.d.ts +22 -0
- package/dist/lib/password-rules.js +43 -0
- package/dist/lib/safe-error-response.d.ts +67 -0
- package/dist/lib/safe-error-response.js +130 -0
- package/dist/lib/session-cookie-shape.d.ts +22 -19
- package/dist/lib/session-cookie-shape.js +14 -18
- package/dist/lib/session-store.d.ts +8 -1
- package/dist/lib/session-store.js +170 -24
- package/dist/lib/startup-init.d.ts +6 -0
- package/dist/lib/startup-init.js +30 -4
- package/dist/lib/token-lifecycle.js +2 -1
- package/dist/lib/token-log-id.d.ts +18 -0
- package/dist/lib/token-log-id.js +84 -0
- package/dist/lib/two-factor-verify-failure.d.ts +78 -0
- package/dist/lib/two-factor-verify-failure.js +220 -0
- package/dist/middleware/create-middleware.d.ts +4 -1
- package/dist/middleware/create-middleware.js +79 -8
- package/dist/pages/login/page.js +5 -0
- package/dist/pages/recovery/page.js +14 -4
- package/dist/pages/test-env/JwtInspectPage.js +7 -149
- package/dist/pages/test-env/RefreshTokenPage.js +4 -1
- package/dist/pages/test-env/redact-credentials.d.ts +6 -0
- package/dist/pages/test-env/redact-credentials.js +101 -0
- package/dist/pages/verify-code/page.js +6 -3
- package/dist/routes/auth/logout.d.ts +2 -1
- package/dist/routes/auth/logout.js +4 -2
- package/dist/routes/auth/session.d.ts +0 -3
- package/dist/routes/auth/session.js +16 -10
- package/dist/routes/auth/settings.js +4 -1
- package/dist/routes/auth/viability.d.ts +3 -2
- package/dist/routes/auth/viability.js +11 -7
- package/dist/server/auth.d.ts +36 -294
- package/dist/server/auth.js +32 -31
- package/dist/server/decode-session.js +15 -26
- package/dist/server/verified-session-token.d.ts +13 -0
- package/dist/server/verified-session-token.js +103 -0
- package/dist/stores/authStore.js +14 -3
- package/dist/types/recovery.d.ts +1 -1
- package/dist/utils/api.d.ts +18 -0
- package/dist/utils/api.js +51 -9
- 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/dist/vibe/sessions.js +2 -1
- package/package.json +13 -2
- package/scripts/verify-release-provenance.mjs +410 -0
- package/src/api/auth-handler.ts +5 -3
- package/src/api-handlers/account/change-password.ts +36 -8
- package/src/api-handlers/account/masked-info.ts +2 -1
- package/src/api-handlers/account/recovery/forward.ts +117 -0
- package/src/api-handlers/account/recovery/initiate.ts +3 -0
- package/src/api-handlers/account/recovery/send-code.ts +13 -99
- package/src/api-handlers/account/recovery/verify-code.ts +12 -99
- package/src/api-handlers/account/reset-password.ts +30 -1
- package/src/api-handlers/account/send-code.ts +3 -9
- package/src/api-handlers/account/update-phone.ts +6 -1
- package/src/api-handlers/account/validate-password.ts +3 -1
- package/src/api-handlers/account/verify-email.ts +4 -9
- package/src/api-handlers/account/verify-sms.ts +4 -9
- package/src/api-handlers/admin/analytics.ts +13 -5
- package/src/api-handlers/admin/audit.ts +13 -5
- package/src/api-handlers/admin/redis-sessions.ts +16 -8
- package/src/api-handlers/admin/sessions.ts +15 -7
- package/src/api-handlers/admin/site-logs.ts +39 -16
- package/src/api-handlers/admin/stats.ts +15 -5
- package/src/api-handlers/admin/users.ts +15 -7
- package/src/api-handlers/admin/vibe-data.ts +20 -12
- package/src/api-handlers/auth/jwks.ts +2 -1
- package/src/api-handlers/auth/login.ts +13 -4
- package/src/api-handlers/auth/passkey-signin.ts +4 -0
- package/src/api-handlers/auth/refresh.ts +38 -11
- package/src/api-handlers/auth/signout.ts +2 -1
- package/src/api-handlers/auth/status.ts +4 -1
- package/src/api-handlers/auth/update-session.ts +45 -11
- package/src/api-handlers/auth/validate.ts +4 -2
- package/src/api-handlers/auth/verify-code.ts +3 -2
- package/src/api-handlers/mfa/backup-code-verify.ts +4 -9
- package/src/api-handlers/mfa/backup-codes.ts +2 -1
- package/src/api-handlers/mfa/methods.ts +2 -1
- package/src/api-handlers/mfa/totp-confirm.ts +2 -1
- package/src/api-handlers/mfa/totp-enroll.ts +2 -1
- package/src/api-handlers/mfa/totp-verify.ts +4 -9
- package/src/api-handlers/passkeys/list.ts +2 -1
- package/src/api-handlers/passkeys/manage.ts +2 -1
- package/src/api-handlers/passkeys/register-options.ts +2 -1
- package/src/api-handlers/passkeys/register-verify.ts +2 -1
- package/src/api-handlers/session/refresh-viability.ts +10 -7
- package/src/api-handlers/session/viability.ts +4 -3
- package/src/api-handlers/test/force-expire.ts +5 -3
- package/src/auth/auth-decision.ts +2 -1
- package/src/auth/better-auth.ts +128 -61
- package/src/auth/utils/idp-client.ts +2 -1
- package/src/components/admin/DataBrowserTab.tsx +4 -1
- package/src/components/admin/SessionsTab.tsx +10 -3
- package/src/components/recovery/RequestedStep.tsx +30 -0
- package/src/components/recovery/SetPasswordStep.tsx +24 -3
- package/src/config/env.ts +0 -2
- package/src/hooks/useViabilitySession.ts +2 -1
- package/src/index.ts +4 -0
- package/src/lib/app-slug.ts +50 -0
- package/src/lib/ensure-fresh-access-token.ts +133 -4
- package/src/lib/idp-client-config.ts +98 -45
- package/src/lib/idp-fetch.ts +1 -1
- package/src/lib/never-in-response.ts +64 -0
- package/src/lib/password-rules.ts +50 -0
- package/src/lib/safe-error-response.ts +126 -0
- package/src/lib/session-cookie-shape.ts +23 -22
- package/src/lib/session-store.ts +167 -24
- package/src/lib/startup-init.ts +29 -4
- package/src/lib/token-lifecycle.ts +2 -1
- package/src/lib/token-log-id.ts +69 -0
- package/src/lib/two-factor-verify-failure.ts +248 -0
- package/src/middleware/create-middleware.ts +97 -9
- package/src/pages/login/page.tsx +4 -0
- package/src/pages/recovery/page.tsx +21 -4
- package/src/pages/test-env/JwtInspectPage.tsx +11 -233
- package/src/pages/test-env/RefreshTokenPage.tsx +9 -4
- package/src/pages/test-env/redact-credentials.ts +96 -0
- package/src/pages/verify-code/page.tsx +6 -3
- package/src/routes/auth/logout.ts +4 -2
- package/src/routes/auth/session.ts +16 -10
- package/src/routes/auth/settings.ts +5 -1
- package/src/routes/auth/viability.ts +11 -7
- package/src/server/auth.ts +33 -35
- package/src/server/decode-session.ts +16 -26
- package/src/server/verified-session-token.ts +103 -0
- package/src/stores/authStore.ts +16 -5
- package/src/types/recovery.ts +1 -1
- package/src/utils/api.ts +47 -8
- 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
- package/src/vibe/sessions.ts +2 -1
|
@@ -20,6 +20,8 @@ const server_1 = require("next/server");
|
|
|
20
20
|
const auth_1 = require("../../server/auth");
|
|
21
21
|
const session_store_1 = require("../../lib/session-store");
|
|
22
22
|
const idp_client_config_1 = require("../../lib/idp-client-config");
|
|
23
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
24
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
23
25
|
/**
|
|
24
26
|
* Get tenant-wide 2FA requirement from cached client config (from broker handshake)
|
|
25
27
|
*/
|
|
@@ -165,7 +167,7 @@ async function GET(req) {
|
|
|
165
167
|
viable: false,
|
|
166
168
|
needsRefresh: false,
|
|
167
169
|
authenticated: false,
|
|
168
|
-
sessionToken, //
|
|
170
|
+
sessionTokenId: (0, token_log_id_1.tokenLogId)(sessionToken), // PAY-1481: a correlation id, never the token; middleware only needs presence to clear the stale cookie
|
|
169
171
|
reason: 'Stale session - cookie exists but session not found in Redis'
|
|
170
172
|
});
|
|
171
173
|
}
|
|
@@ -289,7 +291,7 @@ async function GET(req) {
|
|
|
289
291
|
viable: true,
|
|
290
292
|
needsRefresh: true,
|
|
291
293
|
authenticated: true,
|
|
292
|
-
sessionToken,
|
|
294
|
+
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null,
|
|
293
295
|
// Clear names for middleware decision-making
|
|
294
296
|
tenantRequiresTwoFactor,
|
|
295
297
|
userHasCompletedTenantTwoFactorRequirements,
|
|
@@ -325,7 +327,7 @@ async function GET(req) {
|
|
|
325
327
|
const mfaVerifiedInSession = session?.mfaVerified ?? false;
|
|
326
328
|
// DEBUG: Log what we're reading from the session
|
|
327
329
|
console.log('[VIABILITY] Session 2FA state:', {
|
|
328
|
-
sessionToken:
|
|
330
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
329
331
|
'session.mfaVerified': session?.mfaVerified,
|
|
330
332
|
'session.twoFactorComplete': session?.twoFactorComplete,
|
|
331
333
|
mfaVerifiedInSession,
|
|
@@ -349,7 +351,7 @@ async function GET(req) {
|
|
|
349
351
|
expiresIn: 0,
|
|
350
352
|
hasRefreshToken,
|
|
351
353
|
authenticated: true,
|
|
352
|
-
sessionToken,
|
|
354
|
+
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null,
|
|
353
355
|
// Clear names
|
|
354
356
|
tenantRequiresTwoFactor,
|
|
355
357
|
userHasCompletedTenantTwoFactorRequirements,
|
|
@@ -372,7 +374,7 @@ async function GET(req) {
|
|
|
372
374
|
expiresIn,
|
|
373
375
|
hasRefreshToken,
|
|
374
376
|
authenticated: true,
|
|
375
|
-
sessionToken,
|
|
377
|
+
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null,
|
|
376
378
|
// Clear names
|
|
377
379
|
tenantRequiresTwoFactor,
|
|
378
380
|
userHasCompletedTenantTwoFactorRequirements,
|
|
@@ -390,13 +392,15 @@ async function GET(req) {
|
|
|
390
392
|
});
|
|
391
393
|
}
|
|
392
394
|
catch (error) {
|
|
393
|
-
|
|
395
|
+
// PAY-1438: pre-auth probe - the exception text stays in the log, keyed by correlationId.
|
|
396
|
+
const correlationId = (0, safe_error_response_1.logExceptionWithCorrelation)('[VIABILITY_ROUTE] Error checking session viability:', error);
|
|
394
397
|
return server_1.NextResponse.json({
|
|
395
398
|
viable: false,
|
|
396
399
|
needsRefresh: false,
|
|
397
400
|
authenticated: false,
|
|
398
401
|
error: 'Failed to check session',
|
|
399
|
-
|
|
402
|
+
code: 'INTERNAL_ERROR',
|
|
403
|
+
correlationId
|
|
400
404
|
}, { status: 500 });
|
|
401
405
|
}
|
|
402
406
|
}
|
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
|
-
const
|
|
53
|
-
const single_flight_1 = require("../lib/single-flight");
|
|
54
|
-
let authInstance = null;
|
|
51
|
+
const verified_session_token_1 = require("./verified-session-token");
|
|
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.
|
|
@@ -184,14 +181,18 @@ async function getSession(request) {
|
|
|
184
181
|
sessionData = buildSessionDataFromAuthSession(betterAuthSession);
|
|
185
182
|
return attachSessionData(betterAuthSession, sessionData, sessionToken);
|
|
186
183
|
}
|
|
187
|
-
// Path B — Better Auth said null/incomplete. Fall back to
|
|
184
|
+
// Path B — Better Auth said null/incomplete. Fall back to a VERIFIED cookie
|
|
188
185
|
// decode + Redis-canonical liveness check. This catches cases where the
|
|
189
186
|
// canonical app session at `{slug}:{token}` is still alive but Better
|
|
190
187
|
// Auth's `ba:{slug}:{token}` secondary record was evicted or the
|
|
191
|
-
// in-memory cookie cache returned stale-null.
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
|
|
188
|
+
// in-memory cookie cache returned stale-null.
|
|
189
|
+
//
|
|
190
|
+
// PAY-1476: this path used to take the token with NO signature check, on the
|
|
191
|
+
// theory that "Redis lookup is the validation". It is not: a leaked token has
|
|
192
|
+
// a live record, so the token string alone authenticated, with any garbage in
|
|
193
|
+
// the signature position, and a forged JWS too. The token now comes only from
|
|
194
|
+
// the shared verified resolver (HMAC or JWS signature checked first).
|
|
195
|
+
const sessionToken = await (0, verified_session_token_1.resolveVerifiedSessionTokenFromRequest)(request);
|
|
195
196
|
if (!sessionToken)
|
|
196
197
|
return null;
|
|
197
198
|
let sessionData = null;
|
|
@@ -42,12 +42,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
42
42
|
exports.decodeSession = decodeSession;
|
|
43
43
|
require("server-only");
|
|
44
44
|
const headers_1 = require("next/headers");
|
|
45
|
-
const jose_1 = require("jose");
|
|
46
45
|
const session_store_1 = require("../lib/session-store");
|
|
47
46
|
const idp_client_config_1 = require("../lib/idp-client-config");
|
|
48
47
|
const app_slug_1 = require("../lib/app-slug");
|
|
49
48
|
const startup_init_1 = require("../lib/startup-init");
|
|
50
49
|
const session_cookie_shape_1 = require("../lib/session-cookie-shape");
|
|
50
|
+
const verified_session_token_1 = require("./verified-session-token");
|
|
51
|
+
const token_log_id_1 = require("../lib/token-log-id");
|
|
51
52
|
/**
|
|
52
53
|
* Try Better Auth's server-side session API.
|
|
53
54
|
* Returns a DecodedSession if Better Auth has an active session, null otherwise.
|
|
@@ -161,7 +162,7 @@ async function tryBetterAuthSession(requestCookies) {
|
|
|
161
162
|
// session. Read the commit message before treating a sign-out here as a
|
|
162
163
|
// bug introduced by this fix.
|
|
163
164
|
if (!idpTokens?.roles?.length) {
|
|
164
|
-
console.warn('[DECODE-SESSION] Leg 1 declining: idpTokens unusable (no roles) for session', result.session.token
|
|
165
|
+
console.warn('[DECODE-SESSION] Leg 1 declining: idpTokens unusable (no roles) for session', (0, token_log_id_1.tokenLogId)(result.session.token), '— falling through to the sess: leg');
|
|
165
166
|
return null;
|
|
166
167
|
}
|
|
167
168
|
// Map Better Auth session + IDP tokens to SessionData
|
|
@@ -261,31 +262,19 @@ async function decodeSession(requestCookies) {
|
|
|
261
262
|
// death, because the ONLY fallback was structurally blind to the only
|
|
262
263
|
// shape it would ever be handed.
|
|
263
264
|
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
265
|
+
// PAY-1476: the shape still decides HOW to verify, but BOTH shapes are now
|
|
266
|
+
// verified, by the shared resolver. This branch used to take a BA-hmac
|
|
267
|
+
// cookie's token with no signature check ("Redis presence is the
|
|
268
|
+
// authority"), so `{leaked token}.{anything}` authenticated here - including
|
|
269
|
+
// behind the portal's admin API gate. The resolver checks the HMAC with
|
|
270
|
+
// timingSafeEqual (ba-hmac) or runs jwtVerify (jws) against the same
|
|
271
|
+
// env-first auth secret, and returns null on any failure.
|
|
269
272
|
const shaped = (0, session_cookie_shape_1.extractSessionTokenFromCookieValue)(cookieValue);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
sessionToken = shaped.token;
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
const secretKey = new TextEncoder().encode(secret);
|
|
277
|
-
try {
|
|
278
|
-
const result = await (0, jose_1.jwtVerify)(cookieValue, secretKey);
|
|
279
|
-
payload = result.payload;
|
|
280
|
-
}
|
|
281
|
-
catch (jwtError) {
|
|
282
|
-
console.warn('[DECODE-SESSION] JWT verification failed:', jwtError instanceof Error ? jwtError.message : String(jwtError));
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
sessionToken = payload.sessionToken || payload.redisSessionId;
|
|
286
|
-
}
|
|
273
|
+
const verified = await (0, verified_session_token_1.resolveVerifiedSessionCookie)(cookieValue);
|
|
274
|
+
const sessionToken = verified?.token;
|
|
275
|
+
const payload = verified?.payload;
|
|
287
276
|
if (!sessionToken) {
|
|
288
|
-
console.warn('[DECODE-SESSION]
|
|
277
|
+
console.warn('[DECODE-SESSION] Session cookie did not verify (shape:', shaped?.shape ?? 'unrecognized', ')');
|
|
289
278
|
return null;
|
|
290
279
|
}
|
|
291
280
|
const sessionData = await (0, session_store_1.getSession)(sessionToken);
|
|
@@ -297,7 +286,7 @@ async function decodeSession(requestCookies) {
|
|
|
297
286
|
// either and the caller is about to be signed out. Different meanings,
|
|
298
287
|
// different remediations — do not ship a second silent path while
|
|
299
288
|
// fixing a silent path.
|
|
300
|
-
console.warn('[DECODE-SESSION] No sess: record for token',
|
|
289
|
+
console.warn('[DECODE-SESSION] No sess: record for token', (0, token_log_id_1.tokenLogId)(sessionToken), '— no fallback available, session will be signed out');
|
|
301
290
|
return null;
|
|
302
291
|
}
|
|
303
292
|
// Backwards compat: session.user.email works alongside session.email
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type JWTPayload } from 'jose';
|
|
2
|
+
export interface VerifiedSessionCookie {
|
|
3
|
+
token: string;
|
|
4
|
+
shape: 'ba-hmac' | 'jws';
|
|
5
|
+
/** The verified JWS payload (jws shape only). */
|
|
6
|
+
payload?: JWTPayload;
|
|
7
|
+
}
|
|
8
|
+
/** The verified session cookie (token, shape, JWS payload), or null unless its signature verifies. */
|
|
9
|
+
export declare function resolveVerifiedSessionCookie(rawValue: string | null | undefined): Promise<VerifiedSessionCookie | null>;
|
|
10
|
+
/** The session token carried by a raw session-cookie value, or null unless its signature verifies. */
|
|
11
|
+
export declare function resolveVerifiedSessionToken(rawValue: string | null | undefined): Promise<string | null>;
|
|
12
|
+
/** Same, reading the session cookie (every name it is minted under, chunks joined) from a Request. */
|
|
13
|
+
export declare function resolveVerifiedSessionTokenFromRequest(request: Request): Promise<string | null>;
|