@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.
Files changed (219) hide show
  1. package/README.md +10 -3
  2. package/dist/api/auth-handler.js +5 -3
  3. package/dist/api-handlers/account/change-password.js +33 -8
  4. package/dist/api-handlers/account/masked-info.js +2 -1
  5. package/dist/api-handlers/account/recovery/forward.d.ts +14 -0
  6. package/dist/api-handlers/account/recovery/forward.js +105 -0
  7. package/dist/api-handlers/account/recovery/initiate.js +4 -0
  8. package/dist/api-handlers/account/recovery/send-code.d.ts +12 -2
  9. package/dist/api-handlers/account/recovery/send-code.js +11 -84
  10. package/dist/api-handlers/account/recovery/verify-code.d.ts +11 -2
  11. package/dist/api-handlers/account/recovery/verify-code.js +10 -84
  12. package/dist/api-handlers/account/reset-password.js +24 -1
  13. package/dist/api-handlers/account/send-code.d.ts +1 -15
  14. package/dist/api-handlers/account/send-code.js +3 -6
  15. package/dist/api-handlers/account/update-phone.js +6 -1
  16. package/dist/api-handlers/account/validate-password.js +3 -1
  17. package/dist/api-handlers/account/verify-email.d.ts +1 -17
  18. package/dist/api-handlers/account/verify-email.js +4 -6
  19. package/dist/api-handlers/account/verify-sms.d.ts +1 -17
  20. package/dist/api-handlers/account/verify-sms.js +4 -6
  21. package/dist/api-handlers/admin/analytics.js +12 -4
  22. package/dist/api-handlers/admin/audit.js +12 -4
  23. package/dist/api-handlers/admin/redis-sessions.js +16 -8
  24. package/dist/api-handlers/admin/sessions.js +14 -6
  25. package/dist/api-handlers/admin/site-logs.js +39 -16
  26. package/dist/api-handlers/admin/stats.js +14 -4
  27. package/dist/api-handlers/admin/users.js +14 -6
  28. package/dist/api-handlers/admin/vibe-data.js +19 -11
  29. package/dist/api-handlers/auth/jwks.js +2 -1
  30. package/dist/api-handlers/auth/login.js +13 -4
  31. package/dist/api-handlers/auth/passkey-signin.js +6 -0
  32. package/dist/api-handlers/auth/refresh.js +38 -11
  33. package/dist/api-handlers/auth/signout.js +2 -1
  34. package/dist/api-handlers/auth/status.js +4 -1
  35. package/dist/api-handlers/auth/update-session.d.ts +25 -4
  36. package/dist/api-handlers/auth/update-session.js +38 -11
  37. package/dist/api-handlers/auth/validate.js +4 -2
  38. package/dist/api-handlers/auth/verify-code.js +3 -2
  39. package/dist/api-handlers/mfa/backup-code-verify.d.ts +1 -18
  40. package/dist/api-handlers/mfa/backup-code-verify.js +4 -6
  41. package/dist/api-handlers/mfa/backup-codes.js +2 -1
  42. package/dist/api-handlers/mfa/methods.js +2 -1
  43. package/dist/api-handlers/mfa/totp-confirm.js +2 -1
  44. package/dist/api-handlers/mfa/totp-enroll.js +2 -1
  45. package/dist/api-handlers/mfa/totp-verify.d.ts +1 -19
  46. package/dist/api-handlers/mfa/totp-verify.js +4 -6
  47. package/dist/api-handlers/passkeys/list.js +2 -1
  48. package/dist/api-handlers/passkeys/manage.js +2 -1
  49. package/dist/api-handlers/passkeys/register-options.js +2 -1
  50. package/dist/api-handlers/passkeys/register-verify.js +2 -1
  51. package/dist/api-handlers/session/refresh-viability.d.ts +4 -2
  52. package/dist/api-handlers/session/refresh-viability.js +10 -7
  53. package/dist/api-handlers/session/viability.js +4 -3
  54. package/dist/api-handlers/test/force-expire.js +5 -3
  55. package/dist/auth/auth-decision.js +2 -1
  56. package/dist/auth/better-auth.d.ts +58 -14
  57. package/dist/auth/better-auth.js +125 -54
  58. package/dist/auth/utils/idp-client.js +2 -1
  59. package/dist/client/better-auth-client.d.ts +101 -956
  60. package/dist/components/admin/DataBrowserTab.js +4 -1
  61. package/dist/components/admin/SessionsTab.js +9 -3
  62. package/dist/components/recovery/RequestedStep.d.ts +11 -0
  63. package/dist/components/recovery/RequestedStep.js +14 -0
  64. package/dist/components/recovery/SetPasswordStep.js +10 -1
  65. package/dist/config/env.d.ts +0 -2
  66. package/dist/config/env.js +0 -2
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +8 -1
  69. package/dist/lib/app-slug.d.ts +21 -0
  70. package/dist/lib/app-slug.js +51 -0
  71. package/dist/lib/ensure-fresh-access-token.d.ts +26 -0
  72. package/dist/lib/ensure-fresh-access-token.js +105 -4
  73. package/dist/lib/idp-client-config.d.ts +20 -0
  74. package/dist/lib/idp-client-config.js +73 -45
  75. package/dist/lib/idp-fetch.js +1 -1
  76. package/dist/lib/never-in-response.d.ts +30 -0
  77. package/dist/lib/never-in-response.js +71 -0
  78. package/dist/lib/password-rules.d.ts +22 -0
  79. package/dist/lib/password-rules.js +43 -0
  80. package/dist/lib/safe-error-response.d.ts +67 -0
  81. package/dist/lib/safe-error-response.js +130 -0
  82. package/dist/lib/session-cookie-shape.d.ts +22 -19
  83. package/dist/lib/session-cookie-shape.js +14 -18
  84. package/dist/lib/session-store.d.ts +8 -1
  85. package/dist/lib/session-store.js +170 -24
  86. package/dist/lib/startup-init.d.ts +6 -0
  87. package/dist/lib/startup-init.js +30 -4
  88. package/dist/lib/token-lifecycle.js +2 -1
  89. package/dist/lib/token-log-id.d.ts +18 -0
  90. package/dist/lib/token-log-id.js +84 -0
  91. package/dist/lib/two-factor-verify-failure.d.ts +78 -0
  92. package/dist/lib/two-factor-verify-failure.js +220 -0
  93. package/dist/middleware/create-middleware.d.ts +4 -1
  94. package/dist/middleware/create-middleware.js +79 -8
  95. package/dist/pages/login/page.js +5 -0
  96. package/dist/pages/recovery/page.js +14 -4
  97. package/dist/pages/test-env/JwtInspectPage.js +7 -149
  98. package/dist/pages/test-env/RefreshTokenPage.js +4 -1
  99. package/dist/pages/test-env/redact-credentials.d.ts +6 -0
  100. package/dist/pages/test-env/redact-credentials.js +101 -0
  101. package/dist/pages/verify-code/page.js +6 -3
  102. package/dist/routes/auth/logout.d.ts +2 -1
  103. package/dist/routes/auth/logout.js +4 -2
  104. package/dist/routes/auth/session.d.ts +0 -3
  105. package/dist/routes/auth/session.js +16 -10
  106. package/dist/routes/auth/settings.js +4 -1
  107. package/dist/routes/auth/viability.d.ts +3 -2
  108. package/dist/routes/auth/viability.js +11 -7
  109. package/dist/server/auth.d.ts +36 -294
  110. package/dist/server/auth.js +32 -31
  111. package/dist/server/decode-session.js +15 -26
  112. package/dist/server/verified-session-token.d.ts +13 -0
  113. package/dist/server/verified-session-token.js +103 -0
  114. package/dist/stores/authStore.js +14 -3
  115. package/dist/types/recovery.d.ts +1 -1
  116. package/dist/utils/api.d.ts +18 -0
  117. package/dist/utils/api.js +51 -9
  118. package/dist/vibe/client.d.ts +132 -16
  119. package/dist/vibe/client.js +288 -82
  120. package/dist/vibe/document-unwrap.d.ts +50 -0
  121. package/dist/vibe/document-unwrap.js +78 -0
  122. package/dist/vibe/errors.d.ts +17 -0
  123. package/dist/vibe/errors.js +21 -1
  124. package/dist/vibe/generic.d.ts +2 -49
  125. package/dist/vibe/generic.js +17 -78
  126. package/dist/vibe/index.d.ts +1 -1
  127. package/dist/vibe/index.js +2 -1
  128. package/dist/vibe/sessions.js +2 -1
  129. package/package.json +13 -2
  130. package/scripts/verify-release-provenance.mjs +410 -0
  131. package/src/api/auth-handler.ts +5 -3
  132. package/src/api-handlers/account/change-password.ts +36 -8
  133. package/src/api-handlers/account/masked-info.ts +2 -1
  134. package/src/api-handlers/account/recovery/forward.ts +117 -0
  135. package/src/api-handlers/account/recovery/initiate.ts +3 -0
  136. package/src/api-handlers/account/recovery/send-code.ts +13 -99
  137. package/src/api-handlers/account/recovery/verify-code.ts +12 -99
  138. package/src/api-handlers/account/reset-password.ts +30 -1
  139. package/src/api-handlers/account/send-code.ts +3 -9
  140. package/src/api-handlers/account/update-phone.ts +6 -1
  141. package/src/api-handlers/account/validate-password.ts +3 -1
  142. package/src/api-handlers/account/verify-email.ts +4 -9
  143. package/src/api-handlers/account/verify-sms.ts +4 -9
  144. package/src/api-handlers/admin/analytics.ts +13 -5
  145. package/src/api-handlers/admin/audit.ts +13 -5
  146. package/src/api-handlers/admin/redis-sessions.ts +16 -8
  147. package/src/api-handlers/admin/sessions.ts +15 -7
  148. package/src/api-handlers/admin/site-logs.ts +39 -16
  149. package/src/api-handlers/admin/stats.ts +15 -5
  150. package/src/api-handlers/admin/users.ts +15 -7
  151. package/src/api-handlers/admin/vibe-data.ts +20 -12
  152. package/src/api-handlers/auth/jwks.ts +2 -1
  153. package/src/api-handlers/auth/login.ts +13 -4
  154. package/src/api-handlers/auth/passkey-signin.ts +4 -0
  155. package/src/api-handlers/auth/refresh.ts +38 -11
  156. package/src/api-handlers/auth/signout.ts +2 -1
  157. package/src/api-handlers/auth/status.ts +4 -1
  158. package/src/api-handlers/auth/update-session.ts +45 -11
  159. package/src/api-handlers/auth/validate.ts +4 -2
  160. package/src/api-handlers/auth/verify-code.ts +3 -2
  161. package/src/api-handlers/mfa/backup-code-verify.ts +4 -9
  162. package/src/api-handlers/mfa/backup-codes.ts +2 -1
  163. package/src/api-handlers/mfa/methods.ts +2 -1
  164. package/src/api-handlers/mfa/totp-confirm.ts +2 -1
  165. package/src/api-handlers/mfa/totp-enroll.ts +2 -1
  166. package/src/api-handlers/mfa/totp-verify.ts +4 -9
  167. package/src/api-handlers/passkeys/list.ts +2 -1
  168. package/src/api-handlers/passkeys/manage.ts +2 -1
  169. package/src/api-handlers/passkeys/register-options.ts +2 -1
  170. package/src/api-handlers/passkeys/register-verify.ts +2 -1
  171. package/src/api-handlers/session/refresh-viability.ts +10 -7
  172. package/src/api-handlers/session/viability.ts +4 -3
  173. package/src/api-handlers/test/force-expire.ts +5 -3
  174. package/src/auth/auth-decision.ts +2 -1
  175. package/src/auth/better-auth.ts +128 -61
  176. package/src/auth/utils/idp-client.ts +2 -1
  177. package/src/components/admin/DataBrowserTab.tsx +4 -1
  178. package/src/components/admin/SessionsTab.tsx +10 -3
  179. package/src/components/recovery/RequestedStep.tsx +30 -0
  180. package/src/components/recovery/SetPasswordStep.tsx +24 -3
  181. package/src/config/env.ts +0 -2
  182. package/src/hooks/useViabilitySession.ts +2 -1
  183. package/src/index.ts +4 -0
  184. package/src/lib/app-slug.ts +50 -0
  185. package/src/lib/ensure-fresh-access-token.ts +133 -4
  186. package/src/lib/idp-client-config.ts +98 -45
  187. package/src/lib/idp-fetch.ts +1 -1
  188. package/src/lib/never-in-response.ts +64 -0
  189. package/src/lib/password-rules.ts +50 -0
  190. package/src/lib/safe-error-response.ts +126 -0
  191. package/src/lib/session-cookie-shape.ts +23 -22
  192. package/src/lib/session-store.ts +167 -24
  193. package/src/lib/startup-init.ts +29 -4
  194. package/src/lib/token-lifecycle.ts +2 -1
  195. package/src/lib/token-log-id.ts +69 -0
  196. package/src/lib/two-factor-verify-failure.ts +248 -0
  197. package/src/middleware/create-middleware.ts +97 -9
  198. package/src/pages/login/page.tsx +4 -0
  199. package/src/pages/recovery/page.tsx +21 -4
  200. package/src/pages/test-env/JwtInspectPage.tsx +11 -233
  201. package/src/pages/test-env/RefreshTokenPage.tsx +9 -4
  202. package/src/pages/test-env/redact-credentials.ts +96 -0
  203. package/src/pages/verify-code/page.tsx +6 -3
  204. package/src/routes/auth/logout.ts +4 -2
  205. package/src/routes/auth/session.ts +16 -10
  206. package/src/routes/auth/settings.ts +5 -1
  207. package/src/routes/auth/viability.ts +11 -7
  208. package/src/server/auth.ts +33 -35
  209. package/src/server/decode-session.ts +16 -26
  210. package/src/server/verified-session-token.ts +103 -0
  211. package/src/stores/authStore.ts +16 -5
  212. package/src/types/recovery.ts +1 -1
  213. package/src/utils/api.ts +47 -8
  214. package/src/vibe/client.ts +378 -91
  215. package/src/vibe/document-unwrap.ts +103 -0
  216. package/src/vibe/errors.ts +24 -0
  217. package/src/vibe/generic.ts +8 -100
  218. package/src/vibe/index.ts +1 -0
  219. 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
- console.error(`[Admin Vibe] Request failed: ${options.method} ${endpoint} - ${res.status}`, errorText);
125
- return { ok: false, status: res.status, data: null, error: errorText };
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
- console.error(`[Admin Vibe] Request exception: ${options.method} ${endpoint}`, error);
132
- return { ok: false, status: 0, data: null, error: String(error) };
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
- logger_1.logger.error('[AUTH_JWKS] Upstream returned non-JSON', { status: res.status, bodyText: bodyText?.slice(0, 200) });
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
- response: rawText,
123
- status: backendResponse.status
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
- error: data
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
- console.error('[LOGIN] IDP authentication failure', { error: data });
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: ${sessionToken.substring(0, 8)}... ║`);
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: ${sessionToken.substring(0, 8)}... ║`);
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
- // Also log the full response for detailed debugging
489
- console.error('[AUTH_REFRESH] Full IDP error response:', JSON.stringify(responseData, null, 2).substring(0, 1000));
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: sessionToken.substring(0, 8) + '...',
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
- console.error('[AUTH_REFRESH] Upstream non-compliant IDP response', { bodySample: responseData });
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
- body: responseData
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: sessionToken.substring(0, 8) + '...',
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: sessionToken.substring(0, 8) + '...',
673
+ sessionToken: (0, token_log_id_1.tokenLogId)(sessionToken),
650
674
  hadRefreshToken: true,
651
- refreshTokenFromResponse: refreshToken,
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
- tokenPreview: accessToken ? accessToken.substring(0, 20) + '...' : 'none'
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: redisSessionToken.substring(0, 8) + '...'
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
- return server_1.NextResponse.json({ success: false, error: err instanceof Error ? err.message : 'Unknown error' }, { status: 500 });
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
- * Handles session updates, particularly for 2FA status changes.
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
- * @version 2.0
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
- * Handles session updates, particularly for 2FA status changes.
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
- * @version 2.0
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
- // Update the session with 2FA challenge completion status
60
- const updatedSession = {
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
- console.info('[UPDATE-SESSION] Session updated successfully', {
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: updatedSession.twoFactorSessionVerified,
67
- twoFactorMethod: updatedSession.twoFactorMethod
91
+ twoFactorSessionVerified: echoedRequest.twoFactorSessionVerified,
92
+ twoFactorMethod: echoedRequest.twoFactorMethod,
93
+ persisted: false
68
94
  });
69
95
  const responseData = {
70
96
  success: true,
71
- twoFactorSessionVerified: updatedSession.twoFactorSessionVerified,
72
- twoFactorMethod: updatedSession.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 }, data: result.json }, { 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
- return server_1.NextResponse.json({ success: false, error: { code: 'INTERNAL_SERVER_ERROR', message: e?.message || 'validate error' } }, { status: 500 });
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: sessionToken.substring(0, 8) + '...',
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: sessionToken.substring(0, 8) + '...',
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
- return server_1.NextResponse.json({
82
- success: false,
83
- error: result.json?.message || 'Verification failed',
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
  }