@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.
Files changed (189) 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 +6 -3
  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 +14 -0
  57. package/dist/auth/better-auth.js +33 -13
  58. package/dist/auth/utils/idp-client.js +2 -1
  59. package/dist/components/admin/DataBrowserTab.js +4 -1
  60. package/dist/components/admin/SessionsTab.js +9 -3
  61. package/dist/components/recovery/RequestedStep.d.ts +11 -0
  62. package/dist/components/recovery/RequestedStep.js +14 -0
  63. package/dist/components/recovery/SetPasswordStep.js +10 -1
  64. package/dist/lib/app-slug.d.ts +21 -0
  65. package/dist/lib/app-slug.js +51 -0
  66. package/dist/lib/ensure-fresh-access-token.d.ts +26 -0
  67. package/dist/lib/ensure-fresh-access-token.js +105 -4
  68. package/dist/lib/idp-client-config.d.ts +20 -0
  69. package/dist/lib/idp-client-config.js +65 -14
  70. package/dist/lib/idp-fetch.js +1 -1
  71. package/dist/lib/password-rules.d.ts +22 -0
  72. package/dist/lib/password-rules.js +43 -0
  73. package/dist/lib/safe-error-response.d.ts +67 -0
  74. package/dist/lib/safe-error-response.js +130 -0
  75. package/dist/lib/session-cookie-shape.d.ts +22 -19
  76. package/dist/lib/session-cookie-shape.js +14 -18
  77. package/dist/lib/session-store.d.ts +8 -1
  78. package/dist/lib/session-store.js +170 -24
  79. package/dist/lib/startup-init.d.ts +6 -0
  80. package/dist/lib/startup-init.js +30 -4
  81. package/dist/lib/token-lifecycle.js +2 -1
  82. package/dist/lib/token-log-id.d.ts +18 -0
  83. package/dist/lib/token-log-id.js +84 -0
  84. package/dist/lib/two-factor-verify-failure.d.ts +45 -0
  85. package/dist/lib/two-factor-verify-failure.js +143 -0
  86. package/dist/middleware/create-middleware.d.ts +2 -1
  87. package/dist/middleware/create-middleware.js +43 -8
  88. package/dist/pages/recovery/page.js +14 -4
  89. package/dist/pages/test-env/JwtInspectPage.js +7 -149
  90. package/dist/pages/test-env/RefreshTokenPage.js +4 -1
  91. package/dist/pages/test-env/redact-credentials.d.ts +6 -0
  92. package/dist/pages/test-env/redact-credentials.js +101 -0
  93. package/dist/pages/verify-code/page.js +2 -1
  94. package/dist/routes/auth/logout.d.ts +2 -1
  95. package/dist/routes/auth/logout.js +4 -2
  96. package/dist/routes/auth/session.d.ts +0 -3
  97. package/dist/routes/auth/session.js +16 -10
  98. package/dist/routes/auth/viability.d.ts +3 -2
  99. package/dist/routes/auth/viability.js +11 -7
  100. package/dist/server/auth.js +10 -6
  101. package/dist/server/decode-session.js +15 -26
  102. package/dist/server/verified-session-token.d.ts +13 -0
  103. package/dist/server/verified-session-token.js +103 -0
  104. package/dist/stores/authStore.js +14 -3
  105. package/dist/types/recovery.d.ts +1 -1
  106. package/dist/utils/api.d.ts +18 -0
  107. package/dist/utils/api.js +51 -9
  108. package/dist/vibe/sessions.js +2 -1
  109. package/package.json +2 -1
  110. package/scripts/verify-release-provenance.mjs +410 -0
  111. package/src/api/auth-handler.ts +5 -3
  112. package/src/api-handlers/account/change-password.ts +6 -3
  113. package/src/api-handlers/account/masked-info.ts +2 -1
  114. package/src/api-handlers/account/recovery/forward.ts +117 -0
  115. package/src/api-handlers/account/recovery/initiate.ts +3 -0
  116. package/src/api-handlers/account/recovery/send-code.ts +13 -99
  117. package/src/api-handlers/account/recovery/verify-code.ts +12 -99
  118. package/src/api-handlers/account/reset-password.ts +30 -1
  119. package/src/api-handlers/account/send-code.ts +3 -9
  120. package/src/api-handlers/account/update-phone.ts +6 -1
  121. package/src/api-handlers/account/validate-password.ts +3 -1
  122. package/src/api-handlers/account/verify-email.ts +4 -9
  123. package/src/api-handlers/account/verify-sms.ts +4 -9
  124. package/src/api-handlers/admin/analytics.ts +13 -5
  125. package/src/api-handlers/admin/audit.ts +13 -5
  126. package/src/api-handlers/admin/redis-sessions.ts +16 -8
  127. package/src/api-handlers/admin/sessions.ts +15 -7
  128. package/src/api-handlers/admin/site-logs.ts +39 -16
  129. package/src/api-handlers/admin/stats.ts +15 -5
  130. package/src/api-handlers/admin/users.ts +15 -7
  131. package/src/api-handlers/admin/vibe-data.ts +20 -12
  132. package/src/api-handlers/auth/jwks.ts +2 -1
  133. package/src/api-handlers/auth/login.ts +13 -4
  134. package/src/api-handlers/auth/passkey-signin.ts +4 -0
  135. package/src/api-handlers/auth/refresh.ts +38 -11
  136. package/src/api-handlers/auth/signout.ts +2 -1
  137. package/src/api-handlers/auth/status.ts +4 -1
  138. package/src/api-handlers/auth/update-session.ts +45 -11
  139. package/src/api-handlers/auth/validate.ts +4 -2
  140. package/src/api-handlers/auth/verify-code.ts +3 -2
  141. package/src/api-handlers/mfa/backup-code-verify.ts +4 -9
  142. package/src/api-handlers/mfa/backup-codes.ts +2 -1
  143. package/src/api-handlers/mfa/methods.ts +2 -1
  144. package/src/api-handlers/mfa/totp-confirm.ts +2 -1
  145. package/src/api-handlers/mfa/totp-enroll.ts +2 -1
  146. package/src/api-handlers/mfa/totp-verify.ts +4 -9
  147. package/src/api-handlers/passkeys/list.ts +2 -1
  148. package/src/api-handlers/passkeys/manage.ts +2 -1
  149. package/src/api-handlers/passkeys/register-options.ts +2 -1
  150. package/src/api-handlers/passkeys/register-verify.ts +2 -1
  151. package/src/api-handlers/session/refresh-viability.ts +10 -7
  152. package/src/api-handlers/session/viability.ts +4 -3
  153. package/src/api-handlers/test/force-expire.ts +5 -3
  154. package/src/auth/auth-decision.ts +2 -1
  155. package/src/auth/better-auth.ts +35 -10
  156. package/src/auth/utils/idp-client.ts +2 -1
  157. package/src/components/admin/DataBrowserTab.tsx +4 -1
  158. package/src/components/admin/SessionsTab.tsx +10 -3
  159. package/src/components/recovery/RequestedStep.tsx +30 -0
  160. package/src/components/recovery/SetPasswordStep.tsx +24 -3
  161. package/src/hooks/useViabilitySession.ts +2 -1
  162. package/src/lib/app-slug.ts +50 -0
  163. package/src/lib/ensure-fresh-access-token.ts +133 -4
  164. package/src/lib/idp-client-config.ts +90 -14
  165. package/src/lib/idp-fetch.ts +1 -1
  166. package/src/lib/password-rules.ts +50 -0
  167. package/src/lib/safe-error-response.ts +126 -0
  168. package/src/lib/session-cookie-shape.ts +23 -22
  169. package/src/lib/session-store.ts +167 -24
  170. package/src/lib/startup-init.ts +29 -4
  171. package/src/lib/token-lifecycle.ts +2 -1
  172. package/src/lib/token-log-id.ts +69 -0
  173. package/src/lib/two-factor-verify-failure.ts +167 -0
  174. package/src/middleware/create-middleware.ts +55 -9
  175. package/src/pages/recovery/page.tsx +21 -4
  176. package/src/pages/test-env/JwtInspectPage.tsx +11 -233
  177. package/src/pages/test-env/RefreshTokenPage.tsx +9 -4
  178. package/src/pages/test-env/redact-credentials.ts +96 -0
  179. package/src/pages/verify-code/page.tsx +2 -1
  180. package/src/routes/auth/logout.ts +4 -2
  181. package/src/routes/auth/session.ts +16 -10
  182. package/src/routes/auth/viability.ts +11 -7
  183. package/src/server/auth.ts +10 -6
  184. package/src/server/decode-session.ts +16 -26
  185. package/src/server/verified-session-token.ts +103 -0
  186. package/src/stores/authStore.ts +16 -5
  187. package/src/types/recovery.ts +1 -1
  188. package/src/utils/api.ts +47 -8
  189. package/src/vibe/sessions.ts +2 -1
@@ -15,6 +15,7 @@ const env_1 = require("../../config/env");
15
15
  const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
16
16
  const session_store_1 = require("../../lib/session-store");
17
17
  const jwt_decode_1 = require("../../lib/jwt-decode");
18
+ const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
18
19
  async function POST(req) {
19
20
  try {
20
21
  // Parse request body
@@ -34,12 +35,9 @@ async function POST(req) {
34
35
  body: JSON.stringify({ verification_code: verificationCode }),
35
36
  });
36
37
  if (!result.ok) {
37
- return server_1.NextResponse.json({
38
- success: false,
39
- error: result.json?.message || 'Verification failed',
40
- code: result.json?.code || 'IDP_ERROR',
41
- meta: { attemptedRefresh: result.attemptedRefresh },
42
- }, { status: result.status });
38
+ // PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
39
+ // ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
40
+ return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'email');
43
41
  }
44
42
  // Unwrap IDP envelope
45
43
  const unwrappedData = result.json?.data || result.json;
@@ -7,20 +7,4 @@
7
7
  * @package @payez/next-mvp
8
8
  */
9
9
  import { NextRequest, NextResponse } from 'next/server';
10
- export declare function POST(req: NextRequest): Promise<NextResponse<{
11
- success: boolean;
12
- error: string;
13
- code: string;
14
- }> | NextResponse<{
15
- success: boolean;
16
- error: any;
17
- code: any;
18
- meta: {
19
- attemptedRefresh: boolean;
20
- };
21
- }> | NextResponse<{
22
- success: boolean;
23
- verificationSuccessful: boolean;
24
- twoFactorSessionVerified: boolean;
25
- message: any;
26
- }>>;
10
+ export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;
@@ -15,6 +15,7 @@ const env_1 = require("../../config/env");
15
15
  const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
16
16
  const session_store_1 = require("../../lib/session-store");
17
17
  const jwt_decode_1 = require("../../lib/jwt-decode");
18
+ const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
18
19
  async function POST(req) {
19
20
  try {
20
21
  // Parse request body
@@ -34,12 +35,9 @@ async function POST(req) {
34
35
  body: JSON.stringify({ verification_code: verificationCode }),
35
36
  });
36
37
  if (!result.ok) {
37
- return server_1.NextResponse.json({
38
- success: false,
39
- error: result.json?.message || 'Verification failed',
40
- code: result.json?.code || 'IDP_ERROR',
41
- meta: { attemptedRefresh: result.attemptedRefresh },
42
- }, { status: result.status });
38
+ // PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
39
+ // ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
40
+ return (0, two_factor_verify_failure_1.twoFactorVerifyFailure)(result, 'sms');
43
41
  }
44
42
  // Unwrap IDP envelope
45
43
  const unwrappedData = result.json?.data || result.json;
@@ -48,6 +48,7 @@ const auth_1 = require("../../server/auth");
48
48
  const startup_init_1 = require("../../lib/startup-init");
49
49
  const client_key_1 = require("../../lib/client-key");
50
50
  const roles_1 = require("../../lib/roles");
51
+ const crypto_1 = require("crypto");
51
52
  async function checkAdminRole(request) {
52
53
  const session = await (0, auth_1.getSession)(request);
53
54
  if (!session?.user) {
@@ -67,6 +68,9 @@ async function checkAdminRole(request) {
67
68
  return { isAdmin: true };
68
69
  }
69
70
  async function vibeServiceRequest(endpoint, options) {
71
+ // PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
72
+ // exception text. Failures return a fixed message + correlationId; the same id is on the
73
+ // server log line, which carries status + bodyLength only (PAY-1424).
70
74
  const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
71
75
  const clientId = process.env.VIBE_CLIENT_ID;
72
76
  const signingKey = process.env.VIBE_HMAC_KEY;
@@ -105,13 +109,17 @@ async function vibeServiceRequest(endpoint, options) {
105
109
  return { ok: true, status: 204, data: null };
106
110
  if (!res.ok) {
107
111
  const errorText = await res.text();
108
- return { ok: false, status: res.status, data: null, error: errorText };
112
+ const correlationId = (0, crypto_1.randomUUID)();
113
+ console.error(`[admin/analytics] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
114
+ return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
109
115
  }
110
116
  const body = await res.json();
111
117
  return { ok: true, status: res.status, data: body };
112
118
  }
113
119
  catch (error) {
114
- return { ok: false, status: 0, data: null, error: String(error) };
120
+ const correlationId = (0, crypto_1.randomUUID)();
121
+ console.error(`[admin/analytics] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
122
+ return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
115
123
  }
116
124
  }
117
125
  function getCountryFlag(countryCode) {
@@ -175,7 +183,7 @@ function createAnalyticsHandler(config) {
175
183
  async function getGeoAnalytics(startDate) {
176
184
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 5000 } });
177
185
  if (!result.ok) {
178
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
186
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
179
187
  }
180
188
  const sessions = result.data?.data || result.data?.documents || [];
181
189
  const recentSessions = sessions.filter((s) => new Date(s.created_at) >= startDate);
@@ -216,7 +224,7 @@ async function getGeoAnalytics(startDate) {
216
224
  async function getLoginAnalytics(startDate) {
217
225
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 5000 } });
218
226
  if (!result.ok) {
219
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
227
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
220
228
  }
221
229
  const sessions = result.data?.data || result.data?.documents || [];
222
230
  const recentSessions = sessions.filter((s) => new Date(s.created_at) >= startDate);
@@ -47,6 +47,7 @@ const auth_1 = require("../../server/auth");
47
47
  const startup_init_1 = require("../../lib/startup-init");
48
48
  const client_key_1 = require("../../lib/client-key");
49
49
  const roles_1 = require("../../lib/roles");
50
+ const crypto_1 = require("crypto");
50
51
  async function checkAdminRole(request) {
51
52
  const session = await (0, auth_1.getSession)(request);
52
53
  if (!session?.user) {
@@ -66,6 +67,9 @@ async function checkAdminRole(request) {
66
67
  return { isAdmin: true };
67
68
  }
68
69
  async function vibeServiceRequest(endpoint, options) {
70
+ // PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
71
+ // exception text. Failures return a fixed message + correlationId; the same id is on the
72
+ // server log line, which carries status + bodyLength only (PAY-1424).
69
73
  const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
70
74
  const clientId = process.env.VIBE_CLIENT_ID;
71
75
  const signingKey = process.env.VIBE_HMAC_KEY;
@@ -104,13 +108,17 @@ async function vibeServiceRequest(endpoint, options) {
104
108
  return { ok: true, status: 204, data: null };
105
109
  if (!res.ok) {
106
110
  const errorText = await res.text();
107
- return { ok: false, status: res.status, data: null, error: errorText };
111
+ const correlationId = (0, crypto_1.randomUUID)();
112
+ console.error(`[admin/audit] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
113
+ return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
108
114
  }
109
115
  const body = await res.json();
110
116
  return { ok: true, status: res.status, data: body };
111
117
  }
112
118
  catch (error) {
113
- return { ok: false, status: 0, data: null, error: String(error) };
119
+ const correlationId = (0, crypto_1.randomUUID)();
120
+ console.error(`[admin/audit] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
121
+ return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
114
122
  }
115
123
  }
116
124
  /**
@@ -157,7 +165,7 @@ function createAuditHandler(config) {
157
165
  }
158
166
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/audit_logs/query', { method: 'POST', body: queryBody });
159
167
  if (!result.ok) {
160
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
168
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
161
169
  }
162
170
  const rawLogs = result.data?.data || result.data?.documents || [];
163
171
  const logs = rawLogs.map((log) => ({
@@ -186,7 +194,7 @@ function createAuditHandler(config) {
186
194
  if (action === 'stats') {
187
195
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/audit_logs/query', { method: 'POST', body: { page: 1, pageSize: 10000 } });
188
196
  if (!result.ok) {
189
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
197
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
190
198
  }
191
199
  const logs = result.data?.data || result.data?.documents || [];
192
200
  const now = new Date();
@@ -18,6 +18,8 @@ const server_1 = require("next/server");
18
18
  const auth_1 = require("../../server/auth");
19
19
  const redis_1 = require("../../lib/redis");
20
20
  const roles_1 = require("../../lib/roles");
21
+ const crypto_1 = require("crypto");
22
+ const token_log_id_1 = require("../../lib/token-log-id");
21
23
  /**
22
24
  * Check if the current user has admin role
23
25
  */
@@ -76,7 +78,7 @@ function createRedisSessionsHandler(config) {
76
78
  const sessionToken = key.replace(sessionPrefix, '');
77
79
  sessions.push({
78
80
  id: sessionToken.substring(0, 16), // Truncated for display
79
- session_token: sessionToken.substring(0, 8) + '...', // Masked
81
+ session_token: (0, token_log_id_1.tokenLogId)(sessionToken), // Masked
80
82
  user_id: sessionData.userId,
81
83
  user_name: sessionData.name,
82
84
  user_email: sessionData.email,
@@ -112,8 +114,10 @@ function createRedisSessionsHandler(config) {
112
114
  });
113
115
  }
114
116
  catch (error) {
115
- console.error('[admin/redis-sessions] Error:', error);
116
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
117
+ // PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
118
+ const correlationId = (0, crypto_1.randomUUID)();
119
+ console.error('[admin/redis-sessions] Error:', { correlationId, error });
120
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
117
121
  }
118
122
  },
119
123
  async DELETE(request) {
@@ -137,12 +141,14 @@ function createRedisSessionsHandler(config) {
137
141
  return server_1.NextResponse.json({
138
142
  success: true,
139
143
  message: 'Session revoked',
140
- session_token: sessionToken.substring(0, 8) + '...',
144
+ session_token: (0, token_log_id_1.tokenLogId)(sessionToken),
141
145
  });
142
146
  }
143
147
  catch (error) {
144
- console.error('[admin/redis-sessions] DELETE Error:', error);
145
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
148
+ // PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
149
+ const correlationId = (0, crypto_1.randomUUID)();
150
+ console.error('[admin/redis-sessions] DELETE Error:', { correlationId, error });
151
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
146
152
  }
147
153
  },
148
154
  };
@@ -195,8 +201,10 @@ function createRedisSessionRevokeHandler(config) {
195
201
  });
196
202
  }
197
203
  catch (error) {
198
- console.error('[admin/redis-sessions/revoke] Error:', error);
199
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
204
+ // PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
205
+ const correlationId = (0, crypto_1.randomUUID)();
206
+ console.error('[admin/redis-sessions/revoke] Error:', { correlationId, error });
207
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
200
208
  }
201
209
  },
202
210
  };
@@ -48,6 +48,7 @@ const auth_1 = require("../../server/auth");
48
48
  const startup_init_1 = require("../../lib/startup-init");
49
49
  const client_key_1 = require("../../lib/client-key");
50
50
  const roles_1 = require("../../lib/roles");
51
+ const crypto_1 = require("crypto");
51
52
  /**
52
53
  * Check if the current user has admin role
53
54
  */
@@ -73,6 +74,9 @@ async function checkAdminRole(request) {
73
74
  * Make a service account request to Vibe (admin mode)
74
75
  */
75
76
  async function vibeServiceRequest(endpoint, options) {
77
+ // PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
78
+ // exception text. Failures return a fixed message + correlationId; the same id is on the
79
+ // server log line, which carries status + bodyLength only (PAY-1424).
76
80
  const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
77
81
  const clientId = process.env.VIBE_CLIENT_ID;
78
82
  const signingKey = process.env.VIBE_HMAC_KEY;
@@ -112,13 +116,17 @@ async function vibeServiceRequest(endpoint, options) {
112
116
  }
113
117
  if (!res.ok) {
114
118
  const errorText = await res.text();
115
- return { ok: false, status: res.status, data: null, error: errorText };
119
+ const correlationId = (0, crypto_1.randomUUID)();
120
+ console.error(`[admin/sessions] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
121
+ return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
116
122
  }
117
123
  const body = await res.json();
118
124
  return { ok: true, status: res.status, data: body };
119
125
  }
120
126
  catch (error) {
121
- return { ok: false, status: 0, data: null, error: String(error) };
127
+ const correlationId = (0, crypto_1.randomUUID)();
128
+ console.error(`[admin/sessions] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
129
+ return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
122
130
  }
123
131
  }
124
132
  // Country code to flag emoji mapping
@@ -167,7 +175,7 @@ function createSessionsHandler(config) {
167
175
  }
168
176
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: queryBody });
169
177
  if (!result.ok) {
170
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
178
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
171
179
  }
172
180
  // Extract sessions from response
173
181
  const rawSessions = result.data?.data || result.data?.documents || [];
@@ -202,7 +210,7 @@ function createSessionsHandler(config) {
202
210
  // Get all sessions for stats calculation
203
211
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/login_sessions/query', { method: 'POST', body: { page: 1, pageSize: 1000 } });
204
212
  if (!result.ok) {
205
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
213
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
206
214
  }
207
215
  const sessions = result.data?.data || result.data?.documents || [];
208
216
  // Calculate stats
@@ -239,7 +247,7 @@ function createSessionsHandler(config) {
239
247
  },
240
248
  });
241
249
  if (!result.ok) {
242
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
250
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
243
251
  }
244
252
  return server_1.NextResponse.json({ success: true });
245
253
  }
@@ -258,7 +266,7 @@ function createSessionsHandler(config) {
258
266
  },
259
267
  });
260
268
  if (!queryResult.ok) {
261
- return server_1.NextResponse.json({ error: queryResult.error }, { status: queryResult.status || 500 });
269
+ return server_1.NextResponse.json({ error: queryResult.error, code: 'UPSTREAM_ERROR', correlationId: queryResult.correlationId }, { status: queryResult.status || 500 });
262
270
  }
263
271
  const sessions = queryResult.data?.data || queryResult.data?.documents || [];
264
272
  let revokedCount = 0;
@@ -21,6 +21,7 @@ const server_1 = require("next/server");
21
21
  const auth_1 = require("../../server/auth");
22
22
  const redis_1 = require("../../lib/redis");
23
23
  const roles_1 = require("../../lib/roles");
24
+ const crypto_1 = require("crypto");
24
25
  const REDIS_SITE_LOG_KEY = 'vibe:site-logs:pending';
25
26
  const REDIS_LOG_TTL = 7 * 24 * 60 * 60; // 1 week
26
27
  /**
@@ -99,15 +100,21 @@ function createSiteLogsHandler(config) {
99
100
  const response = await fetch(`${vibeUrl}/v1/admin/site-logs?${params}`, { headers });
100
101
  if (!response.ok) {
101
102
  const errorText = await response.text();
102
- console.error('[admin/site-logs] Vibe error:', response.status, errorText);
103
- return server_1.NextResponse.json({ error: 'Failed to fetch site logs', details: errorText }, { status: response.status });
103
+ // PAY-1424: never the raw upstream body in the log sink.
104
+ // PAY-1425: nor in the response - a fixed message + code, and a correlationId that
105
+ // matches this log line.
106
+ const correlationId = (0, crypto_1.randomUUID)();
107
+ console.error('[admin/site-logs] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
108
+ return server_1.NextResponse.json({ error: 'Failed to fetch site logs', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
104
109
  }
105
110
  const data = await response.json();
106
111
  return server_1.NextResponse.json(data);
107
112
  }
108
113
  catch (error) {
109
- console.error('[admin/site-logs] Error:', error);
110
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
114
+ // PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
115
+ const correlationId = (0, crypto_1.randomUUID)();
116
+ console.error('[admin/site-logs] Error:', { correlationId, error });
117
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
111
118
  }
112
119
  },
113
120
  async POST(request) {
@@ -172,8 +179,10 @@ function createSiteLogsHandler(config) {
172
179
  }, { status: 201 });
173
180
  }
174
181
  catch (error) {
175
- console.error('[admin/site-logs] POST Error:', error);
176
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
182
+ // PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
183
+ const correlationId = (0, crypto_1.randomUUID)();
184
+ console.error('[admin/site-logs] POST Error:', { correlationId, error });
185
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
177
186
  }
178
187
  },
179
188
  };
@@ -206,16 +215,22 @@ function createSiteLogsStatsHandler(config) {
206
215
  const response = await fetch(`${vibeUrl}/v1/admin/site-logs/stats?${params}`, { headers });
207
216
  if (!response.ok) {
208
217
  const errorText = await response.text();
209
- console.error('[admin/site-logs/stats] Vibe error:', response.status, errorText);
210
- return server_1.NextResponse.json({ error: 'Failed to fetch stats', details: errorText }, { status: response.status });
218
+ // PAY-1424: never the raw upstream body in the log sink.
219
+ // PAY-1425: nor in the response - a fixed message + code, and a correlationId that
220
+ // matches this log line.
221
+ const correlationId = (0, crypto_1.randomUUID)();
222
+ console.error('[admin/site-logs/stats] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
223
+ return server_1.NextResponse.json({ error: 'Failed to fetch stats', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
211
224
  }
212
225
  const data = await response.json();
213
226
  // Ensure response matches expected format: { total, unique_users, by_level, by_category }
214
227
  return server_1.NextResponse.json(data);
215
228
  }
216
229
  catch (error) {
217
- console.error('[admin/site-logs/stats] Error:', error);
218
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
230
+ // PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
231
+ const correlationId = (0, crypto_1.randomUUID)();
232
+ console.error('[admin/site-logs/stats] Error:', { correlationId, error });
233
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
219
234
  }
220
235
  },
221
236
  };
@@ -246,15 +261,21 @@ function createSiteLogsDrainHandler(config) {
246
261
  });
247
262
  if (!response.ok) {
248
263
  const errorText = await response.text();
249
- console.error('[admin/site-logs/drain] Vibe error:', response.status, errorText);
250
- return server_1.NextResponse.json({ error: 'Failed to drain site logs', details: errorText }, { status: response.status });
264
+ // PAY-1424: never the raw upstream body in the log sink.
265
+ // PAY-1425: nor in the response - a fixed message + code, and a correlationId that
266
+ // matches this log line.
267
+ const correlationId = (0, crypto_1.randomUUID)();
268
+ console.error('[admin/site-logs/drain] Vibe error:', { correlationId, status: response.status, bodyLength: errorText.length });
269
+ return server_1.NextResponse.json({ error: 'Failed to drain site logs', code: 'UPSTREAM_ERROR', correlationId }, { status: response.status });
251
270
  }
252
271
  const data = await response.json();
253
272
  return server_1.NextResponse.json(data);
254
273
  }
255
274
  catch (error) {
256
- console.error('[admin/site-logs/drain] Error:', error);
257
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
275
+ // PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
276
+ const correlationId = (0, crypto_1.randomUUID)();
277
+ console.error('[admin/site-logs/drain] Error:', { correlationId, error });
278
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
258
279
  }
259
280
  },
260
281
  };
@@ -309,8 +330,10 @@ function createSiteLogsQueueHandler(config) {
309
330
  });
310
331
  }
311
332
  catch (error) {
312
- console.error('[admin/site-logs/queue] Error:', error);
313
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
333
+ // PAY-1425: exception text (hosts, URLs, driver messages) stays server-side.
334
+ const correlationId = (0, crypto_1.randomUUID)();
335
+ console.error('[admin/site-logs/queue] Error:', { correlationId, error });
336
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
314
337
  }
315
338
  },
316
339
  };
@@ -49,6 +49,7 @@ const startup_init_1 = require("../../lib/startup-init");
49
49
  const client_key_1 = require("../../lib/client-key");
50
50
  const redis_1 = require("../../lib/redis");
51
51
  const roles_1 = require("../../lib/roles");
52
+ const crypto_1 = require("crypto");
52
53
  async function checkAdminRole(request) {
53
54
  const session = await (0, auth_1.getSession)(request);
54
55
  if (!session?.user) {
@@ -68,6 +69,9 @@ async function checkAdminRole(request) {
68
69
  return { isAdmin: true };
69
70
  }
70
71
  async function vibeServiceRequest(endpoint, options) {
72
+ // PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
73
+ // exception text. Failures return a fixed message + correlationId; the same id is on the
74
+ // server log line, which carries status + bodyLength only (PAY-1424).
71
75
  const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
72
76
  const clientId = process.env.VIBE_CLIENT_ID;
73
77
  const signingKey = process.env.VIBE_HMAC_KEY;
@@ -105,13 +109,17 @@ async function vibeServiceRequest(endpoint, options) {
105
109
  return { ok: true, status: 204, data: null };
106
110
  if (!res.ok) {
107
111
  const errorText = await res.text();
108
- return { ok: false, status: res.status, data: null, error: errorText };
112
+ const correlationId = (0, crypto_1.randomUUID)();
113
+ console.error(`[admin/stats] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
114
+ return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
109
115
  }
110
116
  const body = await res.json();
111
117
  return { ok: true, status: res.status, data: body };
112
118
  }
113
119
  catch (error) {
114
- return { ok: false, status: 0, data: null, error: String(error) };
120
+ const correlationId = (0, crypto_1.randomUUID)();
121
+ console.error(`[admin/stats] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
122
+ return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
115
123
  }
116
124
  }
117
125
  /**
@@ -242,8 +250,10 @@ function createStatsHandler(config) {
242
250
  });
243
251
  }
244
252
  catch (error) {
245
- console.error('[admin/stats] Error:', error);
246
- return server_1.NextResponse.json({ error: error.message || 'Internal error' }, { status: 500 });
253
+ // PAY-1429: exception text (hosts, URLs, driver messages) stays server-side.
254
+ const correlationId = (0, crypto_1.randomUUID)();
255
+ console.error('[admin/stats] Error:', { correlationId, error });
256
+ return server_1.NextResponse.json({ error: 'Internal error', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
247
257
  }
248
258
  },
249
259
  };
@@ -47,6 +47,7 @@ const auth_1 = require("../../server/auth");
47
47
  const startup_init_1 = require("../../lib/startup-init");
48
48
  const client_key_1 = require("../../lib/client-key");
49
49
  const roles_1 = require("../../lib/roles");
50
+ const crypto_1 = require("crypto");
50
51
  async function checkAdminRole(request) {
51
52
  const session = await (0, auth_1.getSession)(request);
52
53
  if (!session?.user) {
@@ -66,6 +67,9 @@ async function checkAdminRole(request) {
66
67
  return { isAdmin: true };
67
68
  }
68
69
  async function vibeServiceRequest(endpoint, options) {
70
+ // PAY-1429: `error` can reach the HTTP caller, so it never carries upstream Vibe text or
71
+ // exception text. Failures return a fixed message + correlationId; the same id is on the
72
+ // server log line, which carries status + bodyLength only (PAY-1424).
69
73
  const idpUrl = process.env.NEXT_PUBLIC_IDP_URL || process.env.IDP_URL;
70
74
  const clientId = process.env.VIBE_CLIENT_ID;
71
75
  const signingKey = process.env.VIBE_HMAC_KEY;
@@ -104,13 +108,17 @@ async function vibeServiceRequest(endpoint, options) {
104
108
  return { ok: true, status: 204, data: null };
105
109
  if (!res.ok) {
106
110
  const errorText = await res.text();
107
- return { ok: false, status: res.status, data: null, error: errorText };
111
+ const correlationId = (0, crypto_1.randomUUID)();
112
+ console.error(`[admin/users] Vibe request failed: ${options.method} ${endpoint} - ${res.status}`, { correlationId, bodyLength: errorText.length });
113
+ return { ok: false, status: res.status, data: null, error: `Vibe request failed (HTTP ${res.status})`, correlationId };
108
114
  }
109
115
  const body = await res.json();
110
116
  return { ok: true, status: res.status, data: body };
111
117
  }
112
118
  catch (error) {
113
- return { ok: false, status: 0, data: null, error: String(error) };
119
+ const correlationId = (0, crypto_1.randomUUID)();
120
+ console.error(`[admin/users] Vibe request exception: ${options.method} ${endpoint}`, { correlationId, error });
121
+ return { ok: false, status: 0, data: null, error: 'Vibe request could not be completed', correlationId };
114
122
  }
115
123
  }
116
124
  /**
@@ -155,7 +163,7 @@ function createUsersHandler(config) {
155
163
  }
156
164
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/users/query', { method: 'POST', body: queryBody });
157
165
  if (!result.ok) {
158
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
166
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
159
167
  }
160
168
  const rawUsers = result.data?.data || result.data?.documents || [];
161
169
  const users = rawUsers.map((u) => ({
@@ -183,7 +191,7 @@ function createUsersHandler(config) {
183
191
  if (action === 'stats') {
184
192
  const result = await vibeServiceRequest('/v1/collections/vibe_app/tables/users/query', { method: 'POST', body: { page: 1, pageSize: 10000 } });
185
193
  if (!result.ok) {
186
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
194
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
187
195
  }
188
196
  const users = result.data?.data || result.data?.documents || [];
189
197
  const now = new Date();
@@ -205,14 +213,14 @@ function createUsersHandler(config) {
205
213
  if (action === 'update_tier' && userId && tier) {
206
214
  const result = await vibeServiceRequest(`/v1/collections/vibe_app/tables/users/${userId}`, { method: 'PUT', body: { tier } });
207
215
  if (!result.ok) {
208
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
216
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
209
217
  }
210
218
  return server_1.NextResponse.json({ success: true });
211
219
  }
212
220
  if (action === 'update_credits' && userId && credits !== undefined) {
213
221
  const result = await vibeServiceRequest(`/v1/collections/vibe_app/tables/users/${userId}`, { method: 'PUT', body: { credits } });
214
222
  if (!result.ok) {
215
- return server_1.NextResponse.json({ error: result.error }, { status: result.status || 500 });
223
+ return server_1.NextResponse.json({ error: result.error, code: 'UPSTREAM_ERROR', correlationId: result.correlationId }, { status: result.status || 500 });
216
224
  }
217
225
  return server_1.NextResponse.json({ success: true });
218
226
  }