@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
|
@@ -28,22 +28,4 @@
|
|
|
28
28
|
* @package @payez/next-mvp
|
|
29
29
|
*/
|
|
30
30
|
import { NextRequest, NextResponse } from 'next/server';
|
|
31
|
-
export declare function POST(req: NextRequest): Promise<NextResponse<
|
|
32
|
-
success: boolean;
|
|
33
|
-
error: string;
|
|
34
|
-
code: string;
|
|
35
|
-
}> | NextResponse<{
|
|
36
|
-
success: boolean;
|
|
37
|
-
error: any;
|
|
38
|
-
code: any;
|
|
39
|
-
meta: {
|
|
40
|
-
attemptedRefresh: boolean;
|
|
41
|
-
};
|
|
42
|
-
}> | NextResponse<{
|
|
43
|
-
message: any;
|
|
44
|
-
requiresChallenge?: boolean | undefined;
|
|
45
|
-
nextStep?: "mfa_verify" | undefined;
|
|
46
|
-
success: boolean;
|
|
47
|
-
verificationSuccessful: boolean;
|
|
48
|
-
twoFactorSessionVerified: boolean;
|
|
49
|
-
}>>;
|
|
31
|
+
export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;
|
|
@@ -36,6 +36,7 @@ const env_1 = require("../../config/env");
|
|
|
36
36
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
37
37
|
const session_store_1 = require("../../lib/session-store");
|
|
38
38
|
const jwt_decode_1 = require("../../lib/jwt-decode");
|
|
39
|
+
const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
|
|
39
40
|
async function POST(req) {
|
|
40
41
|
try {
|
|
41
42
|
const body = await req.json();
|
|
@@ -55,12 +56,9 @@ async function POST(req) {
|
|
|
55
56
|
body: JSON.stringify({ code }),
|
|
56
57
|
});
|
|
57
58
|
if (!result.ok) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
code: result.json?.code || 'IDP_ERROR',
|
|
62
|
-
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
63
|
-
}, { status: result.status });
|
|
59
|
+
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
60
|
+
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
61
|
+
return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'totp');
|
|
64
62
|
}
|
|
65
63
|
// Unwrap IDP envelope { success, data }
|
|
66
64
|
const unwrappedData = result.json?.data || result.json;
|
|
@@ -4,6 +4,7 @@ exports.GET = GET;
|
|
|
4
4
|
const server_1 = require("next/server");
|
|
5
5
|
const idp_fetch_1 = require("../../lib/idp-fetch");
|
|
6
6
|
const env_1 = require("../../config/env");
|
|
7
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
7
8
|
/**
|
|
8
9
|
* List the signed-in user's passkeys (GET /api/Passkeys, user JWT).
|
|
9
10
|
* Probe-verified live 2026-08-01 (anon -> 401 = routed).
|
|
@@ -15,7 +16,7 @@ async function GET(req) {
|
|
|
15
16
|
return server_1.NextResponse.json({
|
|
16
17
|
success: false,
|
|
17
18
|
message: 'Upstream error',
|
|
18
|
-
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
|
|
19
|
+
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: (0, safe_error_response_1.projectIdpError)(result.json) },
|
|
19
20
|
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
20
21
|
}, { status: result.status });
|
|
21
22
|
}
|
|
@@ -5,6 +5,7 @@ exports.DELETE = DELETE;
|
|
|
5
5
|
const server_1 = require("next/server");
|
|
6
6
|
const idp_fetch_1 = require("../../lib/idp-fetch");
|
|
7
7
|
const env_1 = require("../../config/env");
|
|
8
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
8
9
|
/**
|
|
9
10
|
* Manage ONE passkey (PATCH rename / DELETE revoke, /api/Passkeys/{id},
|
|
10
11
|
* user JWT). The id arrives from the Next dynamic segment via the routes
|
|
@@ -14,7 +15,7 @@ function upstreamError(result) {
|
|
|
14
15
|
return server_1.NextResponse.json({
|
|
15
16
|
success: false,
|
|
16
17
|
message: 'Upstream error',
|
|
17
|
-
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
|
|
18
|
+
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: (0, safe_error_response_1.projectIdpError)(result.json) },
|
|
18
19
|
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
19
20
|
}, { status: result.status });
|
|
20
21
|
}
|
|
@@ -4,6 +4,7 @@ exports.POST = POST;
|
|
|
4
4
|
const server_1 = require("next/server");
|
|
5
5
|
const idp_fetch_1 = require("../../lib/idp-fetch");
|
|
6
6
|
const env_1 = require("../../config/env");
|
|
7
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
7
8
|
/**
|
|
8
9
|
* Begin the passkey REGISTRATION ceremony (POST /api/Passkeys/register/options,
|
|
9
10
|
* user JWT — a credential change; the IDP may demand step-up, surfaced as-is).
|
|
@@ -27,7 +28,7 @@ async function POST(req) {
|
|
|
27
28
|
return server_1.NextResponse.json({
|
|
28
29
|
success: false,
|
|
29
30
|
message: 'Upstream error',
|
|
30
|
-
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
|
|
31
|
+
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: (0, safe_error_response_1.projectIdpError)(result.json) },
|
|
31
32
|
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
32
33
|
}, { status: result.status });
|
|
33
34
|
}
|
|
@@ -4,6 +4,7 @@ exports.POST = POST;
|
|
|
4
4
|
const server_1 = require("next/server");
|
|
5
5
|
const idp_fetch_1 = require("../../lib/idp-fetch");
|
|
6
6
|
const env_1 = require("../../config/env");
|
|
7
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
7
8
|
/**
|
|
8
9
|
* Finish the passkey REGISTRATION ceremony (POST /api/Passkeys/register/verify,
|
|
9
10
|
* user JWT). Body forwarded VERBATIM — it carries the enrolment handle plus the
|
|
@@ -26,7 +27,7 @@ async function POST(req) {
|
|
|
26
27
|
return server_1.NextResponse.json({
|
|
27
28
|
success: false,
|
|
28
29
|
message: 'Upstream error',
|
|
29
|
-
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
|
|
30
|
+
error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: (0, safe_error_response_1.projectIdpError)(result.json) },
|
|
30
31
|
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
31
32
|
}, { status: result.status });
|
|
32
33
|
}
|
|
@@ -2,13 +2,15 @@ import { NextRequest, NextResponse } from 'next/server';
|
|
|
2
2
|
export declare function GET(req: NextRequest): Promise<NextResponse<{
|
|
3
3
|
canRefresh: boolean;
|
|
4
4
|
reason: string;
|
|
5
|
-
|
|
5
|
+
sessionTokenId: null;
|
|
6
6
|
}> | NextResponse<{
|
|
7
7
|
canRefresh: boolean;
|
|
8
8
|
reason: string;
|
|
9
|
-
|
|
9
|
+
sessionTokenId: string;
|
|
10
10
|
}> | NextResponse<{
|
|
11
11
|
canRefresh: boolean;
|
|
12
12
|
reason: string;
|
|
13
13
|
error: string;
|
|
14
|
+
code: string;
|
|
15
|
+
correlationId: string;
|
|
14
16
|
}>>;
|
|
@@ -6,6 +6,7 @@ const session_store_1 = require("../../lib/session-store");
|
|
|
6
6
|
const refresh_token_validator_1 = require("../../lib/refresh-token-validator");
|
|
7
7
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
8
8
|
const logger_1 = require("../../config/logger");
|
|
9
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
9
10
|
async function GET(req) {
|
|
10
11
|
try {
|
|
11
12
|
const sessionToken = req.nextUrl.searchParams.get('token');
|
|
@@ -16,24 +17,26 @@ async function GET(req) {
|
|
|
16
17
|
finalSessionToken = (token?.sessionToken || token?.redisSessionId);
|
|
17
18
|
}
|
|
18
19
|
if (!finalSessionToken) {
|
|
19
|
-
return server_1.NextResponse.json({ canRefresh: false, reason: 'not_logged_in',
|
|
20
|
+
return server_1.NextResponse.json({ canRefresh: false, reason: 'not_logged_in', sessionTokenId: null }, { status: 200 });
|
|
20
21
|
}
|
|
21
22
|
const refreshInProgress = await (0, session_store_1.isRefreshInProgress)(finalSessionToken);
|
|
22
23
|
if (refreshInProgress) {
|
|
23
24
|
// Still need to get session data for twoFactorComplete even when refresh is in progress
|
|
24
25
|
const sessionData = await (0, session_store_1.getSession)(finalSessionToken);
|
|
25
|
-
logger_1.logger.info('[REFRESH-VIABILITY] Refresh already in progress, telling middleware to wait', { sessionToken:
|
|
26
|
-
return server_1.NextResponse.json({ canRefresh: true, reason: 'refresh_in_progress', refreshInProgress: true,
|
|
26
|
+
logger_1.logger.info('[REFRESH-VIABILITY] Refresh already in progress, telling middleware to wait', { sessionToken: (0, token_log_id_1.tokenLogId)(finalSessionToken) });
|
|
27
|
+
return server_1.NextResponse.json({ canRefresh: true, reason: 'refresh_in_progress', refreshInProgress: true, sessionTokenId: (0, token_log_id_1.tokenLogId)(finalSessionToken), twoFactorComplete: sessionData?.mfaVerified ?? false }, { status: 200 });
|
|
27
28
|
}
|
|
28
29
|
const sessionData = await (0, session_store_1.getSession)(finalSessionToken);
|
|
29
30
|
if (!sessionData) {
|
|
30
|
-
return server_1.NextResponse.json({ canRefresh: false, reason: 'session_not_found',
|
|
31
|
+
return server_1.NextResponse.json({ canRefresh: false, reason: 'session_not_found', sessionTokenId: (0, token_log_id_1.tokenLogId)(finalSessionToken) }, { status: 200 });
|
|
31
32
|
}
|
|
32
33
|
const viabilityCheck = (0, refresh_token_validator_1.checkRefreshViability)(sessionData);
|
|
33
|
-
return server_1.NextResponse.json({ canRefresh: viabilityCheck.canRefresh, reason: viabilityCheck.reason, timeRemaining: viabilityCheck.timeRemaining, expiresAt: viabilityCheck.expiresAt, accessTokenExpired: viabilityCheck.accessTokenExpired, accessTokenTimeRemaining: viabilityCheck.accessTokenTimeRemaining,
|
|
34
|
+
return server_1.NextResponse.json({ canRefresh: viabilityCheck.canRefresh, reason: viabilityCheck.reason, timeRemaining: viabilityCheck.timeRemaining, expiresAt: viabilityCheck.expiresAt, accessTokenExpired: viabilityCheck.accessTokenExpired, accessTokenTimeRemaining: viabilityCheck.accessTokenTimeRemaining, sessionTokenId: (0, token_log_id_1.tokenLogId)(finalSessionToken), twoFactorComplete: sessionData.mfaVerified ?? false, userId: sessionData.userId, refreshInProgress: false });
|
|
34
35
|
}
|
|
35
36
|
catch (error) {
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const correlationId = globalThis.crypto.randomUUID();
|
|
38
|
+
logger_1.logger.error('[REFRESH-VIABILITY] Error checking refresh viability', { correlationId, error: error instanceof Error ? error.message : String(error) });
|
|
39
|
+
// PAY-1438: the exception text stays in the log above; the caller gets the id.
|
|
40
|
+
return server_1.NextResponse.json({ canRefresh: false, reason: 'check_error', error: 'Failed to check refresh viability', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -11,6 +11,7 @@ const session_store_1 = require("../../lib/session-store");
|
|
|
11
11
|
const auth_1 = require("../../server/auth");
|
|
12
12
|
const startup_init_1 = require("../../lib/startup-init");
|
|
13
13
|
const idp_client_config_1 = require("../../lib/idp-client-config");
|
|
14
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
14
15
|
async function GET(req) {
|
|
15
16
|
try {
|
|
16
17
|
// Ensure initialization is complete (auth signing secret resolved from IDP)
|
|
@@ -109,7 +110,7 @@ async function GET(req) {
|
|
|
109
110
|
const sessionMfaComplete = sessionData.mfaVerified ?? false;
|
|
110
111
|
const effectiveTwoFactorComplete = sessionMfaComplete && !mfaExpired;
|
|
111
112
|
console.log('[VIABILITY] Session 2FA check:', {
|
|
112
|
-
sessionToken:
|
|
113
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
113
114
|
mfaVerified: sessionData.mfaVerified,
|
|
114
115
|
twoFactorComplete: sessionData.twoFactorComplete,
|
|
115
116
|
sessionMfaComplete,
|
|
@@ -125,7 +126,7 @@ async function GET(req) {
|
|
|
125
126
|
}
|
|
126
127
|
const response = {
|
|
127
128
|
authenticated: true,
|
|
128
|
-
sessionToken, //
|
|
129
|
+
sessionTokenId: (0, token_log_id_1.tokenLogId)(sessionToken), // PAY-1481: correlation id for middleware tracking, never the token
|
|
129
130
|
// 2FA fields - critical for middleware redirect logic
|
|
130
131
|
requires2FA, // From cached client config (client-wide setting)
|
|
131
132
|
twoFactorComplete: effectiveTwoFactorComplete, // From session, BUT respects MFA TTL
|
|
@@ -159,7 +160,7 @@ async function GET(req) {
|
|
|
159
160
|
console.warn('[VIABILITY] Stale cookie detected - session not in Redis');
|
|
160
161
|
return server_1.NextResponse.json({
|
|
161
162
|
authenticated: false,
|
|
162
|
-
sessionToken //
|
|
163
|
+
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null // PAY-1481: presence + correlation for stale cookie detection
|
|
163
164
|
});
|
|
164
165
|
}
|
|
165
166
|
// If there's no token at all, it's not authenticated
|
|
@@ -4,6 +4,8 @@ exports.POST = void 0;
|
|
|
4
4
|
const server_1 = require("next/server");
|
|
5
5
|
const auth_1 = require("../../server/auth");
|
|
6
6
|
const session_store_1 = require("../../lib/session-store");
|
|
7
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
8
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
7
9
|
/**
|
|
8
10
|
* ⚠️ DESTRUCTIVE TEST-ONLY HANDLER — DO NOT MOUNT IN PRODUCTION. ⚠️
|
|
9
11
|
*
|
|
@@ -134,7 +136,7 @@ const POST = async (req) => {
|
|
|
134
136
|
const prev = session.idpAccessTokenExpires || null;
|
|
135
137
|
await (0, session_store_1.updateSession)(sessionToken, { idpAccessTokenExpires: forced });
|
|
136
138
|
console.log('[TEST_EXPIRE] Forced access token expiry for session', {
|
|
137
|
-
sessionToken:
|
|
139
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
138
140
|
previous: prev ? new Date(prev).toISOString() : null,
|
|
139
141
|
newExpiry: new Date(forced).toISOString()
|
|
140
142
|
});
|
|
@@ -147,8 +149,8 @@ const POST = async (req) => {
|
|
|
147
149
|
});
|
|
148
150
|
}
|
|
149
151
|
catch (e) {
|
|
150
|
-
|
|
151
|
-
return server_1.NextResponse.json({ success: false, error:
|
|
152
|
+
const correlationId = (0, safe_error_response_1.logExceptionWithCorrelation)('[TEST_EXPIRE] Error:', e);
|
|
153
|
+
return server_1.NextResponse.json({ success: false, error: 'Failed to force expiry', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
152
154
|
}
|
|
153
155
|
};
|
|
154
156
|
exports.POST = POST;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.makeAuthDecision = makeAuthDecision;
|
|
10
10
|
const safe_callback_1 = require("./safe-callback");
|
|
11
|
+
const token_log_id_1 = require("../lib/token-log-id");
|
|
11
12
|
/**
|
|
12
13
|
* Main decision function - pure, testable, deterministic
|
|
13
14
|
*/
|
|
@@ -99,7 +100,7 @@ function makeAuthDecision(context) {
|
|
|
99
100
|
pathname,
|
|
100
101
|
isLoginPage,
|
|
101
102
|
sessionExists: sessionStatus.exists,
|
|
102
|
-
sessionToken:
|
|
103
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionPointer.sessionToken)
|
|
103
104
|
});
|
|
104
105
|
// Already on the 2FA page
|
|
105
106
|
if (pathname === '/account-auth/verify-code') {
|
|
@@ -182,6 +182,9 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
182
182
|
refreshCache: false;
|
|
183
183
|
};
|
|
184
184
|
};
|
|
185
|
+
account: {
|
|
186
|
+
storeStateStrategy: "cookie";
|
|
187
|
+
};
|
|
185
188
|
advanced: {
|
|
186
189
|
cookiePrefix: string;
|
|
187
190
|
cookies: {
|
|
@@ -192,22 +195,25 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
192
195
|
};
|
|
193
196
|
plugins: [{
|
|
194
197
|
id: "next-cookies";
|
|
198
|
+
version: string;
|
|
195
199
|
hooks: {
|
|
196
200
|
before: {
|
|
197
201
|
matcher(ctx: import("better-auth").HookEndpointContext): boolean;
|
|
198
|
-
handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void
|
|
202
|
+
handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
|
|
199
203
|
}[];
|
|
200
204
|
after: {
|
|
201
205
|
matcher(ctx: import("better-auth").HookEndpointContext): true;
|
|
202
|
-
handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void
|
|
206
|
+
handler: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>;
|
|
203
207
|
}[];
|
|
204
208
|
};
|
|
205
|
-
}, ...(
|
|
209
|
+
}, ...({
|
|
206
210
|
id: "magic-link";
|
|
211
|
+
version: string;
|
|
207
212
|
endpoints: {
|
|
208
213
|
signInMagicLink: import("better-auth").StrictEndpoint<"/sign-in/magic-link", {
|
|
209
214
|
method: "POST";
|
|
210
215
|
requireHeaders: true;
|
|
216
|
+
use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>[];
|
|
211
217
|
body: import("better-auth").ZodObject<{
|
|
212
218
|
email: import("better-auth").ZodEmail;
|
|
213
219
|
name: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
@@ -215,7 +221,7 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
215
221
|
newUserCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
216
222
|
errorCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
217
223
|
metadata: import("better-auth").ZodOptional<import("better-auth").ZodRecord<import("better-auth").ZodString, import("better-auth").ZodAny>>;
|
|
218
|
-
}, import("
|
|
224
|
+
}, import("zod/v4/core").$strip>;
|
|
219
225
|
metadata: {
|
|
220
226
|
openapi: {
|
|
221
227
|
operationId: string;
|
|
@@ -249,8 +255,8 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
249
255
|
callbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
250
256
|
errorCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
251
257
|
newUserCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
252
|
-
}, import("
|
|
253
|
-
use: ((inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void
|
|
258
|
+
}, import("zod/v4/core").$strip>;
|
|
259
|
+
use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>>[];
|
|
254
260
|
requireHeaders: true;
|
|
255
261
|
metadata: {
|
|
256
262
|
openapi: {
|
|
@@ -289,6 +295,16 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
289
295
|
name: string;
|
|
290
296
|
image?: string | null | undefined;
|
|
291
297
|
};
|
|
298
|
+
session: {
|
|
299
|
+
id: string;
|
|
300
|
+
createdAt: Date;
|
|
301
|
+
updatedAt: Date;
|
|
302
|
+
userId: string;
|
|
303
|
+
expiresAt: Date;
|
|
304
|
+
token: string;
|
|
305
|
+
ipAddress?: string | null | undefined;
|
|
306
|
+
userAgent?: string | null | undefined;
|
|
307
|
+
};
|
|
292
308
|
}>;
|
|
293
309
|
};
|
|
294
310
|
rateLimit: {
|
|
@@ -297,8 +313,9 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
297
313
|
max: number;
|
|
298
314
|
}[];
|
|
299
315
|
options: MagicLinkOptions;
|
|
300
|
-
} | {
|
|
316
|
+
} | import("better-auth").BetterAuthPlugin | {
|
|
301
317
|
id: "generic-oauth";
|
|
318
|
+
version: string;
|
|
302
319
|
init: (ctx: import("better-auth").AuthContext) => {
|
|
303
320
|
context: {
|
|
304
321
|
socialProviders: import("better-auth").OAuthProvider<Record<string, any>, Partial<import("better-auth").ProviderOptions<any>>>[];
|
|
@@ -316,7 +333,7 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
316
333
|
scopes: import("better-auth").ZodOptional<import("better-auth").ZodArray<import("better-auth").ZodString>>;
|
|
317
334
|
requestSignUp: import("better-auth").ZodOptional<import("better-auth").ZodBoolean>;
|
|
318
335
|
additionalData: import("better-auth").ZodOptional<import("better-auth").ZodRecord<import("better-auth").ZodString, import("better-auth").ZodAny>>;
|
|
319
|
-
}, import("
|
|
336
|
+
}, import("zod/v4/core").$strip>;
|
|
320
337
|
metadata: {
|
|
321
338
|
openapi: {
|
|
322
339
|
description: string;
|
|
@@ -354,7 +371,7 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
354
371
|
error_description: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
355
372
|
state: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
356
373
|
iss: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
357
|
-
}, import("
|
|
374
|
+
}, import("zod/v4/core").$strip>;
|
|
358
375
|
metadata: {
|
|
359
376
|
allowedMediaTypes: string[];
|
|
360
377
|
openapi: {
|
|
@@ -379,7 +396,7 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
379
396
|
};
|
|
380
397
|
scope: "server";
|
|
381
398
|
};
|
|
382
|
-
},
|
|
399
|
+
}, never>;
|
|
383
400
|
oAuth2LinkAccount: import("better-auth").StrictEndpoint<"/oauth2/link", {
|
|
384
401
|
method: "POST";
|
|
385
402
|
body: import("better-auth").ZodObject<{
|
|
@@ -387,8 +404,8 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
387
404
|
callbackURL: import("better-auth").ZodString;
|
|
388
405
|
scopes: import("better-auth").ZodOptional<import("better-auth").ZodArray<import("better-auth").ZodString>>;
|
|
389
406
|
errorCallbackURL: import("better-auth").ZodOptional<import("better-auth").ZodString>;
|
|
390
|
-
}, import("
|
|
391
|
-
use: ((inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
|
|
407
|
+
}, import("zod/v4/core").$strip>;
|
|
408
|
+
use: import("better-auth").Middleware<import("better-auth").MiddlewareOptions, (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<{
|
|
392
409
|
session: {
|
|
393
410
|
session: Record<string, any> & {
|
|
394
411
|
id: string;
|
|
@@ -410,7 +427,7 @@ export declare function createBetterAuthInstance(idpConfig: IDPClientConfig, opt
|
|
|
410
427
|
image?: string | null | undefined;
|
|
411
428
|
};
|
|
412
429
|
};
|
|
413
|
-
}
|
|
430
|
+
}>>[];
|
|
414
431
|
metadata: {
|
|
415
432
|
openapi: {
|
|
416
433
|
description: string;
|
|
@@ -476,6 +493,20 @@ export declare function __getBetterAuthInstance(): unknown;
|
|
|
476
493
|
* cannot be applied retroactively.
|
|
477
494
|
*/
|
|
478
495
|
export declare function configureBetterAuth(opts: CreateBetterAuthInstanceOptions): void;
|
|
496
|
+
/**
|
|
497
|
+
* PAY-642 (card 338215): the init attempt no longer LATCHES A FAILURE.
|
|
498
|
+
*
|
|
499
|
+
* This used to be `state.initPromise ??= getIDPClientConfig(true).then(...)` with no .catch and no reset. One
|
|
500
|
+
* auth request against a cold process while the IdP was unreachable stored a rejected promise, and every later
|
|
501
|
+
* caller replayed it until the process restarted, even after the IdP recovered. This funnel backs every
|
|
502
|
+
* /api/auth/* route, the login GET handler and withAuth. server/auth.ts getAuthInstance() already used
|
|
503
|
+
* createSingleFlight for this exact bug; this sibling funnel was missed.
|
|
504
|
+
*
|
|
505
|
+
* Now the attempt runs through the same helper: concurrent callers share one attempt, a failure still rejects
|
|
506
|
+
* for every caller of THAT attempt (loud), the next call starts a fresh attempt, and a success is cached in
|
|
507
|
+
* state.cachedInstance as before. The flight lives on the globalThis state for the same reason the instance
|
|
508
|
+
* does (duplicated route chunks must share one init).
|
|
509
|
+
*/
|
|
479
510
|
export declare function getBetterAuthInstance(): Promise<any>;
|
|
480
511
|
/**
|
|
481
512
|
* Get flag-gated auth handler for Next.js route.
|
|
@@ -551,12 +582,25 @@ export declare function exchangeOAuthForIdpTokens(sessionToken: string, provider
|
|
|
551
582
|
* state what actually authenticated ('passkey', 'google').
|
|
552
583
|
*/
|
|
553
584
|
export declare function storeIdpTokensInSession(sessionToken: string, idpTokenData: Record<string, unknown>, authProvider: string): Promise<boolean>;
|
|
585
|
+
/**
|
|
586
|
+
* The `?error=` code the login page receives when an OAuth sign-in is ABORTED because the IdP exchange did not
|
|
587
|
+
* store tokens (PAY-1733). Hosts map it to ONE neutral line; the card's words are "That sign-in didn't work.
|
|
588
|
+
* Try again or use another method." The cause stays in the server log, never on the page.
|
|
589
|
+
*/
|
|
590
|
+
export declare const OAUTH_SIGN_IN_FAILED_ERROR = "OAUTH_SIGN_IN_FAILED";
|
|
591
|
+
/**
|
|
592
|
+
* Re-emit each cookie the callback response SET as an immediate expiry, keeping the attributes that decide
|
|
593
|
+
* WHICH cookie the browser overwrites (Path, Domain, Secure, SameSite, HttpOnly - a `__Secure-` name is only
|
|
594
|
+
* accepted with Secure) and dropping Expires/Max-Age in favour of Max-Age=0.
|
|
595
|
+
*/
|
|
596
|
+
export declare function expireSetCookies(setCookies: readonly string[]): string[];
|
|
554
597
|
/**
|
|
555
598
|
* Create a production-ready GET handler for the auth catch-all route.
|
|
556
599
|
*
|
|
557
600
|
* Wraps better-auth's GET handler with:
|
|
558
601
|
* - OAuth state error recovery (redirects to login instead of error page)
|
|
559
|
-
* - IDP token exchange after successful OAuth callback
|
|
602
|
+
* - IDP token exchange after successful OAuth callback - and, when it stores no tokens, the sign-in is ABORTED
|
|
603
|
+
* (session destroyed, cookies expired, login page with OAUTH_SIGN_IN_FAILED; PAY-1733)
|
|
560
604
|
*
|
|
561
605
|
* Usage in host app:
|
|
562
606
|
* ```ts
|