@payez/next-mvp 4.4.4 → 4.4.6
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 +6 -3
- 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 +14 -0
- package/dist/auth/better-auth.js +33 -13
- package/dist/auth/utils/idp-client.js +2 -1
- 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/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 +65 -14
- package/dist/lib/idp-fetch.js +1 -1
- 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 +45 -0
- package/dist/lib/two-factor-verify-failure.js +143 -0
- package/dist/middleware/create-middleware.d.ts +2 -1
- package/dist/middleware/create-middleware.js +43 -8
- 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 +2 -1
- 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/viability.d.ts +3 -2
- package/dist/routes/auth/viability.js +11 -7
- package/dist/server/auth.js +10 -6
- 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/sessions.js +2 -1
- package/package.json +2 -1
- package/scripts/verify-release-provenance.mjs +410 -0
- package/src/api/auth-handler.ts +5 -3
- package/src/api-handlers/account/change-password.ts +6 -3
- 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 +35 -10
- 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/hooks/useViabilitySession.ts +2 -1
- 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 +90 -14
- package/src/lib/idp-fetch.ts +1 -1
- 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 +167 -0
- package/src/middleware/create-middleware.ts +55 -9
- 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 +2 -1
- package/src/routes/auth/logout.ts +4 -2
- package/src/routes/auth/session.ts +16 -10
- package/src/routes/auth/viability.ts +11 -7
- package/src/server/auth.ts +10 -6
- 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/sessions.ts +2 -1
|
@@ -54,6 +54,7 @@ const auth_1 = require("../../server/auth");
|
|
|
54
54
|
const startup_init_1 = require("../../lib/startup-init");
|
|
55
55
|
const client_key_1 = require("../../lib/client-key");
|
|
56
56
|
const roles_1 = require("../../lib/roles");
|
|
57
|
+
const crypto_1 = require("crypto");
|
|
57
58
|
/**
|
|
58
59
|
* Check if the current user has admin role
|
|
59
60
|
*/
|
|
@@ -79,6 +80,10 @@ async function checkAdminRole(request) {
|
|
|
79
80
|
* Make a service account request to Vibe (admin mode - no user filtering)
|
|
80
81
|
*/
|
|
81
82
|
async function vibeServiceRequest(endpoint, options) {
|
|
83
|
+
// PAY-1425: `error` is returned to the HTTP caller verbatim by every handler below, so it
|
|
84
|
+
// must NEVER carry upstream text (raw Vibe/VibeSQL error bodies can hold SQL fragments,
|
|
85
|
+
// schema names or connection detail) or exception text. Failures return a fixed message
|
|
86
|
+
// plus a correlationId; the same id is on the server log line for operators.
|
|
82
87
|
const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
|
|
83
88
|
const clientId = process.env.VIBE_CLIENT_ID;
|
|
84
89
|
const signingKey = process.env.VIBE_HMAC_KEY;
|
|
@@ -121,15 +126,18 @@ async function vibeServiceRequest(endpoint, options) {
|
|
|
121
126
|
}
|
|
122
127
|
if (!res.ok) {
|
|
123
128
|
const errorText = await res.text();
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
// PAY-1424: never the raw upstream body in the log sink.
|
|
130
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
131
|
+
console.error(`[Admin Vibe] Request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
|
|
132
|
+
return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
|
|
126
133
|
}
|
|
127
134
|
const body = await res.json();
|
|
128
135
|
return { ok: true, status: res.status, data: body };
|
|
129
136
|
}
|
|
130
137
|
catch (error) {
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
const correlationId = (0, crypto_1.randomUUID)();
|
|
139
|
+
console.error(`[Admin Vibe] Request exception: ${options.method} ${endpoint}`, { correlationId, error });
|
|
140
|
+
return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
143
|
/**
|
|
@@ -143,7 +151,7 @@ function createGetCollectionsHandler(config) {
|
|
|
143
151
|
return adminCheck.error;
|
|
144
152
|
const result = await vibeServiceRequest('/v1/collections', { method: 'GET' });
|
|
145
153
|
if (!result.ok) {
|
|
146
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
154
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
147
155
|
}
|
|
148
156
|
return server_1.NextResponse.json(result.data);
|
|
149
157
|
};
|
|
@@ -160,7 +168,7 @@ function createGetTablesHandler(config) {
|
|
|
160
168
|
return adminCheck.error;
|
|
161
169
|
const result = await vibeServiceRequest(`/v1/collections/${collection}/tables`, { method: 'GET' });
|
|
162
170
|
if (!result.ok) {
|
|
163
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
171
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
164
172
|
}
|
|
165
173
|
return server_1.NextResponse.json(result.data);
|
|
166
174
|
};
|
|
@@ -183,7 +191,7 @@ function createGetTableDataHandler(config) {
|
|
|
183
191
|
return server_1.NextResponse.json({ data: [], meta: { total: 0, limit: 50, offset: 0 } });
|
|
184
192
|
}
|
|
185
193
|
if (!result.ok) {
|
|
186
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
194
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'FETCH_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
187
195
|
}
|
|
188
196
|
return server_1.NextResponse.json(result.data);
|
|
189
197
|
};
|
|
@@ -202,7 +210,7 @@ function createGetRecordHandler(config) {
|
|
|
202
210
|
const result = await vibeServiceRequest(endpoint, { method: 'GET' });
|
|
203
211
|
if (!result.ok) {
|
|
204
212
|
const status = result.status === 404 ? 404 : result.status || 500;
|
|
205
|
-
return server_1.NextResponse.json({ success: false, error: { code: result.status === 404 ? 'NOT_FOUND' : 'FETCH_ERROR', message: result.error } }, { status });
|
|
213
|
+
return server_1.NextResponse.json({ success: false, error: { code: result.status === 404 ? 'NOT_FOUND' : 'FETCH_ERROR', message: result.error, correlationId: result.correlationId } }, { status });
|
|
206
214
|
}
|
|
207
215
|
return server_1.NextResponse.json(result.data);
|
|
208
216
|
};
|
|
@@ -221,7 +229,7 @@ function createUpdateRecordHandler(config) {
|
|
|
221
229
|
const endpoint = `/v1/collections/${collection}/tables/${table}/${id}`;
|
|
222
230
|
const result = await vibeServiceRequest(endpoint, { method: 'PUT', body });
|
|
223
231
|
if (!result.ok) {
|
|
224
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'UPDATE_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
232
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'UPDATE_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
225
233
|
}
|
|
226
234
|
return server_1.NextResponse.json(result.data);
|
|
227
235
|
};
|
|
@@ -239,7 +247,7 @@ function createDeleteRecordHandler(config) {
|
|
|
239
247
|
const endpoint = `/v1/collections/${collection}/tables/${table}/${id}`;
|
|
240
248
|
const result = await vibeServiceRequest(endpoint, { method: 'DELETE' });
|
|
241
249
|
if (!result.ok) {
|
|
242
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'DELETE_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
250
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'DELETE_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
243
251
|
}
|
|
244
252
|
return new server_1.NextResponse(null, { status: 204 });
|
|
245
253
|
};
|
|
@@ -261,7 +269,7 @@ function createQueryHandler(config) {
|
|
|
261
269
|
return server_1.NextResponse.json({ data: [], meta: { total: 0, page: 1, pageSize: 20 } });
|
|
262
270
|
}
|
|
263
271
|
if (!result.ok) {
|
|
264
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'QUERY_ERROR', message: result.error } }, { status: result.status || 500 });
|
|
272
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'QUERY_ERROR', message: result.error, correlationId: result.correlationId } }, { status: result.status || 500 });
|
|
265
273
|
}
|
|
266
274
|
return server_1.NextResponse.json(result.data);
|
|
267
275
|
};
|
|
@@ -17,7 +17,8 @@ async function GET(_req) {
|
|
|
17
17
|
return server_1.NextResponse.json(json, { status: res.status });
|
|
18
18
|
}
|
|
19
19
|
catch (e) {
|
|
20
|
-
|
|
20
|
+
// PAY-1427: no body slice - a non-JSON upstream is an error/proxy page of unknown content.
|
|
21
|
+
logger_1.logger.error('[AUTH_JWKS] Upstream returned non-JSON', { status: res.status, bodyLength: bodyText.length, contentType: res.headers.get('content-type') });
|
|
21
22
|
return server_1.NextResponse.json({ error: 'Invalid JWKS response' }, { status: 502 });
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -118,9 +118,12 @@ function createLoginHandler(config) {
|
|
|
118
118
|
data = JSON.parse(rawText);
|
|
119
119
|
}
|
|
120
120
|
catch (jsonError) {
|
|
121
|
+
// PAY-1424: never the raw body - a non-JSON IDP/proxy error page can echo request
|
|
122
|
+
// context (credentials) back. Status, length and content-type are enough to triage.
|
|
121
123
|
console.error('[LOGIN] IDP returned non-JSON response', {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
status: backendResponse.status,
|
|
125
|
+
bodyLength: rawText.length,
|
|
126
|
+
contentType: backendResponse.headers.get('content-type'),
|
|
124
127
|
});
|
|
125
128
|
return server_1.NextResponse.json({ error: 'Invalid response from authentication service' }, { status: 502 });
|
|
126
129
|
}
|
|
@@ -137,15 +140,21 @@ function createLoginHandler(config) {
|
|
|
137
140
|
}, { status: 429 }));
|
|
138
141
|
}
|
|
139
142
|
// Pass through error from IDP
|
|
143
|
+
// PAY-1424: shape only, never the envelope contents.
|
|
140
144
|
console.error('[LOGIN] IDP service error', {
|
|
141
145
|
status: backendResponse.status,
|
|
142
|
-
|
|
146
|
+
bodyKeys: data && typeof data === 'object' ? Object.keys(data) : undefined,
|
|
147
|
+
errorKeys: data?.error && typeof data?.error === 'object' ? Object.keys(data?.error) : undefined,
|
|
143
148
|
});
|
|
144
149
|
return addSecurityHeaders(server_1.NextResponse.json(data, { status: backendResponse.status }));
|
|
145
150
|
}
|
|
146
151
|
if (!data.success) {
|
|
147
152
|
// IDP returned a canonical error envelope with HTTP 200
|
|
148
|
-
|
|
153
|
+
// PAY-1424: shape only, never the envelope contents.
|
|
154
|
+
console.error('[LOGIN] IDP authentication failure', {
|
|
155
|
+
bodyKeys: data && typeof data === 'object' ? Object.keys(data) : undefined,
|
|
156
|
+
errorKeys: data?.error && typeof data?.error === 'object' ? Object.keys(data?.error) : undefined,
|
|
157
|
+
});
|
|
149
158
|
return addSecurityHeaders(server_1.NextResponse.json(data, { status: 200 }));
|
|
150
159
|
}
|
|
151
160
|
// Normalize response shape for NextAuth authorize()
|
|
@@ -7,6 +7,7 @@ const env_1 = require("../../config/env");
|
|
|
7
7
|
const idp_client_config_1 = require("../../lib/idp-client-config");
|
|
8
8
|
const client_key_1 = require("../../lib/client-key");
|
|
9
9
|
const idp_url_guard_1 = require("../../lib/idp-url-guard");
|
|
10
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
10
11
|
/**
|
|
11
12
|
* Passkey SIGN-IN ceremony (ANONYMOUS — no session exists yet):
|
|
12
13
|
* optionsPOST -> POST /api/ExternalAuth/passwordless/passkey/options
|
|
@@ -56,6 +57,9 @@ async function anonCall(req, path) {
|
|
|
56
57
|
}
|
|
57
58
|
async function optionsPOST(req) {
|
|
58
59
|
const { res, json } = await anonCall(req, '/api/ExternalAuth/passwordless/passkey/options');
|
|
60
|
+
// PAY-1438: anonymous route - on failure only the whitelisted envelope, never the IDP body.
|
|
61
|
+
if (!res.ok)
|
|
62
|
+
return server_1.NextResponse.json((0, safe_error_response_1.idpErrorEnvelope)(json, 'Passkey sign-in failed'), { status: res.status });
|
|
59
63
|
return server_1.NextResponse.json(json ?? { success: false, message: 'Empty upstream response' }, { status: res.status });
|
|
60
64
|
}
|
|
61
65
|
/**
|
|
@@ -108,5 +112,7 @@ async function verifyPOST(req) {
|
|
|
108
112
|
cache: 'no-store',
|
|
109
113
|
});
|
|
110
114
|
const json = await res.json().catch(() => null);
|
|
115
|
+
if (!res.ok)
|
|
116
|
+
return server_1.NextResponse.json((0, safe_error_response_1.idpErrorEnvelope)(json, 'Passkey sign-in failed'), { status: res.status });
|
|
111
117
|
return server_1.NextResponse.json(json ?? { success: false, message: 'Empty upstream response' }, { status: res.status });
|
|
112
118
|
}
|
|
@@ -43,6 +43,7 @@ const auth_1 = require("../../server/auth");
|
|
|
43
43
|
const session_store_1 = require("../../lib/session-store");
|
|
44
44
|
const token_expiry_1 = require("../../lib/token-expiry");
|
|
45
45
|
const token_utils_1 = require("../../auth/utils/token-utils");
|
|
46
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
46
47
|
/**
|
|
47
48
|
* Creates a refresh token handler for Next.js API routes
|
|
48
49
|
*
|
|
@@ -152,7 +153,7 @@ function createRefreshHandler(config) {
|
|
|
152
153
|
console.log('║ REFRESH TOKEN ATTEMPT - PRE-FLIGHT ║');
|
|
153
154
|
console.log('╠══════════════════════════════════════════════════════════════════════════════╣');
|
|
154
155
|
console.log(`║ User: ${(userId || 'unknown').substring(0, 50).padEnd(50)} ║`);
|
|
155
|
-
console.log(`║ Session: ${
|
|
156
|
+
console.log(`║ Session: ${(0, token_log_id_1.tokenLogId)(sessionToken)} ║`);
|
|
156
157
|
console.log('╠──────────────────────────────────────────────────────────────────────────────╣');
|
|
157
158
|
console.log(`║ Access Token Expires In: ${String(accessTokenExpiresIn).padEnd(10)} seconds ║`);
|
|
158
159
|
console.log(`║ Refresh Token Age: ${String(refreshTokenAge).padEnd(10)} seconds ║`);
|
|
@@ -482,11 +483,18 @@ function createRefreshHandler(config) {
|
|
|
482
483
|
console.error(`║ ${whyItFailed.substring(0, 76).padEnd(76)} ║`);
|
|
483
484
|
console.error('╠──────────────────────────────────────────────────────────────────────────────╣');
|
|
484
485
|
console.error(`║ User: ${(userId || 'unknown').substring(0, 60).padEnd(60)}║`);
|
|
485
|
-
console.error(`║ Session: ${
|
|
486
|
+
console.error(`║ Session: ${(0, token_log_id_1.tokenLogId)(sessionToken)} ║`);
|
|
486
487
|
console.error(`║ Resolution: ${(resolution || 'Check IDP logs').substring(0, 60).padEnd(60)}║`);
|
|
487
488
|
console.error('╚══════════════════════════════════════════════════════════════════════════════╝');
|
|
488
|
-
//
|
|
489
|
-
|
|
489
|
+
// NEVER the raw envelope (DotNetPert-Scout, PR 139 review / mail 58691): this used to print
|
|
490
|
+
// a stringified dump of responseData (first 1000 chars) - the same whole IDP body the :618
|
|
491
|
+
// "unsuccessful" branch stopped logging. The box above already carries status, code and
|
|
492
|
+
// flags; add envelope SHAPE only (key names), never values.
|
|
493
|
+
console.error('[AUTH_REFRESH] IDP error response shape', {
|
|
494
|
+
bodyKeys: responseData && typeof responseData === 'object' ? Object.keys(responseData) : undefined,
|
|
495
|
+
errorKeys: idpError && typeof idpError === 'object' ? Object.keys(idpError) : undefined,
|
|
496
|
+
errorHasMessage: !!idpError.message,
|
|
497
|
+
});
|
|
490
498
|
// If IDP signals to discard token, clear it from Redis
|
|
491
499
|
if (discardToken) {
|
|
492
500
|
// Race-safe discard (CAS): only clear if the refresh token we sent is STILL
|
|
@@ -500,7 +508,7 @@ function createRefreshHandler(config) {
|
|
|
500
508
|
reason: 'IDP_DISCARD_TOKEN',
|
|
501
509
|
errorCode,
|
|
502
510
|
userId,
|
|
503
|
-
sessionToken:
|
|
511
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
504
512
|
hadRefreshToken: !!currentSession.idpRefreshToken,
|
|
505
513
|
stack: new Error().stack
|
|
506
514
|
});
|
|
@@ -542,7 +550,16 @@ function createRefreshHandler(config) {
|
|
|
542
550
|
(responseData.success === true ? Object.prototype.hasOwnProperty.call(responseData, 'data') : Object.prototype.hasOwnProperty.call(responseData, 'error')) &&
|
|
543
551
|
Object.prototype.hasOwnProperty.call(responseData, 'meta');
|
|
544
552
|
if (!isCompliant) {
|
|
545
|
-
|
|
553
|
+
// NEVER the raw body (DotNetPert-Scout, mail 58678): a non-compliant response has an
|
|
554
|
+
// UNKNOWN shape by definition, so there is no basis to assume it is free of tokens or
|
|
555
|
+
// other IDP-side secrets. Log shape metadata only - same "kind, never value" reasoning
|
|
556
|
+
// as the session-store.ts / refresh.ts REFRESH_TOKEN_REMOVAL fixes in PR 138.
|
|
557
|
+
console.error('[AUTH_REFRESH] Upstream non-compliant IDP response', {
|
|
558
|
+
bodyType: typeof responseData,
|
|
559
|
+
bodyKeys: responseData && typeof responseData === 'object' ? Object.keys(responseData) : undefined,
|
|
560
|
+
hasSuccessKey: !!(responseData && typeof responseData === 'object' && Object.prototype.hasOwnProperty.call(responseData, 'success')),
|
|
561
|
+
hasMetaKey: !!(responseData && typeof responseData === 'object' && Object.prototype.hasOwnProperty.call(responseData, 'meta')),
|
|
562
|
+
});
|
|
546
563
|
return server_1.NextResponse.json({
|
|
547
564
|
error: 'Upstream non-compliance',
|
|
548
565
|
code: 'UPSTREAM_SERVICE_ERROR',
|
|
@@ -556,17 +573,24 @@ function createRefreshHandler(config) {
|
|
|
556
573
|
const errorCode = idpError.code || 'UPSTREAM_VALIDATION_ERROR';
|
|
557
574
|
const discardToken = idpError.discard_token === true;
|
|
558
575
|
const retryable = idpError.retryable === true;
|
|
576
|
+
// NEVER the raw envelope (DotNetPert-Scout, mail 58678): `responseData` here is the
|
|
577
|
+
// WHOLE IDP response body, and its `error` sub-object's contents are not something this
|
|
578
|
+
// handler controls or has audited - printing it wholesale would ship whatever the IDP
|
|
579
|
+
// decided to put there straight to the log sink. Log the fields THIS handler already
|
|
580
|
+
// extracts and acts on (retryable, plus envelope-shape flags for diagnosis) instead.
|
|
559
581
|
console.warn('[AUTH_REFRESH] IDP refresh unsuccessful', {
|
|
560
582
|
code: errorCode,
|
|
561
583
|
discardToken,
|
|
562
|
-
|
|
584
|
+
retryable,
|
|
585
|
+
errorHasMessage: !!idpError.message,
|
|
586
|
+
errorKeys: Object.keys(idpError),
|
|
563
587
|
});
|
|
564
588
|
if (discardToken) {
|
|
565
589
|
console.error('[AUTH_REFRESH] ⚠️ REFRESH_TOKEN_REMOVAL: IDP success:false with discard_token', {
|
|
566
590
|
reason: 'IDP_SUCCESS_FALSE_DISCARD',
|
|
567
591
|
errorCode,
|
|
568
592
|
userId,
|
|
569
|
-
sessionToken:
|
|
593
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
570
594
|
hadRefreshToken: !!currentSession.idpRefreshToken,
|
|
571
595
|
stack: new Error().stack
|
|
572
596
|
});
|
|
@@ -646,9 +670,12 @@ function createRefreshHandler(config) {
|
|
|
646
670
|
console.error('[AUTH_REFRESH] ⚠️ REFRESH_TOKEN_REMOVAL: No new refresh token in IDP response', {
|
|
647
671
|
reason: 'NO_NEW_REFRESH_TOKEN_IN_RESPONSE',
|
|
648
672
|
userId,
|
|
649
|
-
sessionToken:
|
|
673
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
650
674
|
hadRefreshToken: true,
|
|
651
|
-
|
|
675
|
+
// Kind only, never the value (credential). This branch runs when refreshToken is not a
|
|
676
|
+
// non-empty string, so the old raw value was almost always empty - but a token-shaped
|
|
677
|
+
// non-string would have been printed. refreshTokenType below already carries the kind.
|
|
678
|
+
refreshTokenFromResponsePresent: refreshToken !== undefined && refreshToken !== null && refreshToken !== '',
|
|
652
679
|
refreshTokenType: typeof refreshToken,
|
|
653
680
|
payloadKeys: Object.keys(payload || {}),
|
|
654
681
|
stack: new Error().stack
|
|
@@ -696,7 +723,7 @@ function createRefreshHandler(config) {
|
|
|
696
723
|
expiresAt: new Date(accessTokenExpires).toISOString(),
|
|
697
724
|
userId,
|
|
698
725
|
hasNewRefreshToken: hasNewRefresh,
|
|
699
|
-
|
|
726
|
+
hasAccessToken: !!accessToken // PAY-1427: was tokenPreview (first 20 chars of the new access token)
|
|
700
727
|
});
|
|
701
728
|
}
|
|
702
729
|
catch (error) {
|
|
@@ -17,6 +17,7 @@ const headers_1 = require("next/headers");
|
|
|
17
17
|
const session_store_1 = require("../../lib/session-store");
|
|
18
18
|
const auth_1 = require("../../server/auth");
|
|
19
19
|
const app_slug_1 = require("../../lib/app-slug");
|
|
20
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
20
21
|
// JWT decode helper - simple base64 decode without verification
|
|
21
22
|
function jwtDecode(token) {
|
|
22
23
|
try {
|
|
@@ -135,7 +136,7 @@ function createSignoutHandler() {
|
|
|
135
136
|
try {
|
|
136
137
|
await (0, session_store_1.deleteSessionEverywhere)(redisSessionToken);
|
|
137
138
|
console.info('[SIGNOUT] Redis session cleanup successful', {
|
|
138
|
-
sessionToken:
|
|
139
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(redisSessionToken)
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
catch (sessionError) {
|
|
@@ -4,6 +4,7 @@ exports.GET = GET;
|
|
|
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");
|
|
7
8
|
async function GET(req) {
|
|
8
9
|
try {
|
|
9
10
|
const betterAuthSession = await (0, auth_1.getSession)(req);
|
|
@@ -18,6 +19,8 @@ async function GET(req) {
|
|
|
18
19
|
return server_1.NextResponse.json({ success: true, userId: betterAuthSession?.user?.id || sessionModel.userId || null });
|
|
19
20
|
}
|
|
20
21
|
catch (err) {
|
|
21
|
-
|
|
22
|
+
// PAY-1438: this catch wraps both session reads (Better Auth + Redis), before any auth decision.
|
|
23
|
+
const correlationId = (0, safe_error_response_1.logExceptionWithCorrelation)('[AUTH_STATUS] Error reading session:', err);
|
|
24
|
+
return server_1.NextResponse.json({ success: false, error: 'Failed to read session status', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
|
|
22
25
|
}
|
|
23
26
|
}
|
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Authentication Update Session API Handler
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* This handler is designed to be imported and used by Next.js applications
|
|
6
|
-
* using the @payez/next-mvp package.
|
|
4
|
+
* *** THIS HANDLER DOES NOT PERSIST ANYTHING. IT IS AN ECHO. ***
|
|
7
5
|
*
|
|
8
|
-
*
|
|
6
|
+
* It authenticates the caller, reads `twoFactorSessionVerified` from the REQUEST
|
|
7
|
+
* BODY, and echoes that caller-asserted value straight back. There is no redis
|
|
8
|
+
* write, no cookie write, no JWT mint and no session-store call anywhere in this
|
|
9
|
+
* module - the only `.set(` calls are the security headers below. The 2FA state
|
|
10
|
+
* does NOT survive the request.
|
|
11
|
+
*
|
|
12
|
+
* Do not wire this expecting it to record a second factor. It is kept because
|
|
13
|
+
* live callers POST to it on the 2FA completion path and a 404/501 would break
|
|
14
|
+
* that flow; it is deliberately inert, NOT a session writer. Callers can detect
|
|
15
|
+
* this programmatically via `persisted: false` in the response.
|
|
16
|
+
*
|
|
17
|
+
* Card 391621 measured the consequence: because nothing is stored, downstream
|
|
18
|
+
* 2FA gates read no verified state and FAIL CLOSED. That is why this is an
|
|
19
|
+
* honesty/diagnosability defect rather than an auth bypass - but a future
|
|
20
|
+
* consumer that trusts `success: true` here would be trusting nothing.
|
|
21
|
+
*
|
|
22
|
+
* If you need to actually record 2FA completion, write to the session store
|
|
23
|
+
* (see `../../server/session-store`); do not extend this handler in place.
|
|
24
|
+
*
|
|
25
|
+
* @version 2.1
|
|
9
26
|
* @requires Authentication (authenticated endpoint)
|
|
10
27
|
*/
|
|
11
28
|
import { NextRequest, NextResponse } from 'next/server';
|
|
12
29
|
/**
|
|
13
30
|
* Creates an update-session handler for Next.js API routes.
|
|
14
31
|
*
|
|
32
|
+
* *** NO-OP: this handler PERSISTS NOTHING. *** It requires a valid session,
|
|
33
|
+
* then echoes the caller-asserted `twoFactorSessionVerified` back with
|
|
34
|
+
* `persisted: false`. See the module header before wiring it.
|
|
35
|
+
*
|
|
15
36
|
* Better Auth resolves its session from cookies, so this handler takes no
|
|
16
37
|
* configuration. Use the default `POST` export below for typical usage.
|
|
17
38
|
*
|
|
@@ -2,11 +2,28 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Authentication Update Session API Handler
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* This handler is designed to be imported and used by Next.js applications
|
|
7
|
-
* using the @payez/next-mvp package.
|
|
5
|
+
* *** THIS HANDLER DOES NOT PERSIST ANYTHING. IT IS AN ECHO. ***
|
|
8
6
|
*
|
|
9
|
-
*
|
|
7
|
+
* It authenticates the caller, reads `twoFactorSessionVerified` from the REQUEST
|
|
8
|
+
* BODY, and echoes that caller-asserted value straight back. There is no redis
|
|
9
|
+
* write, no cookie write, no JWT mint and no session-store call anywhere in this
|
|
10
|
+
* module - the only `.set(` calls are the security headers below. The 2FA state
|
|
11
|
+
* does NOT survive the request.
|
|
12
|
+
*
|
|
13
|
+
* Do not wire this expecting it to record a second factor. It is kept because
|
|
14
|
+
* live callers POST to it on the 2FA completion path and a 404/501 would break
|
|
15
|
+
* that flow; it is deliberately inert, NOT a session writer. Callers can detect
|
|
16
|
+
* this programmatically via `persisted: false` in the response.
|
|
17
|
+
*
|
|
18
|
+
* Card 391621 measured the consequence: because nothing is stored, downstream
|
|
19
|
+
* 2FA gates read no verified state and FAIL CLOSED. That is why this is an
|
|
20
|
+
* honesty/diagnosability defect rather than an auth bypass - but a future
|
|
21
|
+
* consumer that trusts `success: true` here would be trusting nothing.
|
|
22
|
+
*
|
|
23
|
+
* If you need to actually record 2FA completion, write to the session store
|
|
24
|
+
* (see `../../server/session-store`); do not extend this handler in place.
|
|
25
|
+
*
|
|
26
|
+
* @version 2.1
|
|
10
27
|
* @requires Authentication (authenticated endpoint)
|
|
11
28
|
*/
|
|
12
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -31,6 +48,10 @@ function addSecurityHeaders(response) {
|
|
|
31
48
|
/**
|
|
32
49
|
* Creates an update-session handler for Next.js API routes.
|
|
33
50
|
*
|
|
51
|
+
* *** NO-OP: this handler PERSISTS NOTHING. *** It requires a valid session,
|
|
52
|
+
* then echoes the caller-asserted `twoFactorSessionVerified` back with
|
|
53
|
+
* `persisted: false`. See the module header before wiring it.
|
|
54
|
+
*
|
|
34
55
|
* Better Auth resolves its session from cookies, so this handler takes no
|
|
35
56
|
* configuration. Use the default `POST` export below for typical usage.
|
|
36
57
|
*
|
|
@@ -56,20 +77,26 @@ function createUpdateSessionHandler() {
|
|
|
56
77
|
if (!session) {
|
|
57
78
|
return addSecurityHeaders(server_1.NextResponse.json({ error: 'No session token available' }, { status: 401 }));
|
|
58
79
|
}
|
|
59
|
-
//
|
|
60
|
-
|
|
80
|
+
// NOT a session update. This object is local, is never written anywhere,
|
|
81
|
+
// and is discarded when the request ends. Named for what it is so the
|
|
82
|
+
// next reader cannot mistake it for persisted state.
|
|
83
|
+
const echoedRequest = {
|
|
61
84
|
twoFactorSessionVerified: !!twoFactorSessionVerified,
|
|
62
85
|
twoFactorMethod: twoFactorMethod || session.twoFactorMethod
|
|
63
86
|
};
|
|
64
|
-
|
|
87
|
+
// Previously logged "Session updated successfully", which asserted a write
|
|
88
|
+
// that never happened and made the no-op invisible in production logs.
|
|
89
|
+
console.warn('[UPDATE-SESSION] NO-OP: nothing persisted; echoing caller-asserted 2FA state', {
|
|
65
90
|
userId: session.user?.id,
|
|
66
|
-
twoFactorSessionVerified:
|
|
67
|
-
twoFactorMethod:
|
|
91
|
+
twoFactorSessionVerified: echoedRequest.twoFactorSessionVerified,
|
|
92
|
+
twoFactorMethod: echoedRequest.twoFactorMethod,
|
|
93
|
+
persisted: false
|
|
68
94
|
});
|
|
69
95
|
const responseData = {
|
|
70
96
|
success: true,
|
|
71
|
-
twoFactorSessionVerified:
|
|
72
|
-
twoFactorMethod:
|
|
97
|
+
twoFactorSessionVerified: echoedRequest.twoFactorSessionVerified,
|
|
98
|
+
twoFactorMethod: echoedRequest.twoFactorMethod,
|
|
99
|
+
persisted: false
|
|
73
100
|
};
|
|
74
101
|
return addSecurityHeaders(server_1.NextResponse.json(responseData));
|
|
75
102
|
}
|
|
@@ -6,6 +6,7 @@ const idp_fetch_1 = require("../../lib/idp-fetch");
|
|
|
6
6
|
const env_1 = require("../../config/env");
|
|
7
7
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
8
8
|
const session_store_1 = require("../../lib/session-store");
|
|
9
|
+
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
9
10
|
/**
|
|
10
11
|
* Validates current access token with the IDP and returns normalized info.
|
|
11
12
|
* Sources access token from Authorization header or Redis session via cookie.
|
|
@@ -32,12 +33,13 @@ async function GET(req) {
|
|
|
32
33
|
const url = `${env_1.ENV_CONFIG.IDP_URL}${env_1.API_ENDPOINTS.externalAuth.validate}`;
|
|
33
34
|
const result = await (0, idp_fetch_1.idpFetchJSON)(req, url, { method: 'GET', headers: { Authorization: `Bearer ${bearer}` } });
|
|
34
35
|
if (!result.ok) {
|
|
35
|
-
return server_1.NextResponse.json({ success: false, error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status
|
|
36
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: (0, safe_error_response_1.projectIdpError)(result.json) } }, { status: result.status });
|
|
36
37
|
}
|
|
37
38
|
// Passthrough normalized
|
|
38
39
|
return server_1.NextResponse.json(result.json ?? { success: true }, { status: 200 });
|
|
39
40
|
}
|
|
40
41
|
catch (e) {
|
|
41
|
-
|
|
42
|
+
const correlationId = (0, safe_error_response_1.logExceptionWithCorrelation)('[AUTH_VALIDATE] Error:', e);
|
|
43
|
+
return server_1.NextResponse.json({ success: false, error: { code: 'INTERNAL_SERVER_ERROR', message: 'validate error', correlationId } }, { status: 500 });
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -15,6 +15,7 @@ exports.createVerifyCodeHandler = createVerifyCodeHandler;
|
|
|
15
15
|
const server_1 = require("next/server");
|
|
16
16
|
const auth_1 = require("../../server/auth");
|
|
17
17
|
const session_store_1 = require("../../lib/session-store");
|
|
18
|
+
const token_log_id_1 = require("../../lib/token-log-id");
|
|
18
19
|
/**
|
|
19
20
|
* Creates a verify-code/complete-2FA handler for Next.js API routes.
|
|
20
21
|
*
|
|
@@ -48,7 +49,7 @@ function createVerifyCodeHandler() {
|
|
|
48
49
|
return server_1.NextResponse.json({ success: false, message: 'No session found' }, { status: 401 });
|
|
49
50
|
}
|
|
50
51
|
console.info('[VERIFY-CODE] Updating session with new tokens after 2FA', {
|
|
51
|
-
sessionToken:
|
|
52
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
52
53
|
userId: betterAuthSession?.user?.id,
|
|
53
54
|
hasAccessToken: !!accessToken,
|
|
54
55
|
hasRefreshToken: !!refreshToken,
|
|
@@ -63,7 +64,7 @@ function createVerifyCodeHandler() {
|
|
|
63
64
|
// treats the missing expiry as "never expires".
|
|
64
65
|
await (0, session_store_1.mark2FAComplete)(sessionToken, accessToken);
|
|
65
66
|
console.info('[VERIFY-CODE] 2FA completion successful', {
|
|
66
|
-
sessionToken:
|
|
67
|
+
sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
|
|
67
68
|
userId: betterAuthSession?.user?.id
|
|
68
69
|
});
|
|
69
70
|
return server_1.NextResponse.json({
|
|
@@ -53,21 +53,4 @@
|
|
|
53
53
|
* @package @payez/next-mvp
|
|
54
54
|
*/
|
|
55
55
|
import { NextRequest, NextResponse } from 'next/server';
|
|
56
|
-
export declare function POST(req: NextRequest): Promise<NextResponse<
|
|
57
|
-
success: boolean;
|
|
58
|
-
error: string;
|
|
59
|
-
code: string;
|
|
60
|
-
}> | NextResponse<{
|
|
61
|
-
success: boolean;
|
|
62
|
-
error: any;
|
|
63
|
-
code: any;
|
|
64
|
-
meta: {
|
|
65
|
-
attemptedRefresh: boolean;
|
|
66
|
-
};
|
|
67
|
-
}> | NextResponse<{
|
|
68
|
-
success: boolean;
|
|
69
|
-
verificationSuccessful: boolean;
|
|
70
|
-
twoFactorSessionVerified: boolean;
|
|
71
|
-
codeConsumed: boolean;
|
|
72
|
-
message: any;
|
|
73
|
-
}>>;
|
|
56
|
+
export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;
|
|
@@ -61,6 +61,7 @@ const env_1 = require("../../config/env");
|
|
|
61
61
|
const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
|
|
62
62
|
const session_store_1 = require("../../lib/session-store");
|
|
63
63
|
const jwt_decode_1 = require("../../lib/jwt-decode");
|
|
64
|
+
const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
|
|
64
65
|
async function POST(req) {
|
|
65
66
|
try {
|
|
66
67
|
const body = await req.json();
|
|
@@ -78,12 +79,9 @@ async function POST(req) {
|
|
|
78
79
|
body: JSON.stringify({ code }),
|
|
79
80
|
});
|
|
80
81
|
if (!result.ok) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
code: result.json?.code || 'IDP_ERROR',
|
|
85
|
-
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
86
|
-
}, { status: result.status });
|
|
82
|
+
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
83
|
+
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
84
|
+
return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'backup_code');
|
|
87
85
|
}
|
|
88
86
|
const unwrappedData = result.json?.data || result.json;
|
|
89
87
|
// Only a challenge that returns fresh tokens may transition the session.
|
|
@@ -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
|
* Generate MFA backup codes (POST /api/Account/mfa/backup-codes, user JWT).
|
|
9
10
|
* Regeneration REPLACES the prior set — the response carries the new codes
|
|
@@ -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
|
}
|
|
@@ -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
|
* MFA methods (GET list / DELETE one, /api/Account/mfa/methods[/{id}],
|
|
10
11
|
* user JWT). Drives the method picker: an EMPTY list must route the user to
|
|
@@ -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
|
* Confirm TOTP enrolment with the first authenticator code
|
|
9
10
|
* (POST /api/Account/mfa/totp/confirm, user JWT). Body forwarded verbatim —
|
|
@@ -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
|
* Begin TOTP enrolment (POST /api/Account/mfa/totp/enroll, user JWT).
|
|
9
10
|
* Returns the shared secret / otpauth URI for the QR. An enrolment left
|
|
@@ -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
|
}
|