@payez/next-mvp 4.11.0 → 4.12.1

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 (100) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/api-handlers/account/recovery/forward.js +2 -1
  3. package/dist/api-handlers/account/recovery/initiate.js +2 -1
  4. package/dist/api-handlers/account/reset-password.js +2 -1
  5. package/dist/api-handlers/account/send-code.js +2 -1
  6. package/dist/api-handlers/account/update-phone.js +2 -1
  7. package/dist/api-handlers/account/validate-password.js +2 -1
  8. package/dist/api-handlers/account/verify-email.js +2 -1
  9. package/dist/api-handlers/account/verify-sms.js +2 -1
  10. package/dist/api-handlers/anon/preferences.js +3 -2
  11. package/dist/api-handlers/auth/credential-login-email-code.js +3 -2
  12. package/dist/api-handlers/auth/credential-login.js +6 -5
  13. package/dist/api-handlers/auth/refresh.js +2 -1
  14. package/dist/api-handlers/auth/signout.js +43 -6
  15. package/dist/api-handlers/mfa/backup-code-verify.js +2 -1
  16. package/dist/api-handlers/mfa/totp-verify.js +2 -1
  17. package/dist/api-handlers/session/refresh-viability.js +3 -2
  18. package/dist/api-handlers/session/viability.js +2 -1
  19. package/dist/auth/better-auth.js +7 -6
  20. package/dist/auth/idp-oauth.js +4 -2
  21. package/dist/auth/utils/idp-client.js +5 -4
  22. package/dist/auth/utils/token-utils.js +4 -3
  23. package/dist/config/logger.js +2 -1
  24. package/dist/config/logging-config.js +4 -3
  25. package/dist/config/vibe-log-transport.js +2 -1
  26. package/dist/lib/ensure-fresh-access-token.js +17 -11
  27. package/dist/lib/geolocation.js +5 -22
  28. package/dist/lib/idp-client-config.js +4 -3
  29. package/dist/lib/idp-fetch.d.ts +11 -0
  30. package/dist/lib/idp-fetch.js +3 -1
  31. package/dist/lib/idp-logout.d.ts +10 -0
  32. package/dist/lib/idp-logout.js +73 -1
  33. package/dist/lib/jwt-decode-client.js +2 -1
  34. package/dist/lib/jwt-decode.js +2 -1
  35. package/dist/lib/log-safe.d.ts +19 -0
  36. package/dist/lib/log-safe.js +32 -0
  37. package/dist/lib/redis.js +2 -1
  38. package/dist/lib/safe-error-response.d.ts +5 -1
  39. package/dist/lib/safe-error-response.js +7 -2
  40. package/dist/lib/session-store.js +2 -1
  41. package/dist/lib/site-logger.d.ts +2 -8
  42. package/dist/lib/site-logger.js +11 -5
  43. package/dist/lib/standardized-client-api.js +3 -2
  44. package/dist/lib/token-lifecycle.js +3 -2
  45. package/dist/middleware/create-middleware.js +2 -1
  46. package/dist/middleware/rbac-check.d.ts +0 -13
  47. package/dist/middleware/rbac-check.js +6 -5
  48. package/dist/routes/auth/logout.js +6 -2
  49. package/dist/routes/auth/settings.js +2 -1
  50. package/dist/server/auth-guard.js +4 -3
  51. package/dist/server/auth.js +5 -9
  52. package/dist/server/decode-session.js +3 -2
  53. package/dist/server/with-auth.js +2 -1
  54. package/package.json +1 -1
  55. package/src/api-handlers/account/recovery/forward.ts +2 -1
  56. package/src/api-handlers/account/recovery/initiate.ts +2 -1
  57. package/src/api-handlers/account/reset-password.ts +2 -1
  58. package/src/api-handlers/account/send-code.ts +2 -1
  59. package/src/api-handlers/account/update-phone.ts +2 -1
  60. package/src/api-handlers/account/validate-password.ts +2 -1
  61. package/src/api-handlers/account/verify-email.ts +2 -1
  62. package/src/api-handlers/account/verify-sms.ts +2 -1
  63. package/src/api-handlers/anon/preferences.ts +3 -2
  64. package/src/api-handlers/auth/credential-login-email-code.ts +3 -2
  65. package/src/api-handlers/auth/credential-login.ts +6 -5
  66. package/src/api-handlers/auth/refresh.ts +2 -1
  67. package/src/api-handlers/auth/signout.ts +54 -7
  68. package/src/api-handlers/mfa/backup-code-verify.ts +2 -1
  69. package/src/api-handlers/mfa/totp-verify.ts +2 -1
  70. package/src/api-handlers/session/refresh-viability.ts +3 -2
  71. package/src/api-handlers/session/viability.ts +2 -1
  72. package/src/auth/better-auth.ts +7 -6
  73. package/src/auth/idp-oauth.ts +7 -2
  74. package/src/auth/utils/idp-client.ts +5 -4
  75. package/src/auth/utils/token-utils.ts +4 -3
  76. package/src/config/logger.ts +2 -1
  77. package/src/config/logging-config.ts +4 -3
  78. package/src/config/vibe-log-transport.ts +2 -1
  79. package/src/lib/ensure-fresh-access-token.ts +17 -11
  80. package/src/lib/geolocation.ts +5 -4
  81. package/src/lib/idp-client-config.ts +4 -3
  82. package/src/lib/idp-fetch.ts +11 -1
  83. package/src/lib/idp-logout.ts +36 -1
  84. package/src/lib/jwt-decode-client.ts +3 -1
  85. package/src/lib/jwt-decode.ts +2 -1
  86. package/src/lib/log-safe.ts +30 -0
  87. package/src/lib/redis.ts +2 -1
  88. package/src/lib/safe-error-response.ts +7 -2
  89. package/src/lib/session-store.ts +2 -1
  90. package/src/lib/site-logger.ts +13 -7
  91. package/src/lib/standardized-client-api.ts +3 -2
  92. package/src/lib/token-lifecycle.ts +3 -2
  93. package/src/middleware/create-middleware.ts +2 -1
  94. package/src/middleware/rbac-check.ts +2 -1
  95. package/src/routes/auth/logout.ts +9 -2
  96. package/src/routes/auth/settings.ts +2 -1
  97. package/src/server/auth-guard.ts +4 -3
  98. package/src/server/auth.ts +5 -10
  99. package/src/server/decode-session.ts +3 -2
  100. package/src/server/with-auth.ts +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog — @payez/next-mvp
2
2
 
3
+ ## 4.12.1 (to be published under dist-tag `next` only, after 4.12.0; `latest` stays 4.8.9 until Jon says otherwise)
4
+
5
+ A patch cut from the 4.12.0 tree carrying ONE change: the PAY-2425 hygiene fix below (PR 1500).
6
+
7
+ - **CHANGE (PAY-2425, hygiene): the refresh no longer invents authentication claims for an OAuth (Google) session.** `lib/ensure-fresh-access-token` answered an OAuth session whose stored authentication methods were empty with `amr ['pwd','mfa']`, `two_factor_method 'oauth'` and an `acr` raised from '1' to '2' ("OAuth IS a form of multi-factor auth"), and echoed a stored `mfaVerified` as `two_factor_verified` whatever the session's methods said. OAuth is federated sign-in, not a second factor (BAPert 77404). The refresh body now carries only what the session records: empty stays empty, `acr` is the session's own level, no method is named from silence, and `two_factor_verified` is sent only when the session's recorded methods contain `mfa`, `sms`, `totp` or `email`. A session that really attests `mfa` still sends it. The IdP ignores these request fields and mints `amr`/`acr` from the stored refresh row, so this removes a false claim from the wire; it does not establish that any bypass existed or is closed. **What changes for a caller:** a Google user on a `require2FA` client is asked for a code, as before, because the IdP decides that; a Google-only client that wants no second factor sets `require2FA = false` explicitly. An earlier version of this fix on `nextpert/375639-amr-fabrication` (e6e3b6c3, e3b3bb12) was never merged; the refresh path has since moved into this helper. Rows: `test/next-mvp-pay-2425-no-fabricated-mfa.test.cjs` (R1/R2/R3, C1, N1 and four mutants).
8
+
9
+ ## 4.12.0 (to be published under dist-tag `next` only, after 4.11.0; `latest` stays 4.8.9 until Jon says otherwise)
10
+
11
+ A minor: PII-in-logs groups 1, 2, 3a and 3b, the GOV-023 signout BREAKING change, the logout-401 FIX (PAY-2396), and the BREAKING site-logger signatures (3b: callers of `siteEvents.loginFailed` / `siteEvents.error` must change).
12
+
13
+
14
+ - **BREAKING: `siteEvents.loginFailed` and `siteEvents.error` (`lib/site-logger`) lose their free-text parameters (PII-in-logs group 3b, BAPert 76553 Q2).** `loginFailed({ reason, url?, user_agent?, ip_address? })` no longer takes `email`; the `reason` is stored only if it is a closed UPPER_SNAKE code, anything else is stored as `UNSPECIFIED`. `error({ code, user_id?, url?, user_agent?, ip_address? })` takes a closed `code` instead of `message`, `error` and `stack`; the stored message is the fixed sentence `Application error` and the code goes in `context.code`. The parameters are removed from the TYPE, so a TypeScript caller that still passes `email`, `message`, `error` or `stack` fails to compile, which is the point. **Migration:** replace `loginFailed({ reason: 'bad password', email })` with `loginFailed({ reason: 'BAD_PASSWORD' })` (look the user up by `user_id` elsewhere if support needs it), and `error({ message: err.message, stack: err.stack })` with `error({ code: 'YOUR_CLOSED_CODE' })`. Callers found: the package itself calls only `siteEvents.logout`; VibeID-Customer-Portal 0; idealvibe.online 0 `siteEvents.*` (it has its own copy of `lib/site-logger.ts`, which keeps the old shapes until its own caller PR); UMI-Website 0 (not verified against its repo). Row: `test/next-mvp-pii-logs-group3b.test.cjs`.
15
+
16
+ - **BREAKING: `api-handlers/auth/signout` now ends the IdP grant, not only the local session (GOV-023, BAPert 76687).** It used to delete the local record only and make no IdP call, so an app that mounted it left the IdP refresh token live until it expired (`routes/auth/logout` already revoked first). It now runs the same IdP-first path: `revokeSessionAtIdp`, then the local delete, then the cookies. If the IdP revoke fails, or cannot resolve the session while a refresh token is held, or the local delete fails, it answers **HTTP 500** `{ success: false, code: 'SESSION_NOT_ENDED', cause: 'idp_unconfirmed' | 'local_delete_failed', message }` (the same code, causes and sentences as `routes/auth/logout`; both failing: the local cause) instead of `success: true`; the local delete and the cookie clearing still happen. A caller that treated every signout answer as success must now read `success` / `cause`. Ships in the release after 4.11.0 (with the site-logger signature change). Rows: `test/next-mvp-gov023-signout-ends-idp-grant.test.cjs`.
17
+ - **CHANGE: a caught error is logged by its NAME and a closed code/status, never its message (PII-in-logs group 1, BAPert 76553).** New `lib/log-safe` (`errForLog`, `closedCodeForLog`, internal, not in the exports map): `error=<Name> [status=<100-599>] [code=<CLOSED_TOKEN>]`; `message`, `stack`, `cause` and `details` never appear, and a name, status or code that is not a closed shape is dropped. Applied to the shared sites with the widest reach: `logExceptionWithCorrelation` (14 handlers; the line keeps the `correlationId`, the logged `error` is now the string above instead of the raw error object), the four catches in `auth/utils/idp-client` (login, refresh, verify-2FA, send-2FA-code), `api-handlers/session/refresh-viability`'s catch (it logged the exception message next to the correlation id; found by the Mac run of `user-facing-error-not-returned`), and `server/auth`'s `SESSION_READ_SWALLOWED` line (it used to log a redacted, 80-character-truncated message; it now logs `error=<Name>` only). Operators lose the exception text in these log lines on purpose; the correlation id still ties a response to its line. Rows: `test/next-mvp-pii-logs-group1.test.cjs`; `test/pay-2136-no-session-origin.test.cjs` and `test/user-facing-error-not-returned.test.cjs` now assert the opposite of what they did (the message must NOT be in the log).
18
+ - **CHANGE: more caught errors are logged by NAME and closed code/status, not by message (PII-in-logs group 2, BAPert 76767).** The catches in `api-handlers/account/*` (send-code, update-phone, validate-password, verify-email, verify-sms, reset-password, recovery initiate and forward), `api-handlers/mfa/*` (backup-code-verify, totp-verify), `auth/utils/token-utils` (three sites) and `lib/session-store` now log `errForLog(err)` instead of the error object. The response bodies do not change. Row: `test/next-mvp-pii-logs-group2.test.cjs` (a source scan of those 12 files, plus a real-dist token-decode failure whose canary must not reach a log line).
19
+ - **CHANGE: the rest of the server-side caught-error logs use `errForLog` (PII-in-logs group 3a, BAPert 76955).** `api-handlers` (anon preferences, credential-login and its email-code lane, refresh, session viability), `auth` (better-auth, idp-oauth), `lib` (ensure-fresh-access-token, idp-client-config, jwt-decode, redis, site-logger, standardized-client-api, token-lifecycle), `config` (logger, logging-config, vibe-log-transport), `routes/auth` (logout, settings), `server` (auth-guard, decode-session, with-auth) and `middleware` now log the error's name and a closed status/code, not `err`, `err.message` or `String(err)`. `lib/geolocation` no longer logs the IP or the upstream's `data.message` (Q1). The `payez-idp access token refused` line now reads `error=Error code=RESPONSE_ADDRESS_MASKED` or `code=NO_USABLE_EMAIL` (a closed code on the two refusals thrown in `auth/idp-oauth`, BAPert 76998) instead of the reason sentence, so an operator still sees WHY and no message text is logged. Response bodies do not change. Browser code (client, components, hooks, stores) is untouched. Row: `test/next-mvp-pii-logs-group3a.test.cjs`.
20
+ - **FIX: a sign-out whose logout answers 401 now forces one REAL refresh, so a dead grant is no longer reported as "could not confirm the sign-out" (BAPert 77013, QAPert 77012).** After a password change, an admin revoke or a sign-out elsewhere, Core has invalidated the access token while the stored copy still looks fresh. `POST /api/Account/logout` then answered 401, the refresh the 401 triggers answered "already fresh" without asking the IdP, so no discard marker was written and `revokeSessionAtIdp` threw: the route answered `SESSION_NOT_ENDED` / `idp_unconfirmed` and logged "the IdP refresh token is STILL LIVE" for a grant that was already dead. Now `revokeSessionAtIdp` makes the refresh `idpFetchJSON` runs after a 401 a FORCED one, done in-process (`refreshOnAuthFailure` calls `ensureFreshAccessToken` with a window that cannot be "fresh", config from `IDP_URL` / `CLIENT_ID` as `oauth/resume` reads it). Nothing changes on the public `POST /api/auth/refresh`: no header can force a refresh (NightHawk 77021, BAPert 77022). The IdP's answer decides: a refresh 401 with the discard marker is `grant-already-dead` (a successful sign-out); a refresh that succeeds while the logout is still refused means the grant is live and stays `idp_unconfirmed`. The failure is a typed `IdpLogoutUnconfirmedError` (`code` `IDP_LOGOUT_UNCONFIRMED`, `grantLive`); `routes/auth/logout` and `api-handlers/auth/signout` print "STILL LIVE" only when `grantLive` is true and otherwise say the revoke could not be confirmed. `api-handlers/auth/signout` also logs its revoke and session-cleanup errors through `errForLog` now (they passed the raw error). Rows: `test/next-mvp-logout-401-forces-refresh.test.cjs`. A real, unstubbed row (a real password change must land on `notice=password-changed`) is QAPert's walk.
21
+
3
22
  ## 4.11.0 (to be published under dist-tag `next` only, after 4.10.0; `latest` stays 4.8.9 until Jon says otherwise)
4
23
 
5
24
  Carries PRs 1443, 1444, 1445, 1446, 1451, 1452, 1460 and 1461 (and the 1450 design doc): the refresh consolidation (the refresh route is now a thin wrapper over `ensureFreshAccessToken`), the one freshness window, the fetch timeout with the in-doubt marker, and the trace-id cleanup. A minor, because response codes and bodies that callers may match on changed (BREAKING below).
@@ -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 safe_error_response_1 = require("../../../lib/safe-error-response");
10
+ const log_safe_1 = require("../../../lib/log-safe");
10
11
  /**
11
12
  * Account recovery - the ONE forwarder behind /recovery/send-code and /recovery/verify-code (PAY-1588).
12
13
  *
@@ -61,7 +62,7 @@ async function recoveryClientKey(where) {
61
62
  return (0, client_key_1.resolveClientKey)(config, where).key;
62
63
  }
63
64
  catch (e) {
64
- console.error(`[${where}] could not resolve IDP client config for client_id:`, e);
65
+ console.error(`[${where}] could not resolve IDP client config for client_id:`, (0, log_safe_1.errForLog)(e));
65
66
  return null;
66
67
  }
67
68
  }
@@ -5,6 +5,7 @@ const server_1 = require("next/server");
5
5
  const idp_client_config_1 = require("../../../lib/idp-client-config");
6
6
  const client_key_1 = require("../../../lib/client-key");
7
7
  const safe_error_response_1 = require("../../../lib/safe-error-response");
8
+ const log_safe_1 = require("../../../lib/log-safe");
8
9
  function getIdpUrl() {
9
10
  const url = process.env.IDP_URL;
10
11
  if (!url)
@@ -37,7 +38,7 @@ async function recoveryClientKey() {
37
38
  return (0, client_key_1.resolveClientKey)(config, 'account/recovery/initiate').key;
38
39
  }
39
40
  catch (e) {
40
- console.error('[account/recovery/initiate] could not resolve IDP client config for client_id:', e);
41
+ console.error('[account/recovery/initiate] could not resolve IDP client config for client_id:', (0, log_safe_1.errForLog)(e));
41
42
  return null;
42
43
  }
43
44
  }
@@ -5,6 +5,7 @@ const server_1 = require("next/server");
5
5
  const idp_client_config_1 = require("../../lib/idp-client-config");
6
6
  const client_key_1 = require("../../lib/client-key");
7
7
  const safe_error_response_1 = require("../../lib/safe-error-response");
8
+ const log_safe_1 = require("../../lib/log-safe");
8
9
  function getIdpUrl() {
9
10
  const url = process.env.IDP_URL;
10
11
  if (!url)
@@ -37,7 +38,7 @@ async function recoveryClientKey() {
37
38
  return (0, client_key_1.resolveClientKey)(config, 'account/reset-password').key;
38
39
  }
39
40
  catch (e) {
40
- console.error('[account/reset-password] could not resolve IDP client config for client_id:', e);
41
+ console.error('[account/reset-password] could not resolve IDP client config for client_id:', (0, log_safe_1.errForLog)(e));
41
42
  return null;
42
43
  }
43
44
  }
@@ -13,6 +13,7 @@ const server_1 = require("next/server");
13
13
  const idp_fetch_1 = require("../../lib/idp-fetch");
14
14
  const env_1 = require("../../config/env");
15
15
  const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
16
+ const log_safe_1 = require("../../lib/log-safe");
16
17
  async function POST(req) {
17
18
  try {
18
19
  // Parse request body
@@ -47,7 +48,7 @@ async function POST(req) {
47
48
  }, { status: 200 });
48
49
  }
49
50
  catch (error) {
50
- console.error('[SEND_CODE] Error:', error);
51
+ console.error('[SEND_CODE] Error:', (0, log_safe_1.errForLog)(error));
51
52
  return server_1.NextResponse.json({
52
53
  success: false,
53
54
  error: 'Failed to send verification code',
@@ -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 log_safe_1 = require("../../lib/log-safe");
7
8
  /**
8
9
  * Update Phone Number API Handler
9
10
  *
@@ -63,7 +64,7 @@ async function POST(req) {
63
64
  });
64
65
  }
65
66
  catch (error) {
66
- console.error('[UPDATE_PHONE] Error:', error);
67
+ console.error('[UPDATE_PHONE] Error:', (0, log_safe_1.errForLog)(error));
67
68
  return server_1.NextResponse.json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to update phone number' } }, { status: 500 });
68
69
  }
69
70
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.POST = POST;
4
4
  const server_1 = require("next/server");
5
+ const log_safe_1 = require("../../lib/log-safe");
5
6
  async function POST(req) {
6
7
  try {
7
8
  const body = await req.json();
@@ -88,7 +89,7 @@ async function POST(req) {
88
89
  });
89
90
  }
90
91
  catch (error) {
91
- console.error('[VALIDATE_PASSWORD] Error:', error);
92
+ console.error('[VALIDATE_PASSWORD] Error:', (0, log_safe_1.errForLog)(error));
92
93
  return server_1.NextResponse.json({
93
94
  is_valid: false,
94
95
  score: 0,
@@ -16,6 +16,7 @@ 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
18
  const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
19
+ const log_safe_1 = require("../../lib/log-safe");
19
20
  async function POST(req) {
20
21
  try {
21
22
  // Parse request body
@@ -108,7 +109,7 @@ async function POST(req) {
108
109
  }, { status: 200 });
109
110
  }
110
111
  catch (error) {
111
- console.error('[VERIFY_EMAIL] Error:', error);
112
+ console.error('[VERIFY_EMAIL] Error:', (0, log_safe_1.errForLog)(error));
112
113
  return server_1.NextResponse.json({
113
114
  success: false,
114
115
  error: 'Failed to verify code',
@@ -16,6 +16,7 @@ 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
18
  const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
19
+ const log_safe_1 = require("../../lib/log-safe");
19
20
  async function POST(req) {
20
21
  try {
21
22
  // Parse request body
@@ -108,7 +109,7 @@ async function POST(req) {
108
109
  }, { status: 200 });
109
110
  }
110
111
  catch (error) {
111
- console.error('[VERIFY_SMS] Error:', error);
112
+ console.error('[VERIFY_SMS] Error:', (0, log_safe_1.errForLog)(error));
112
113
  return server_1.NextResponse.json({
113
114
  success: false,
114
115
  error: 'Failed to verify code',
@@ -13,6 +13,7 @@ exports.POST = POST;
13
13
  const server_1 = require("next/server");
14
14
  const headers_1 = require("next/headers");
15
15
  const anon_session_1 = require("../../lib/anon-session");
16
+ const log_safe_1 = require("../../lib/log-safe");
16
17
  const COOKIE_MAX_AGE = 90 * 24 * 60 * 60; // 90 days in seconds
17
18
  /**
18
19
  * GET handler - retrieves anonymous session preferences
@@ -45,7 +46,7 @@ async function GET(request) {
45
46
  return response;
46
47
  }
47
48
  catch (error) {
48
- console.error('[ANON-PREFERENCES] GET error:', error);
49
+ console.error('[ANON-PREFERENCES] GET error:', (0, log_safe_1.errForLog)(error));
49
50
  return server_1.NextResponse.json({ success: false, error: 'Failed to get preferences' }, { status: 500 });
50
51
  }
51
52
  }
@@ -90,7 +91,7 @@ async function POST(request) {
90
91
  return response;
91
92
  }
92
93
  catch (error) {
93
- console.error('[ANON-PREFERENCES] POST error:', error);
94
+ console.error('[ANON-PREFERENCES] POST error:', (0, log_safe_1.errForLog)(error));
94
95
  return server_1.NextResponse.json({ success: false, error: 'Failed to update preferences' }, { status: 500 });
95
96
  }
96
97
  }
@@ -5,6 +5,7 @@ const server_1 = require("next/server");
5
5
  const idp_client_config_1 = require("../../lib/idp-client-config");
6
6
  const client_key_1 = require("../../lib/client-key");
7
7
  const bearer_fetch_1 = require("../../lib/bearer-fetch");
8
+ const log_safe_1 = require("../../lib/log-safe");
8
9
  // `export const runtime = 'nodejs'` must be written LITERALLY in the app's route file (Next ignores a re-exported segment config).
9
10
  /** One email-code request per this many seconds per browser (the IdP rate-limits too; this stops a double click and a loop). */
10
11
  const EMAIL_CODE_COOLDOWN_S = 30;
@@ -39,7 +40,7 @@ async function POST(req) {
39
40
  clientKey = (0, client_key_1.resolveClientKey)(config, 'graft/admin-login/email-code').key ?? '';
40
41
  }
41
42
  catch (err) {
42
- console.error('[ADMIN_LOGIN_EMAIL_CODE] IDP client config unavailable:', err instanceof Error ? err.message : String(err));
43
+ console.error('[ADMIN_LOGIN_EMAIL_CODE] IDP client config unavailable:', (0, log_safe_1.errForLog)(err));
43
44
  return server_1.NextResponse.json({ success: false, error: 'Sign-in is temporarily unavailable. This is a problem on our side. Please try again shortly.' }, { status: 503 });
44
45
  }
45
46
  let sendOk = false;
@@ -70,7 +71,7 @@ async function POST(req) {
70
71
  }
71
72
  }
72
73
  catch (err) {
73
- console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send fetch failed:', err instanceof Error ? err.message : String(err));
74
+ console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send fetch failed:', (0, log_safe_1.errForLog)(err));
74
75
  }
75
76
  if (!sendOk) {
76
77
  return server_1.NextResponse.json({ success: false, error: 'We could not ask for an email code just now - this is a problem on our side. Try again shortly.' }, { status: 502 });
@@ -50,6 +50,7 @@ const crypto_1 = require("crypto");
50
50
  const device_id_1 = require("../../lib/device-id");
51
51
  const twofa_rejection_1 = require("../../lib/twofa-rejection");
52
52
  const bearer_fetch_1 = require("../../lib/bearer-fetch");
53
+ const log_safe_1 = require("../../lib/log-safe");
53
54
  // `export const runtime = 'nodejs'` must be written LITERALLY in the app's route file (Next ignores a re-exported segment config).
54
55
  /**
55
56
  * The device id to forward to the IDP for THIS caller — card 240566.
@@ -171,7 +172,7 @@ async function POST(req) {
171
172
  config = await (0, idp_client_config_1.getIDPClientConfig)();
172
173
  }
173
174
  catch (err) {
174
- console.error('[ADMIN_LOGIN] IDP client config unavailable — returning 503:', err);
175
+ console.error('[ADMIN_LOGIN] IDP client config unavailable — returning 503:', (0, log_safe_1.errForLog)(err));
175
176
  return server_1.NextResponse.json({
176
177
  success: false,
177
178
  error: 'Sign-in is temporarily unavailable. This is a problem on our side, not with your details. Please try again shortly.',
@@ -265,7 +266,7 @@ async function POST(req) {
265
266
  }
266
267
  }
267
268
  catch (error) {
268
- console.error('[ADMIN-LOGIN] twofa verify fetch failed:', error instanceof Error ? error.message : String(error));
269
+ console.error('[ADMIN-LOGIN] twofa verify fetch failed:', (0, log_safe_1.errForLog)(error));
269
270
  return server_1.NextResponse.json({ success: false, error: 'Unable to reach the authentication service' }, { status: 503 });
270
271
  }
271
272
  if (!idpResponse.ok) {
@@ -350,7 +351,7 @@ async function POST(req) {
350
351
  });
351
352
  }
352
353
  catch (error) {
353
- console.error('[ADMIN-LOGIN] IDP fetch failed:', error instanceof Error ? error.message : String(error));
354
+ console.error('[ADMIN-LOGIN] IDP fetch failed:', (0, log_safe_1.errForLog)(error));
354
355
  return server_1.NextResponse.json({ success: false, error: 'Unable to reach the authentication service' }, { status: 503 });
355
356
  }
356
357
  const rawText = await idpResponse.text();
@@ -446,7 +447,7 @@ async function POST(req) {
446
447
  }
447
448
  }
448
449
  catch (err) {
449
- console.warn('[ADMIN-LOGIN] mfa/methods fetch failed, defaulting to email lane:', err instanceof Error ? err.message : String(err));
450
+ console.warn('[ADMIN-LOGIN] mfa/methods fetch failed, defaulting to email lane:', (0, log_safe_1.errForLog)(err));
450
451
  }
451
452
  }
452
453
  // PAY-2314: advertise what this user can actually use, derived from the SAME usableFactors that chose the lane
@@ -523,7 +524,7 @@ async function POST(req) {
523
524
  }
524
525
  }
525
526
  catch (err) {
526
- console.error(`[ADMIN-LOGIN] twofa/${sendLane}/send fetch failed:`, err instanceof Error ? err.message : String(err));
527
+ console.error(`[ADMIN-LOGIN] twofa/${sendLane}/send fetch failed:`, (0, log_safe_1.errForLog)(err));
527
528
  }
528
529
  }
529
530
  // 267479: `method:'email'` WAS A HARDCODED LITERAL HERE, and it was a lie for
@@ -29,6 +29,7 @@ const auth_1 = require("../../server/auth");
29
29
  const ensure_fresh_access_token_1 = require("../../lib/ensure-fresh-access-token");
30
30
  const refresh_in_doubt_1 = require("../../lib/refresh-in-doubt");
31
31
  const safe_trace_id_1 = require("../../lib/safe-trace-id");
32
+ const log_safe_1 = require("../../lib/log-safe");
32
33
  /** E's failure -> this route's body and status. Bodies for the cases the route always had are exactly as they were. */
33
34
  function failureResponse(r) {
34
35
  switch (r.code) {
@@ -128,7 +129,7 @@ function createRefreshHandler(config) {
128
129
  });
129
130
  }
130
131
  catch (err) {
131
- console.error('[AUTH_REFRESH] Unexpected error', { error: err?.message || String(err) });
132
+ console.error('[AUTH_REFRESH] Unexpected error', { error: (0, log_safe_1.errForLog)(err) });
132
133
  return server_1.NextResponse.json({
133
134
  error: 'Unexpected error during refresh',
134
135
  code: 'INTERNAL_SERVER_ERROR'
@@ -15,6 +15,9 @@ exports.createSignoutHandler = createSignoutHandler;
15
15
  const server_1 = require("next/server");
16
16
  const headers_1 = require("next/headers");
17
17
  const session_store_1 = require("../../lib/session-store");
18
+ const idp_logout_1 = require("../../lib/idp-logout");
19
+ const log_safe_1 = require("../../lib/log-safe");
20
+ const logout_1 = require("../../routes/auth/logout");
18
21
  const auth_1 = require("../../server/auth");
19
22
  const app_slug_1 = require("../../lib/app-slug");
20
23
  const token_log_id_1 = require("../../lib/token-log-id");
@@ -130,9 +133,39 @@ function createSignoutHandler() {
130
133
  console.warn('[SIGNOUT] Manual JWT decode failed to extract session token');
131
134
  }
132
135
  }
133
- // Delete the session from EVERY store (181922): canonical + BA secondary.
134
- // Canonical-only deletion let the ba: record resurrect the session.
136
+ // GOV-023 (BAPert 76687): ending the session ends the IdP GRANT first, the same IdP-first path as routes/auth/logout. Before this, this
137
+ // handler only deleted the local record, so a consumer who mounted it left the IdP refresh token live until it expired.
138
+ // A failed or unresolved revoke is the same typed failure as a failed local delete (SESSION_NOT_ENDED + cause); the local delete still
139
+ // runs and the cookies are cleared either way.
140
+ let idpRevokeFailed = false;
141
+ let localDeleteFailed = false;
135
142
  if (redisSessionToken) {
143
+ // Does the stored session hold an IdP refresh token? (revokeSessionAtIdp reads the canonical row, then the Better Auth record.)
144
+ let heldRefreshToken = false;
145
+ try {
146
+ const stored = (await (0, session_store_1.getSession)(redisSessionToken)) ?? (await (0, session_store_1.getBetterAuthSession)(redisSessionToken));
147
+ heldRefreshToken = !!stored?.idpRefreshToken;
148
+ }
149
+ catch {
150
+ // unknown: treated as "none held", so an unreadable store does not turn a clean sign-out into a failure on its own
151
+ }
152
+ try {
153
+ const outcome = await (0, idp_logout_1.revokeSessionAtIdp)(req);
154
+ // 'nothing-to-revoke' while a refresh token IS held means the helper could not resolve this session from the request (this
155
+ // handler also finds the session by decoding the cookie when Better Auth cannot): the grant is NOT ended, so say so.
156
+ if (outcome === 'nothing-to-revoke' && heldRefreshToken) {
157
+ idpRevokeFailed = true;
158
+ console.error('[SIGNOUT] IdP revoke could not resolve the session - the IdP refresh token is STILL LIVE');
159
+ }
160
+ }
161
+ catch (revokeError) {
162
+ idpRevokeFailed = true;
163
+ console.error(revokeError?.grantLive === true
164
+ ? '[SIGNOUT] IdP revoke failed - the IdP refresh token is STILL LIVE:'
165
+ : '[SIGNOUT] IdP revoke could not be confirmed (the grant may already be dead):', (0, log_safe_1.errForLog)(revokeError));
166
+ }
167
+ // Delete the session from EVERY store (181922): canonical + BA secondary.
168
+ // Canonical-only deletion let the ba: record resurrect the session.
136
169
  try {
137
170
  await (0, session_store_1.deleteSessionEverywhere)(redisSessionToken);
138
171
  console.info('[SIGNOUT] Redis session cleanup successful', {
@@ -140,21 +173,25 @@ function createSignoutHandler() {
140
173
  });
141
174
  }
142
175
  catch (sessionError) {
143
- // Log error but don't fail the signout process
144
- console.error('[SIGNOUT] Redis session cleanup failed', { error: sessionError });
176
+ localDeleteFailed = true;
177
+ console.error('[SIGNOUT] Redis session cleanup failed - the server session is STILL LIVE:', (0, log_safe_1.errForLog)(sessionError));
145
178
  }
146
179
  }
147
180
  else {
148
181
  console.warn('[SIGNOUT] No Redis session token (UUID) extracted from cookie; skipping Redis deletion');
149
182
  }
150
- // Build response
183
+ // Build response. A sign-out that did not end the session must not say it did (same typed answer as routes/auth/logout).
151
184
  const responseData = {
152
185
  success: true,
153
186
  message: sessionToken ? 'Session deleted successfully' : 'No active session found',
154
187
  sessionDeleted: !!sessionToken,
155
188
  chunkCookiesDeleted: chunkCookies.length
156
189
  };
157
- const response = server_1.NextResponse.json(responseData);
190
+ const response = (localDeleteFailed || idpRevokeFailed)
191
+ ? server_1.NextResponse.json(localDeleteFailed
192
+ ? { success: false, code: logout_1.SESSION_NOT_ENDED_CODE, cause: 'local_delete_failed', message: logout_1.SESSION_NOT_ENDED_LOCAL_MESSAGE }
193
+ : { success: false, code: logout_1.SESSION_NOT_ENDED_CODE, cause: 'idp_unconfirmed', message: logout_1.SESSION_NOT_ENDED_IDP_MESSAGE }, { status: 500 })
194
+ : server_1.NextResponse.json(responseData);
158
195
  // Always clear cookies, regardless of success/failure (using app-slug prefixed names)
159
196
  try {
160
197
  response.cookies.delete(sessionCookieName);
@@ -62,6 +62,7 @@ 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
64
  const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
65
+ const log_safe_1 = require("../../lib/log-safe");
65
66
  async function POST(req) {
66
67
  try {
67
68
  const body = await req.json();
@@ -141,7 +142,7 @@ async function POST(req) {
141
142
  }, { status: 200 });
142
143
  }
143
144
  catch (error) {
144
- console.error('[VERIFY_BACKUP_CODE] Error:', error);
145
+ console.error('[VERIFY_BACKUP_CODE] Error:', (0, log_safe_1.errForLog)(error));
145
146
  return server_1.NextResponse.json({
146
147
  success: false,
147
148
  error: 'Failed to verify backup code',
@@ -37,6 +37,7 @@ const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
37
37
  const session_store_1 = require("../../lib/session-store");
38
38
  const jwt_decode_1 = require("../../lib/jwt-decode");
39
39
  const two_factor_verify_failure_1 = require("../../lib/two-factor-verify-failure");
40
+ const log_safe_1 = require("../../lib/log-safe");
40
41
  async function POST(req) {
41
42
  try {
42
43
  const body = await req.json();
@@ -141,7 +142,7 @@ async function POST(req) {
141
142
  }, { status: 200 });
142
143
  }
143
144
  catch (error) {
144
- console.error('[VERIFY_TOTP] Error:', error);
145
+ console.error('[VERIFY_TOTP] Error:', (0, log_safe_1.errForLog)(error));
145
146
  return server_1.NextResponse.json({
146
147
  success: false,
147
148
  error: 'Failed to verify code',
@@ -7,6 +7,7 @@ const refresh_token_validator_1 = require("../../lib/refresh-token-validator");
7
7
  const test_aware_get_token_1 = require("../../lib/test-aware-get-token");
8
8
  const logger_1 = require("../../config/logger");
9
9
  const token_log_id_1 = require("../../lib/token-log-id");
10
+ const log_safe_1 = require("../../lib/log-safe");
10
11
  const auth_1 = require("../../server/auth");
11
12
  async function GET(req) {
12
13
  try {
@@ -53,8 +54,8 @@ async function GET(req) {
53
54
  }
54
55
  catch (error) {
55
56
  const correlationId = globalThis.crypto.randomUUID();
56
- logger_1.logger.error('[REFRESH-VIABILITY] Error checking refresh viability', { correlationId, error: error instanceof Error ? error.message : String(error) });
57
- // PAY-1438: the exception text stays in the log above; the caller gets the id.
57
+ logger_1.logger.error('[REFRESH-VIABILITY] Error checking refresh viability', { correlationId, error: (0, log_safe_1.errForLog)(error) });
58
+ // PAY-1438: the caller gets the id; the log line names the error only (errForLog), never its message (PII-in-logs group 1, BAPert 76553).
58
59
  return server_1.NextResponse.json({ canRefresh: false, reason: 'check_error', error: 'Failed to check refresh viability', code: 'INTERNAL_ERROR', correlationId }, { status: 500 });
59
60
  }
60
61
  }
@@ -14,6 +14,7 @@ const startup_init_1 = require("../../lib/startup-init");
14
14
  const idp_client_config_1 = require("../../lib/idp-client-config");
15
15
  const token_log_id_1 = require("../../lib/token-log-id");
16
16
  const sessions_1 = require("../../vibe/sessions");
17
+ const log_safe_1 = require("../../lib/log-safe");
17
18
  async function GET(req) {
18
19
  try {
19
20
  // Ensure initialization is complete (auth signing secret resolved from IDP)
@@ -213,7 +214,7 @@ async function GET(req) {
213
214
  return server_1.NextResponse.json({ authenticated: false });
214
215
  }
215
216
  catch (error) {
216
- console.error('[API Viability] Error checking session viability:', error);
217
+ console.error('[API Viability] Error checking session viability:', (0, log_safe_1.errForLog)(error));
217
218
  return server_1.NextResponse.json({ authenticated: false }, { status: 500 });
218
219
  }
219
220
  }
@@ -76,6 +76,7 @@ const redis_1 = require("../lib/redis");
76
76
  const single_flight_1 = require("../lib/single-flight");
77
77
  const session_store_1 = require("../lib/session-store");
78
78
  const token_log_id_1 = require("../lib/token-log-id");
79
+ const log_safe_1 = require("../lib/log-safe");
79
80
  /**
80
81
  * 185121: short sha256 fingerprint for secrets/tokens in log lines — enough
81
82
  * to tell "same" from "divergent", never enough to reconstruct. 12 hex chars
@@ -740,7 +741,7 @@ function createBetterAuthInstance(idpConfig, opts = {}) {
740
741
  await deleteSession(key);
741
742
  }
742
743
  catch (err) {
743
- console.warn('[BETTER_AUTH] secondary delete: canonical store cleanup failed:', err);
744
+ console.warn('[BETTER_AUTH] secondary delete: canonical store cleanup failed:', (0, log_safe_1.errForLog)(err));
744
745
  }
745
746
  },
746
747
  },
@@ -1188,7 +1189,7 @@ async function exchangeOAuthForIdpTokens(sessionToken, provider = 'google') {
1188
1189
  return stored;
1189
1190
  }
1190
1191
  catch (err) {
1191
- console.error('[BETTER_AUTH] IDP token exchange failed:', err instanceof Error ? err.message : String(err));
1192
+ console.error('[BETTER_AUTH] IDP token exchange failed:', (0, log_safe_1.errForLog)(err));
1192
1193
  return false;
1193
1194
  }
1194
1195
  }
@@ -1270,7 +1271,7 @@ async function storeIdpTokensInSession(sessionToken, idpTokenData, authProvider)
1270
1271
  return true;
1271
1272
  }
1272
1273
  catch (err) {
1273
- console.error('[BETTER_AUTH] storeIdpTokensInSession failed:', err instanceof Error ? err.message : String(err));
1274
+ console.error('[BETTER_AUTH] storeIdpTokensInSession failed:', (0, log_safe_1.errForLog)(err));
1274
1275
  return false;
1275
1276
  }
1276
1277
  }
@@ -1359,14 +1360,14 @@ async function abortOAuthSignIn(callbackResponse, sessionToken, cookieHeader, lo
1359
1360
  headers.append('set-cookie', sc);
1360
1361
  }
1361
1362
  catch (err) {
1362
- console.warn('[BETTER_AUTH] abort: Better Auth signOut failed, deleting the record directly:', err instanceof Error ? err.message : String(err));
1363
+ console.warn('[BETTER_AUTH] abort: Better Auth signOut failed, deleting the record directly:', (0, log_safe_1.errForLog)(err));
1363
1364
  }
1364
1365
  try {
1365
1366
  const config = await (0, idp_client_config_1.getIDPClientConfig)();
1366
1367
  await (0, redis_1.getRedis)().del(`ba:${config.clientSlug || (0, app_slug_1.getAppSlug)()}:${sessionToken}`);
1367
1368
  }
1368
1369
  catch (err) {
1369
- console.error('[BETTER_AUTH] abort: could not delete the Better Auth session record', (0, token_log_id_1.tokenLogId)(sessionToken), err instanceof Error ? err.message : String(err));
1370
+ console.error('[BETTER_AUTH] abort: could not delete the Better Auth session record', (0, token_log_id_1.tokenLogId)(sessionToken), (0, log_safe_1.errForLog)(err));
1370
1371
  }
1371
1372
  }
1372
1373
  for (const sc of expireSetCookies(callbackResponse.headers.getSetCookie?.() ?? []))
@@ -1484,7 +1485,7 @@ function createAuthGetHandler(loginPath = '/account-auth/login') {
1484
1485
  }
1485
1486
  }
1486
1487
  catch (err) {
1487
- console.error('[BETTER_AUTH] IDP token exchange failed:', err.message);
1488
+ console.error('[BETTER_AUTH] IDP token exchange failed:', (0, log_safe_1.errForLog)(err));
1488
1489
  }
1489
1490
  if (exchange !== true) {
1490
1491
  // A specific IdP error code goes on the URL; otherwise the generic OAUTH_SIGN_IN_FAILED.
@@ -27,6 +27,7 @@ exports.createIdpOAuthHooks = createIdpOAuthHooks;
27
27
  * case-insensitively, at the authorize step). Both come from ONE value: the `redirectURI` of the provider config.
28
28
  */
29
29
  const jose_1 = require("jose");
30
+ const log_safe_1 = require("../lib/log-safe");
30
31
  const DISCOVERY_TTL_MS = 10 * 60 * 1000;
31
32
  const HTTP_TIMEOUT_MS = 10_000;
32
33
  /** The URI sent at authorize AND token time. Explicit env wins; else the registered {app}/api/auth/callback form. */
@@ -252,7 +253,8 @@ function createIdpOAuthHooks(opts) {
252
253
  console.warn('[BETTER_AUTH] payez-idp token response address is masked or unusable; ignored');
253
254
  const email = resolveEmail(payload, responseEmailUsable ? responseEmailRaw : undefined);
254
255
  if (!email) {
255
- throw new Error(responseMasked ? 'response address is masked' : 'no usable email (needs data.user.email or an email claim, address-shaped, equal when both are present)');
256
+ // A closed `code` rides on the error so the refusal log tells an operator WHY (errForLog prints a closed UPPER_SNAKE code) with no value (BAPert 76998 E11).
257
+ throw Object.assign(new Error(responseMasked ? 'response address is masked' : 'no usable email (needs data.user.email or an email claim, address-shaped, equal when both are present)'), { code: responseMasked ? 'RESPONSE_ADDRESS_MASKED' : 'NO_USABLE_EMAIL' });
256
258
  }
257
259
  const name = (typeof payload.name === 'string' && payload.name.trim()) ||
258
260
  (typeof payload.full_name === 'string' && payload.full_name.trim()) ||
@@ -272,7 +274,7 @@ function createIdpOAuthHooks(opts) {
272
274
  }
273
275
  catch (err) {
274
276
  // Reason only, never the token.
275
- console.warn('[BETTER_AUTH] payez-idp access token refused:', err instanceof Error ? err.message : String(err));
277
+ console.warn('[BETTER_AUTH] payez-idp access token refused:', (0, log_safe_1.errForLog)(err));
276
278
  return null;
277
279
  }
278
280
  }
@@ -19,6 +19,7 @@ exports.idpLogin = idpLogin;
19
19
  exports.idpRefreshToken = idpRefreshToken;
20
20
  exports.idpVerify2FA = idpVerify2FA;
21
21
  exports.idpSend2FACode = idpSend2FACode;
22
+ const log_safe_1 = require("../../lib/log-safe");
22
23
  // ============================================================================
23
24
  // CONFIGURATION
24
25
  // ============================================================================
@@ -95,7 +96,7 @@ async function idpLogin(credentials, clientHeaders) {
95
96
  };
96
97
  }
97
98
  catch (error) {
98
- console.error('[IDP_CLIENT] Login request failed:', error);
99
+ console.error('[IDP_CLIENT] Login request failed:', (0, log_safe_1.errForLog)(error));
99
100
  return {
100
101
  success: false,
101
102
  error: {
@@ -211,7 +212,7 @@ async function idpRefreshToken(refreshToken, mfaContext) {
211
212
  };
212
213
  }
213
214
  catch (error) {
214
- console.error('[IDP_CLIENT] Token refresh request failed:', error);
215
+ console.error('[IDP_CLIENT] Token refresh request failed:', (0, log_safe_1.errForLog)(error));
215
216
  return {
216
217
  success: false,
217
218
  error: {
@@ -261,7 +262,7 @@ async function idpVerify2FA(accessToken, code, method) {
261
262
  return { success: true };
262
263
  }
263
264
  catch (error) {
264
- console.error('[IDP_CLIENT] 2FA verification failed:', error);
265
+ console.error('[IDP_CLIENT] 2FA verification failed:', (0, log_safe_1.errForLog)(error));
265
266
  return {
266
267
  success: false,
267
268
  error: {
@@ -303,7 +304,7 @@ async function idpSend2FACode(accessToken, method) {
303
304
  return { success: true };
304
305
  }
305
306
  catch (error) {
306
- console.error('[IDP_CLIENT] Send 2FA code failed:', error);
307
+ console.error('[IDP_CLIENT] Send 2FA code failed:', (0, log_safe_1.errForLog)(error));
307
308
  return {
308
309
  success: false,
309
310
  error: {
@@ -21,6 +21,7 @@ exports.expClaimToMs = expClaimToMs;
21
21
  exports.validateTokenExpiry = validateTokenExpiry;
22
22
  const jwt_decode_1 = require("../../lib/jwt-decode");
23
23
  const refresh_window_1 = require("../../lib/refresh-window");
24
+ const log_safe_1 = require("../../lib/log-safe");
24
25
  // Re-export header utilities for consumers
25
26
  var jwt_decode_2 = require("../../lib/jwt-decode");
26
27
  Object.defineProperty(exports, "decodeJwtHeader", { enumerable: true, get: function () { return jwt_decode_2.decodeJwtHeader; } });
@@ -39,7 +40,7 @@ function decodeIdpAccessToken(token) {
39
40
  return (0, jwt_decode_1.jwtDecode)(token);
40
41
  }
41
42
  catch (error) {
42
- console.error('[TOKEN_UTILS] Failed to decode access token:', error);
43
+ console.error('[TOKEN_UTILS] Failed to decode access token:', (0, log_safe_1.errForLog)(error));
43
44
  return null;
44
45
  }
45
46
  }
@@ -64,7 +65,7 @@ function decodeIdpAccessTokenFull(token) {
64
65
  };
65
66
  }
66
67
  catch (error) {
67
- console.error('[TOKEN_UTILS] Failed to decode access token (full):', error);
68
+ console.error('[TOKEN_UTILS] Failed to decode access token (full):', (0, log_safe_1.errForLog)(error));
68
69
  return null;
69
70
  }
70
71
  }
@@ -213,7 +214,7 @@ function validateTokenExpiry(accessToken, cachedExpiresAt) {
213
214
  };
214
215
  }
215
216
  catch (error) {
216
- console.error('[TOKEN_UTILS] Failed to validate token expiry:', error);
217
+ console.error('[TOKEN_UTILS] Failed to validate token expiry:', (0, log_safe_1.errForLog)(error));
217
218
  return { valid: false, actualExpiresAt: null, mismatch: false };
218
219
  }
219
220
  }