@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.
- package/CHANGELOG.md +19 -0
- package/dist/api-handlers/account/recovery/forward.js +2 -1
- package/dist/api-handlers/account/recovery/initiate.js +2 -1
- package/dist/api-handlers/account/reset-password.js +2 -1
- package/dist/api-handlers/account/send-code.js +2 -1
- package/dist/api-handlers/account/update-phone.js +2 -1
- package/dist/api-handlers/account/validate-password.js +2 -1
- package/dist/api-handlers/account/verify-email.js +2 -1
- package/dist/api-handlers/account/verify-sms.js +2 -1
- package/dist/api-handlers/anon/preferences.js +3 -2
- package/dist/api-handlers/auth/credential-login-email-code.js +3 -2
- package/dist/api-handlers/auth/credential-login.js +6 -5
- package/dist/api-handlers/auth/refresh.js +2 -1
- package/dist/api-handlers/auth/signout.js +43 -6
- package/dist/api-handlers/mfa/backup-code-verify.js +2 -1
- package/dist/api-handlers/mfa/totp-verify.js +2 -1
- package/dist/api-handlers/session/refresh-viability.js +3 -2
- package/dist/api-handlers/session/viability.js +2 -1
- package/dist/auth/better-auth.js +7 -6
- package/dist/auth/idp-oauth.js +4 -2
- package/dist/auth/utils/idp-client.js +5 -4
- package/dist/auth/utils/token-utils.js +4 -3
- package/dist/config/logger.js +2 -1
- package/dist/config/logging-config.js +4 -3
- package/dist/config/vibe-log-transport.js +2 -1
- package/dist/lib/ensure-fresh-access-token.js +17 -11
- package/dist/lib/geolocation.js +5 -22
- package/dist/lib/idp-client-config.js +4 -3
- package/dist/lib/idp-fetch.d.ts +11 -0
- package/dist/lib/idp-fetch.js +3 -1
- package/dist/lib/idp-logout.d.ts +10 -0
- package/dist/lib/idp-logout.js +73 -1
- package/dist/lib/jwt-decode-client.js +2 -1
- package/dist/lib/jwt-decode.js +2 -1
- package/dist/lib/log-safe.d.ts +19 -0
- package/dist/lib/log-safe.js +32 -0
- package/dist/lib/redis.js +2 -1
- package/dist/lib/safe-error-response.d.ts +5 -1
- package/dist/lib/safe-error-response.js +7 -2
- package/dist/lib/session-store.js +2 -1
- package/dist/lib/site-logger.d.ts +2 -8
- package/dist/lib/site-logger.js +11 -5
- package/dist/lib/standardized-client-api.js +3 -2
- package/dist/lib/token-lifecycle.js +3 -2
- package/dist/middleware/create-middleware.js +2 -1
- package/dist/middleware/rbac-check.d.ts +0 -13
- package/dist/middleware/rbac-check.js +6 -5
- package/dist/routes/auth/logout.js +6 -2
- package/dist/routes/auth/settings.js +2 -1
- package/dist/server/auth-guard.js +4 -3
- package/dist/server/auth.js +5 -9
- package/dist/server/decode-session.js +3 -2
- package/dist/server/with-auth.js +2 -1
- package/package.json +1 -1
- package/src/api-handlers/account/recovery/forward.ts +2 -1
- package/src/api-handlers/account/recovery/initiate.ts +2 -1
- package/src/api-handlers/account/reset-password.ts +2 -1
- package/src/api-handlers/account/send-code.ts +2 -1
- package/src/api-handlers/account/update-phone.ts +2 -1
- package/src/api-handlers/account/validate-password.ts +2 -1
- package/src/api-handlers/account/verify-email.ts +2 -1
- package/src/api-handlers/account/verify-sms.ts +2 -1
- package/src/api-handlers/anon/preferences.ts +3 -2
- package/src/api-handlers/auth/credential-login-email-code.ts +3 -2
- package/src/api-handlers/auth/credential-login.ts +6 -5
- package/src/api-handlers/auth/refresh.ts +2 -1
- package/src/api-handlers/auth/signout.ts +54 -7
- package/src/api-handlers/mfa/backup-code-verify.ts +2 -1
- package/src/api-handlers/mfa/totp-verify.ts +2 -1
- package/src/api-handlers/session/refresh-viability.ts +3 -2
- package/src/api-handlers/session/viability.ts +2 -1
- package/src/auth/better-auth.ts +7 -6
- package/src/auth/idp-oauth.ts +7 -2
- package/src/auth/utils/idp-client.ts +5 -4
- package/src/auth/utils/token-utils.ts +4 -3
- package/src/config/logger.ts +2 -1
- package/src/config/logging-config.ts +4 -3
- package/src/config/vibe-log-transport.ts +2 -1
- package/src/lib/ensure-fresh-access-token.ts +17 -11
- package/src/lib/geolocation.ts +5 -4
- package/src/lib/idp-client-config.ts +4 -3
- package/src/lib/idp-fetch.ts +11 -1
- package/src/lib/idp-logout.ts +36 -1
- package/src/lib/jwt-decode-client.ts +3 -1
- package/src/lib/jwt-decode.ts +2 -1
- package/src/lib/log-safe.ts +30 -0
- package/src/lib/redis.ts +2 -1
- package/src/lib/safe-error-response.ts +7 -2
- package/src/lib/session-store.ts +2 -1
- package/src/lib/site-logger.ts +13 -7
- package/src/lib/standardized-client-api.ts +3 -2
- package/src/lib/token-lifecycle.ts +3 -2
- package/src/middleware/create-middleware.ts +2 -1
- package/src/middleware/rbac-check.ts +2 -1
- package/src/routes/auth/logout.ts +9 -2
- package/src/routes/auth/settings.ts +2 -1
- package/src/server/auth-guard.ts +4 -3
- package/src/server/auth.ts +5 -10
- package/src/server/decode-session.ts +3 -2
- 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:',
|
|
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:',
|
|
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:',
|
|
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:',
|
|
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:',
|
|
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:`,
|
|
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:
|
|
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
|
-
//
|
|
134
|
-
//
|
|
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
|
-
|
|
144
|
-
console.error('[SIGNOUT] Redis session cleanup failed
|
|
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 =
|
|
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:
|
|
57
|
-
// PAY-1438: the
|
|
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
|
}
|
package/dist/auth/better-auth.js
CHANGED
|
@@ -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:',
|
|
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:',
|
|
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:',
|
|
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),
|
|
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:',
|
|
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.
|
package/dist/auth/idp-oauth.js
CHANGED
|
@@ -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
|
-
|
|
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:',
|
|
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
|
}
|