@payez/next-mvp 4.11.0 → 4.12.0
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 +13 -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 +2 -1
- 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 +2 -1
- 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/dist/config/logger.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.redisLogger = exports.tokenRefreshLogger = exports.tokenSyncLogger = exports.tokenLogger = exports.circuitBreakerLogger = exports.apiLogger = exports.idpLogger = exports.authLogger = exports.logger = exports.log = void 0;
|
|
4
4
|
const logging_config_1 = require("./logging-config");
|
|
5
5
|
const vibe_log_transport_1 = require("./vibe-log-transport");
|
|
6
|
+
const log_safe_1 = require("../lib/log-safe");
|
|
6
7
|
let loggingConfig = (0, logging_config_1.getLoggingConfigSync)();
|
|
7
8
|
(0, logging_config_1.startConfigWatching)();
|
|
8
9
|
const isEdgeRuntime = () => {
|
|
@@ -48,7 +49,7 @@ const createLogger = () => {
|
|
|
48
49
|
return winston.createLogger({ level: loggingConfig.logLevel, levels: loggingConfig.levels, format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()), transports, exitOnError: false });
|
|
49
50
|
}
|
|
50
51
|
catch (error) {
|
|
51
|
-
console.warn('Failed to initialize Winston logger, falling back to EdgeLogger:', error);
|
|
52
|
+
console.warn('Failed to initialize Winston logger, falling back to EdgeLogger:', (0, log_safe_1.errForLog)(error));
|
|
52
53
|
return new EdgeLogger();
|
|
53
54
|
}
|
|
54
55
|
};
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.removeConfigWatcher = exports.addConfigWatcher = exports.stopConfigWatching = exports.startConfigWatching = exports.getLoggingConfigSync = exports.getLoggingConfig = exports.loggingConfigManager = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const fs_2 = require("fs");
|
|
6
|
+
const log_safe_1 = require("../lib/log-safe");
|
|
6
7
|
const isEdgeRuntime = typeof globalThis.EdgeRuntime !== 'undefined' || process.env.NEXT_RUNTIME === 'edge';
|
|
7
8
|
class LoggingConfigManager {
|
|
8
9
|
config = null;
|
|
@@ -39,7 +40,7 @@ class LoggingConfigManager {
|
|
|
39
40
|
return config;
|
|
40
41
|
}
|
|
41
42
|
catch (error) {
|
|
42
|
-
console.warn(`Failed to load logging config from ${this.configPath}:`, error);
|
|
43
|
+
console.warn(`Failed to load logging config from ${this.configPath}:`, (0, log_safe_1.errForLog)(error));
|
|
43
44
|
return this.getDefaultConfig();
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -61,7 +62,7 @@ class LoggingConfigManager {
|
|
|
61
62
|
} });
|
|
62
63
|
}
|
|
63
64
|
catch (error) {
|
|
64
|
-
console.warn('Failed to start watching logging config file:', error);
|
|
65
|
+
console.warn('Failed to start watching logging config file:', (0, log_safe_1.errForLog)(error));
|
|
65
66
|
} }
|
|
66
67
|
stopWatching() { if (this.isEdgeRuntime || this.isBrowser) {
|
|
67
68
|
return;
|
|
@@ -75,7 +76,7 @@ class LoggingConfigManager {
|
|
|
75
76
|
cb(config);
|
|
76
77
|
}
|
|
77
78
|
catch (error) {
|
|
78
|
-
console.error('Error in logging config watcher:', error);
|
|
79
|
+
console.error('Error in logging config watcher:', (0, log_safe_1.errForLog)(error));
|
|
79
80
|
} }); }
|
|
80
81
|
validateAndMergeConfig(rawConfig) {
|
|
81
82
|
const defaults = this.getDefaultConfig();
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.VibeLogTransport = void 0;
|
|
16
16
|
exports.createVibeLogTransport = createVibeLogTransport;
|
|
17
17
|
const redis_1 = require("../lib/redis");
|
|
18
|
+
const log_safe_1 = require("../lib/log-safe");
|
|
18
19
|
// Dynamic import — winston is a peerDependency and may not be installed.
|
|
19
20
|
// We resolve the base class lazily so builds don't break without it.
|
|
20
21
|
let TransportBase;
|
|
@@ -144,7 +145,7 @@ class VibeLogTransport extends TransportBase {
|
|
|
144
145
|
this.batch = [...entries, ...this.batch].slice(0, 100);
|
|
145
146
|
}
|
|
146
147
|
// Log to console as fallback
|
|
147
|
-
console.error('[VibeLogTransport] Failed to flush logs:', err);
|
|
148
|
+
console.error('[VibeLogTransport] Failed to flush logs:', (0, log_safe_1.errForLog)(err));
|
|
148
149
|
}
|
|
149
150
|
finally {
|
|
150
151
|
this.isFlushing = false;
|
|
@@ -25,6 +25,7 @@ const refresh_rotation_1 = require("./refresh-rotation");
|
|
|
25
25
|
const refresh_window_1 = require("./refresh-window");
|
|
26
26
|
const safe_trace_id_1 = require("./safe-trace-id");
|
|
27
27
|
const refresh_in_doubt_1 = require("./refresh-in-doubt");
|
|
28
|
+
const log_safe_1 = require("./log-safe");
|
|
28
29
|
/**
|
|
29
30
|
* The refresh envelope rule (BAPert 76455 (B)): `success` is a boolean; `success:true` needs a `data` object holding a string `access_token`;
|
|
30
31
|
* `success:false` needs `error.code` as a string; `meta`, `request_id` and `time_stamp` are optional; a non-2xx can not say success:true.
|
|
@@ -318,7 +319,7 @@ async function ensureFreshAccessToken(sessionToken, config, options = {}) {
|
|
|
318
319
|
await (0, session_store_1.updateSession)(sessionToken, (0, refresh_in_doubt_1.inDoubtPatch)(sentRefreshToken));
|
|
319
320
|
}
|
|
320
321
|
catch (markError) {
|
|
321
|
-
console.error('[ENSURE-FRESH] could not record the in-doubt marker', { error:
|
|
322
|
+
console.error('[ENSURE-FRESH] could not record the in-doubt marker', { error: (0, log_safe_1.errForLog)(markError) });
|
|
322
323
|
}
|
|
323
324
|
return {
|
|
324
325
|
ok: false,
|
package/dist/lib/geolocation.js
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* =============================================================================
|
|
4
|
-
* GEOLOCATION UTILITY
|
|
5
|
-
* =============================================================================
|
|
6
|
-
*
|
|
7
|
-
* Server-side utility for resolving IP addresses to geographic locations.
|
|
8
|
-
* Uses ip-api.com (free tier: 45 requests/minute).
|
|
9
|
-
*
|
|
10
|
-
* USAGE:
|
|
11
|
-
* ------
|
|
12
|
-
* import { getLocationFromIP, extractClientIP } from '@payez/next-mvp/lib/geolocation';
|
|
13
|
-
*
|
|
14
|
-
* const ip = extractClientIP(request.headers);
|
|
15
|
-
* const location = await getLocationFromIP(ip);
|
|
16
|
-
* console.log(location?.city); // 'Mountain View'
|
|
17
|
-
*
|
|
18
|
-
* =============================================================================
|
|
19
|
-
*/
|
|
20
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
3
|
exports.isPrivateIP = isPrivateIP;
|
|
22
4
|
exports.getPublicIP = getPublicIP;
|
|
@@ -25,6 +7,7 @@ exports.getLocationFromIP = getLocationFromIP;
|
|
|
25
7
|
exports.formatLocation = formatLocation;
|
|
26
8
|
exports.getCountryFlag = getCountryFlag;
|
|
27
9
|
exports.clearGeoCache = clearGeoCache;
|
|
10
|
+
const log_safe_1 = require("./log-safe");
|
|
28
11
|
// -----------------------------------------------------------------------------
|
|
29
12
|
// CACHE CONFIGURATION
|
|
30
13
|
// -----------------------------------------------------------------------------
|
|
@@ -90,7 +73,7 @@ async function getPublicIP() {
|
|
|
90
73
|
// Validate it looks like an IP
|
|
91
74
|
if (ip && /^[\d.]+$/.test(ip) || /^[a-f0-9:]+$/i.test(ip)) {
|
|
92
75
|
cachedPublicIP = { ip, timestamp: Date.now() };
|
|
93
|
-
console.log(
|
|
76
|
+
console.log("[geolocation] Public IP resolved");
|
|
94
77
|
return ip;
|
|
95
78
|
}
|
|
96
79
|
}
|
|
@@ -147,7 +130,7 @@ async function getLocationFromIP(ip, resolvePublicIP = true) {
|
|
|
147
130
|
if (resolvePublicIP) {
|
|
148
131
|
const publicIP = await getPublicIP();
|
|
149
132
|
if (publicIP && !isPrivateIP(publicIP)) {
|
|
150
|
-
console.log(
|
|
133
|
+
console.log("[geolocation] Private IP -> using the resolved public IP");
|
|
151
134
|
// Recursively call with public IP (but don't resolve again to prevent loops)
|
|
152
135
|
return getLocationFromIP(publicIP, false);
|
|
153
136
|
}
|
|
@@ -176,7 +159,7 @@ async function getLocationFromIP(ip, resolvePublicIP = true) {
|
|
|
176
159
|
}
|
|
177
160
|
const data = await response.json();
|
|
178
161
|
if (data.status !== 'success') {
|
|
179
|
-
console.warn(
|
|
162
|
+
console.warn("[geolocation] Lookup failed (upstream refused)");
|
|
180
163
|
// Cache the failure to avoid repeated lookups
|
|
181
164
|
geoCache.set(ip, { location: null, timestamp: Date.now() });
|
|
182
165
|
return null;
|
|
@@ -196,7 +179,7 @@ async function getLocationFromIP(ip, resolvePublicIP = true) {
|
|
|
196
179
|
return location;
|
|
197
180
|
}
|
|
198
181
|
catch (error) {
|
|
199
|
-
console.error(
|
|
182
|
+
console.error("[geolocation] lookup failed:", (0, log_safe_1.errForLog)(error)); // no IP on a routine line (BAPert 76553 Q1)
|
|
200
183
|
return null;
|
|
201
184
|
}
|
|
202
185
|
}
|
|
@@ -82,6 +82,7 @@ exports.getEnabledProviders = getEnabledProviders;
|
|
|
82
82
|
require("server-only");
|
|
83
83
|
const crypto_1 = require("crypto");
|
|
84
84
|
const never_in_response_1 = require("./never-in-response");
|
|
85
|
+
const log_safe_1 = require("./log-safe");
|
|
85
86
|
// REDIS IS IMPORTED LAZILY, AND THAT IS LOAD-BEARING.
|
|
86
87
|
// A module-scope `import redis from './redis'` drags ioredis into EVERY bundle that
|
|
87
88
|
// touches this file - including instrumentation.ts. Next compiles the instrumentation
|
|
@@ -276,7 +277,7 @@ async function getConfigFromRedis() {
|
|
|
276
277
|
return parsed.config;
|
|
277
278
|
}
|
|
278
279
|
catch (error) {
|
|
279
|
-
console.warn('[IDP_CONFIG] Failed to read from Redis cache:', error);
|
|
280
|
+
console.warn('[IDP_CONFIG] Failed to read from Redis cache:', (0, log_safe_1.errForLog)(error));
|
|
280
281
|
return null;
|
|
281
282
|
}
|
|
282
283
|
}
|
|
@@ -293,7 +294,7 @@ async function setConfigInRedis(config) {
|
|
|
293
294
|
await (await getRedis()).set(key, JSON.stringify(data), 'EX', ttlSeconds + 10);
|
|
294
295
|
}
|
|
295
296
|
catch (error) {
|
|
296
|
-
console.warn('[IDP_CONFIG] Failed to write to Redis cache:', error);
|
|
297
|
+
console.warn('[IDP_CONFIG] Failed to write to Redis cache:', (0, log_safe_1.errForLog)(error));
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
// ============================================================================
|
|
@@ -529,7 +530,7 @@ async function clearConfigRedisCache() {
|
|
|
529
530
|
console.log('[IDP_CONFIG] Redis cache cleared:', key);
|
|
530
531
|
}
|
|
531
532
|
catch (error) {
|
|
532
|
-
console.warn('[IDP_CONFIG] Failed to clear Redis cache:', error);
|
|
533
|
+
console.warn('[IDP_CONFIG] Failed to clear Redis cache:', (0, log_safe_1.errForLog)(error));
|
|
533
534
|
}
|
|
534
535
|
}
|
|
535
536
|
/**
|
package/dist/lib/idp-fetch.d.ts
CHANGED
|
@@ -35,5 +35,16 @@ export interface IdpFetchOptions {
|
|
|
35
35
|
* `skipped: true` without a request; null on the retry leaves the first answer standing. Absent: `init` is used as before.
|
|
36
36
|
*/
|
|
37
37
|
initFor?: (sessionToken: string) => Promise<RequestInit | null>;
|
|
38
|
+
/**
|
|
39
|
+
* 4.12: the refresh that follows a 401/403 is done by THIS function instead of the HTTP self-call to /api/auth/refresh. Sign-out supplies one that
|
|
40
|
+
* calls `ensureFreshAccessToken` in-process with a window that cannot be "fresh": a logout 401 with a locally-fresh token usually means Core
|
|
41
|
+
* invalidated the token (password change), and only the IdP's answer to a real refresh (401 + the discard marker = the grant is dead; success =
|
|
42
|
+
* the grant is live) can say which. No header on the public refresh route, so nothing a browser sends can force a refresh (BAPert 77022).
|
|
43
|
+
* Returns the status the refresh route would have answered. Absent: `internalRefresh` as before.
|
|
44
|
+
*/
|
|
45
|
+
refreshOnAuthFailure?: (sessionToken: string) => Promise<{
|
|
46
|
+
ok: boolean;
|
|
47
|
+
status: number;
|
|
48
|
+
}>;
|
|
38
49
|
}
|
|
39
50
|
export declare function idpFetchJSON<T = any>(req: NextRequest, targetUrl: string, init?: RequestInit, opts?: IdpFetchOptions): Promise<IdpFetchResult<T>>;
|
package/dist/lib/idp-fetch.js
CHANGED
|
@@ -180,7 +180,9 @@ async function idpFetchJSON(req, targetUrl, init = {}, opts = {}) {
|
|
|
180
180
|
attemptedRefresh = true;
|
|
181
181
|
try {
|
|
182
182
|
// Use centralized internal API helper for server-to-server calls
|
|
183
|
-
const rf =
|
|
183
|
+
const rf = opts.refreshOnAuthFailure
|
|
184
|
+
? await opts.refreshOnAuthFailure(sessionToken)
|
|
185
|
+
: await (0, internal_api_1.internalRefresh)(req.headers.get('cookie') || '', sessionToken);
|
|
184
186
|
refreshStatus = rf.status;
|
|
185
187
|
if (rf.ok) {
|
|
186
188
|
const fresh = await (0, session_store_1.getSession)(sessionToken);
|
package/dist/lib/idp-logout.d.ts
CHANGED
|
@@ -21,4 +21,14 @@ import type { NextRequest } from 'next/server';
|
|
|
21
21
|
* refresh token, and a body built earlier would name the old one (NightHawk 76288).
|
|
22
22
|
*/
|
|
23
23
|
export type IdpLogoutOutcome = 'revoked' | 'nothing-to-revoke' | 'grant-already-dead';
|
|
24
|
+
/**
|
|
25
|
+
* The revoke could not be confirmed. `grantLive` is true ONLY when the IdP itself showed the grant is alive: the forced refresh SUCCEEDED (it issued
|
|
26
|
+
* new tokens) and the logout still did not end it. Every other unknown (a transport error, a 5xx, a refresh that threw or answered something
|
|
27
|
+
* else) leaves it false: the grant may be live or dead, and the log must not claim either (BAPert 77013). The closed `code` is what errForLog prints.
|
|
28
|
+
*/
|
|
29
|
+
export declare class IdpLogoutUnconfirmedError extends Error {
|
|
30
|
+
readonly grantLive: boolean;
|
|
31
|
+
readonly code = "IDP_LOGOUT_UNCONFIRMED";
|
|
32
|
+
constructor(status: number, grantLive: boolean);
|
|
33
|
+
}
|
|
24
34
|
export declare function revokeSessionAtIdp(req: NextRequest): Promise<IdpLogoutOutcome>;
|
package/dist/lib/idp-logout.js
CHANGED
|
@@ -1,12 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.IdpLogoutUnconfirmedError = void 0;
|
|
3
37
|
exports.revokeSessionAtIdp = revokeSessionAtIdp;
|
|
4
38
|
const idp_fetch_1 = require("./idp-fetch");
|
|
5
39
|
const session_store_1 = require("./session-store");
|
|
6
40
|
const env_1 = require("../config/env");
|
|
41
|
+
/**
|
|
42
|
+
* The revoke could not be confirmed. `grantLive` is true ONLY when the IdP itself showed the grant is alive: the forced refresh SUCCEEDED (it issued
|
|
43
|
+
* new tokens) and the logout still did not end it. Every other unknown (a transport error, a 5xx, a refresh that threw or answered something
|
|
44
|
+
* else) leaves it false: the grant may be live or dead, and the log must not claim either (BAPert 77013). The closed `code` is what errForLog prints.
|
|
45
|
+
*/
|
|
46
|
+
class IdpLogoutUnconfirmedError extends Error {
|
|
47
|
+
grantLive;
|
|
48
|
+
code = 'IDP_LOGOUT_UNCONFIRMED';
|
|
49
|
+
constructor(status, grantLive) {
|
|
50
|
+
super(`IdP logout answered ${status}`);
|
|
51
|
+
this.grantLive = grantLive;
|
|
52
|
+
this.name = 'IdpLogoutUnconfirmedError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.IdpLogoutUnconfirmedError = IdpLogoutUnconfirmedError;
|
|
7
56
|
async function revokeSessionAtIdp(req) {
|
|
8
57
|
let resolvedSession;
|
|
9
58
|
const result = await (0, idp_fetch_1.idpFetchJSON)(req, `${env_1.ENV_CONFIG.IDP_URL}/api/Account/logout`, {}, {
|
|
59
|
+
// A logout 401 with a locally-fresh token must reach a REAL refresh, so the IdP's answer can prove the grant dead (BAPert 77013). Done
|
|
60
|
+
// in-process, not through the public refresh route, so no request header can force it (NightHawk 77021, BAPert 77022).
|
|
61
|
+
refreshOnAuthFailure: forceRefreshAtIdp,
|
|
10
62
|
initFor: async (token) => {
|
|
11
63
|
resolvedSession = token;
|
|
12
64
|
let session = await (0, session_store_1.getSession)(token);
|
|
@@ -28,7 +80,27 @@ async function revokeSessionAtIdp(req) {
|
|
|
28
80
|
return 'revoked';
|
|
29
81
|
if (result.attemptedRefresh && result.refreshStatus === 401 && resolvedSession && (await grantWasDiscarded(resolvedSession)))
|
|
30
82
|
return 'grant-already-dead';
|
|
31
|
-
|
|
83
|
+
const refreshSucceeded = result.attemptedRefresh && typeof result.refreshStatus === 'number' && result.refreshStatus >= 200 && result.refreshStatus < 300;
|
|
84
|
+
throw new IdpLogoutUnconfirmedError(result.status, refreshSucceeded);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Refresh the session NOW, ignoring "the stored access token looks fresh", by calling `ensureFreshAccessToken` in this process (the one
|
|
88
|
+
* implementation that talks to the IdP, holds the lock and writes the discard marker). Config is read the way `oauth/resume` reads it
|
|
89
|
+
* (IDP_URL, CLIENT_ID, the default refresh endpoint). It answers with the status the refresh route would have: 200 on success, 401 for
|
|
90
|
+
* NO_SESSION / NO_REFRESH_TOKEN, 409 for a lost lock, otherwise E's own status (a discarded IdP 401 is 401 with the marker written).
|
|
91
|
+
* Missing config or a throw is an UNKNOWN outcome: the caller treats a thrown refresh as `refreshStatus: null`.
|
|
92
|
+
*/
|
|
93
|
+
async function forceRefreshAtIdp(sessionToken) {
|
|
94
|
+
const idpBaseUrl = process.env.IDP_URL;
|
|
95
|
+
const clientId = process.env.CLIENT_ID;
|
|
96
|
+
if (!idpBaseUrl || !clientId)
|
|
97
|
+
throw new Error('IDP_URL and CLIENT_ID are required to prove a grant dead');
|
|
98
|
+
const { ensureFreshAccessToken } = await Promise.resolve().then(() => __importStar(require('./ensure-fresh-access-token')));
|
|
99
|
+
const r = await ensureFreshAccessToken(sessionToken, { idpBaseUrl, clientId }, { requireRefreshToken: true, safetyWindowMs: Number.MAX_SAFE_INTEGER });
|
|
100
|
+
if (r.ok)
|
|
101
|
+
return { ok: true, status: 200 };
|
|
102
|
+
const status = r.code === 'NO_SESSION' || r.code === 'NO_REFRESH_TOKEN' ? 401 : r.code === 'CONFLICT' ? 409 : r.status;
|
|
103
|
+
return { ok: false, status };
|
|
32
104
|
}
|
|
33
105
|
/**
|
|
34
106
|
* A refresh 401 is not by itself "the grant is gone" (NightHawk 76295, BAPert 76297): refresh.ts also answers 401 for 'no session', for
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.jwtDecode = jwtDecode;
|
|
8
|
+
const log_safe_1 = require("./log-safe");
|
|
8
9
|
// Decode base64url
|
|
9
10
|
function base64urlDecode(base64url) {
|
|
10
11
|
try {
|
|
@@ -40,7 +41,7 @@ function jwtDecode(token) {
|
|
|
40
41
|
return parsedPayload;
|
|
41
42
|
}
|
|
42
43
|
catch (e) {
|
|
43
|
-
console.error('[JWT] Decode failed:',
|
|
44
|
+
console.error('[JWT] Decode failed:', (0, log_safe_1.errForLog)(e));
|
|
44
45
|
return null;
|
|
45
46
|
}
|
|
46
47
|
}
|
package/dist/lib/jwt-decode.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.jwtDecode = jwtDecode;
|
|
|
4
4
|
exports.decodeJwtHeader = decodeJwtHeader;
|
|
5
5
|
exports.extractKidFromToken = extractKidFromToken;
|
|
6
6
|
const jwt_decode_1 = require("jwt-decode");
|
|
7
|
+
const log_safe_1 = require("./log-safe");
|
|
7
8
|
/**
|
|
8
9
|
* Decode JWT payload (standard claims).
|
|
9
10
|
* This is a thin wrapper around jwt-decode library.
|
|
@@ -40,7 +41,7 @@ function decodeJwtHeader(token) {
|
|
|
40
41
|
return JSON.parse(headerJson);
|
|
41
42
|
}
|
|
42
43
|
catch (error) {
|
|
43
|
-
console.error('[JWT_DECODE] Failed to decode JWT header:', error);
|
|
44
|
+
console.error('[JWT_DECODE] Failed to decode JWT header:', (0, log_safe_1.errForLog)(error));
|
|
44
45
|
return null;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* log-safe: the ONLY way a caught error or an upstream `code` goes into a server log (PII-in-logs census, BAPert 76553 group 1; ported from the
|
|
3
|
+
* portal's api-error-response.ts, BAPert 76470).
|
|
4
|
+
*
|
|
5
|
+
* Server logs persist and are read beyond the person who made the request. An error's `message`, `stack`, `cause` and `details` can carry an
|
|
6
|
+
* email, a phone number, a code, a token, a SQL fragment or a host: an IdP error's message is the upstream's own free text, and V8's JSON parse
|
|
7
|
+
* error quotes its input. So a log line gets the error's NAME and a closed STATUS / CODE, nothing else. Every field is matched against a closed
|
|
8
|
+
* shape; anything that does not match is dropped, not truncated.
|
|
9
|
+
*
|
|
10
|
+
* No imports on purpose: this is a leaf, loaded by modules that must not pull in anything else.
|
|
11
|
+
*/
|
|
12
|
+
/** An upstream `code` is loggable only if it is a closed token (UPPER_SNAKE). Anything else, including free text, becomes null. */
|
|
13
|
+
export declare function closedCodeForLog(code: unknown): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Describe a caught error for a log line: `error=<Name> [status=<n>] [code=<CLOSED_CODE>]`.
|
|
16
|
+
* It NEVER carries `message`, `stack`, `cause` or `details`. A name that is not a short identifier (a hostile or free-text `name`) becomes
|
|
17
|
+
* `NonError`; a status outside 100-599 is dropped; a code outside the closed token shape is dropped.
|
|
18
|
+
*/
|
|
19
|
+
export declare function errForLog(err: unknown): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* log-safe: the ONLY way a caught error or an upstream `code` goes into a server log (PII-in-logs census, BAPert 76553 group 1; ported from the
|
|
4
|
+
* portal's api-error-response.ts, BAPert 76470).
|
|
5
|
+
*
|
|
6
|
+
* Server logs persist and are read beyond the person who made the request. An error's `message`, `stack`, `cause` and `details` can carry an
|
|
7
|
+
* email, a phone number, a code, a token, a SQL fragment or a host: an IdP error's message is the upstream's own free text, and V8's JSON parse
|
|
8
|
+
* error quotes its input. So a log line gets the error's NAME and a closed STATUS / CODE, nothing else. Every field is matched against a closed
|
|
9
|
+
* shape; anything that does not match is dropped, not truncated.
|
|
10
|
+
*
|
|
11
|
+
* No imports on purpose: this is a leaf, loaded by modules that must not pull in anything else.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.closedCodeForLog = closedCodeForLog;
|
|
15
|
+
exports.errForLog = errForLog;
|
|
16
|
+
/** An upstream `code` is loggable only if it is a closed token (UPPER_SNAKE). Anything else, including free text, becomes null. */
|
|
17
|
+
function closedCodeForLog(code) {
|
|
18
|
+
return typeof code === 'string' && /^[A-Z0-9_]{1,64}$/.test(code) ? code : null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Describe a caught error for a log line: `error=<Name> [status=<n>] [code=<CLOSED_CODE>]`.
|
|
22
|
+
* It NEVER carries `message`, `stack`, `cause` or `details`. A name that is not a short identifier (a hostile or free-text `name`) becomes
|
|
23
|
+
* `NonError`; a status outside 100-599 is dropped; a code outside the closed token shape is dropped.
|
|
24
|
+
*/
|
|
25
|
+
function errForLog(err) {
|
|
26
|
+
const e = (err !== null && typeof err === 'object' ? err : {});
|
|
27
|
+
const name = typeof e.name === 'string' && /^[A-Za-z0-9_]{1,48}$/.test(e.name) ? e.name : 'NonError';
|
|
28
|
+
const status = typeof e.status === 'number' && Number.isInteger(e.status) && e.status >= 100 && e.status <= 599 ? ` status=${e.status}` : '';
|
|
29
|
+
const closed = closedCodeForLog(e.code);
|
|
30
|
+
const code = closed ? ` code=${closed}` : '';
|
|
31
|
+
return `error=${name}${status}${code}`;
|
|
32
|
+
}
|
package/dist/lib/redis.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.redis = void 0;
|
|
|
7
7
|
exports.getRedis = getRedis;
|
|
8
8
|
// E:\Repos\PayEz-Next-MVP\packages\next-mvp\src\lib\redis.ts
|
|
9
9
|
const ioredis_1 = __importDefault(require("ioredis"));
|
|
10
|
+
const log_safe_1 = require("./log-safe");
|
|
10
11
|
let client = null;
|
|
11
12
|
// ── 183496: fail FAST and NAMED, never a 20s spinner ───────────────────────
|
|
12
13
|
// The DEFAULTS were the defect (Nextpert-Scout's killable-proxy measurement,
|
|
@@ -75,7 +76,7 @@ function createClient() {
|
|
|
75
76
|
// unhandled EventEmitter error, which can take the whole PROCESS down -
|
|
76
77
|
// a Redis blip becoming a dead server is worse than the spinner was.
|
|
77
78
|
instance.on('error', (err) => {
|
|
78
|
-
console.error(`[redis] PATH FAILURE ${new Date().toISOString()}: ${
|
|
79
|
+
console.error(`[redis] PATH FAILURE ${new Date().toISOString()}: ${(0, log_safe_1.errForLog)(err)}`);
|
|
79
80
|
});
|
|
80
81
|
// 185121 line 3: the 16:05 event left no named trigger because ONLY 'error'
|
|
81
82
|
// was listened for. A reconnect or a clean end is silent in the artifact,
|
|
@@ -19,7 +19,11 @@ export interface IdpErrorProjection {
|
|
|
19
19
|
resolution?: string;
|
|
20
20
|
request_id?: string;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Log an exception with a fresh correlationId and return the id for the response body.
|
|
24
|
+
* The log line carries the error's NAME and closed code/status only (errForLog), never its message, stack or cause: those can hold an
|
|
25
|
+
* email, a phone number, a token or a quoted request body (PII-in-logs, BAPert 76553 group 1). The correlationId still ties the response to the line.
|
|
26
|
+
*/
|
|
23
27
|
export declare function logExceptionWithCorrelation(tag: string, err: unknown): string;
|
|
24
28
|
/** Whitelisted projection of an IDP error envelope: code / message / resolution / request_id only. */
|
|
25
29
|
export declare function projectIdpError(json: unknown): IdpErrorProjection;
|
|
@@ -23,11 +23,16 @@ exports.projectResetTokenInvalid = projectResetTokenInvalid;
|
|
|
23
23
|
exports.projectPasswordPolicyRules = projectPasswordPolicyRules;
|
|
24
24
|
exports.projectPasswordBreached = projectPasswordBreached;
|
|
25
25
|
const password_rules_1 = require("./password-rules");
|
|
26
|
+
const log_safe_1 = require("./log-safe");
|
|
26
27
|
const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
27
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Log an exception with a fresh correlationId and return the id for the response body.
|
|
30
|
+
* The log line carries the error's NAME and closed code/status only (errForLog), never its message, stack or cause: those can hold an
|
|
31
|
+
* email, a phone number, a token or a quoted request body (PII-in-logs, BAPert 76553 group 1). The correlationId still ties the response to the line.
|
|
32
|
+
*/
|
|
28
33
|
function logExceptionWithCorrelation(tag, err) {
|
|
29
34
|
const correlationId = globalThis.crypto.randomUUID();
|
|
30
|
-
console.error(tag, { correlationId, error: err });
|
|
35
|
+
console.error(tag, { correlationId, error: (0, log_safe_1.errForLog)(err) });
|
|
31
36
|
return correlationId;
|
|
32
37
|
}
|
|
33
38
|
/** Whitelisted projection of an IDP error envelope: code / message / resolution / request_id only. */
|
|
@@ -44,6 +44,7 @@ const app_slug_1 = require("./app-slug");
|
|
|
44
44
|
const token_utils_1 = require("../auth/utils/token-utils");
|
|
45
45
|
const token_log_id_1 = require("./token-log-id");
|
|
46
46
|
const refresh_in_doubt_1 = require("./refresh-in-doubt");
|
|
47
|
+
const log_safe_1 = require("./log-safe");
|
|
47
48
|
// Use app-slug prefixes for multi-app isolation
|
|
48
49
|
const getSessionKey = (token) => `${(0, app_slug_1.getSessionPrefix)()}${token}`;
|
|
49
50
|
const getRefreshLockKey = (token) => `${(0, app_slug_1.getRefreshLockPrefix)()}${token}`;
|
|
@@ -79,7 +80,7 @@ async function createSession(data) {
|
|
|
79
80
|
.exec();
|
|
80
81
|
}
|
|
81
82
|
catch (error) {
|
|
82
|
-
console.error('[SESSION-STORE] Failed to create session:', error);
|
|
83
|
+
console.error('[SESSION-STORE] Failed to create session:', (0, log_safe_1.errForLog)(error));
|
|
83
84
|
throw error;
|
|
84
85
|
}
|
|
85
86
|
return sessionToken;
|
|
@@ -54,7 +54,6 @@ export declare const siteEvents: {
|
|
|
54
54
|
}) => void;
|
|
55
55
|
loginFailed: (opts: {
|
|
56
56
|
reason: string;
|
|
57
|
-
email?: string;
|
|
58
57
|
url?: string;
|
|
59
58
|
user_agent?: string;
|
|
60
59
|
ip_address?: string;
|
|
@@ -106,9 +105,7 @@ export declare const siteEvents: {
|
|
|
106
105
|
ip_address?: string;
|
|
107
106
|
}) => void;
|
|
108
107
|
error: (opts: {
|
|
109
|
-
|
|
110
|
-
error?: string;
|
|
111
|
-
stack?: string;
|
|
108
|
+
code: string;
|
|
112
109
|
user_id?: string | number;
|
|
113
110
|
url?: string;
|
|
114
111
|
user_agent?: string;
|
|
@@ -139,7 +136,6 @@ declare const _default: {
|
|
|
139
136
|
}) => void;
|
|
140
137
|
loginFailed: (opts: {
|
|
141
138
|
reason: string;
|
|
142
|
-
email?: string;
|
|
143
139
|
url?: string;
|
|
144
140
|
user_agent?: string;
|
|
145
141
|
ip_address?: string;
|
|
@@ -191,9 +187,7 @@ declare const _default: {
|
|
|
191
187
|
ip_address?: string;
|
|
192
188
|
}) => void;
|
|
193
189
|
error: (opts: {
|
|
194
|
-
|
|
195
|
-
error?: string;
|
|
196
|
-
stack?: string;
|
|
190
|
+
code: string;
|
|
197
191
|
user_id?: string | number;
|
|
198
192
|
url?: string;
|
|
199
193
|
user_agent?: string;
|
package/dist/lib/site-logger.js
CHANGED
|
@@ -16,6 +16,7 @@ exports.configureSiteLogger = configureSiteLogger;
|
|
|
16
16
|
exports.logSiteEvent = logSiteEvent;
|
|
17
17
|
exports.getClientIp = getClientIp;
|
|
18
18
|
const redis_1 = require("./redis");
|
|
19
|
+
const log_safe_1 = require("./log-safe");
|
|
19
20
|
// Redis key for site logs (separate from data_logs)
|
|
20
21
|
const REDIS_SITE_LOG_KEY = 'vibe:site-logs:pending';
|
|
21
22
|
const REDIS_LOG_TTL = 7 * 24 * 60 * 60; // 1 week
|
|
@@ -53,12 +54,12 @@ async function logSiteEvent(entry) {
|
|
|
53
54
|
console.log('[site-logger] Redis push success, queue length:', result);
|
|
54
55
|
redis.expire(REDIS_SITE_LOG_KEY, REDIS_LOG_TTL).catch(() => { });
|
|
55
56
|
}).catch(err => {
|
|
56
|
-
console.error('[site-logger] Redis push failed:',
|
|
57
|
+
console.error('[site-logger] Redis push failed:', (0, log_safe_1.errForLog)(err));
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
catch (error) {
|
|
60
61
|
// Fail silently - logging should never break the app
|
|
61
|
-
console.error('[site-logger] Failed to log event:', error);
|
|
62
|
+
console.error('[site-logger] Failed to log event:', (0, log_safe_1.errForLog)(error));
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
/**
|
|
@@ -94,12 +95,14 @@ exports.siteEvents = {
|
|
|
94
95
|
ip_address: opts.ip_address,
|
|
95
96
|
});
|
|
96
97
|
},
|
|
98
|
+
// BREAKING (PII-in-logs group 3b, BAPert 76553 Q2): no `email` parameter. A failed login is told apart by a CLOSED reason code (UPPER_SNAKE),
|
|
99
|
+
// never by the address that was tried; anything that is not a closed code is stored as UNSPECIFIED. A caller that still passes `email` fails to compile.
|
|
97
100
|
loginFailed: (opts) => {
|
|
98
101
|
logSiteEvent({
|
|
99
102
|
level: 'warn',
|
|
100
103
|
category: 'auth',
|
|
101
104
|
message: 'Login failed',
|
|
102
|
-
context: { reason: opts.reason
|
|
105
|
+
context: { reason: (0, log_safe_1.closedCodeForLog)(opts.reason) ?? 'UNSPECIFIED' },
|
|
103
106
|
url: opts.url || '/auth/login',
|
|
104
107
|
user_agent: opts.user_agent,
|
|
105
108
|
ip_address: opts.ip_address,
|
|
@@ -181,12 +184,15 @@ exports.siteEvents = {
|
|
|
181
184
|
ip_address: opts.ip_address,
|
|
182
185
|
});
|
|
183
186
|
},
|
|
187
|
+
// BREAKING (PII-in-logs group 3b, BAPert 76553 Q2): takes a CLOSED code, not caller text, an error string or a stack. The stored message is the fixed
|
|
188
|
+
// sentence 'Application error'; the code (UPPER_SNAKE, else UNSPECIFIED) goes in context. A caller that still passes `message`, `error` or `stack`
|
|
189
|
+
// fails to compile.
|
|
184
190
|
error: (opts) => {
|
|
185
191
|
logSiteEvent({
|
|
186
192
|
level: 'error',
|
|
187
193
|
category: 'error',
|
|
188
|
-
message:
|
|
189
|
-
context: {
|
|
194
|
+
message: 'Application error',
|
|
195
|
+
context: { code: (0, log_safe_1.closedCodeForLog)(opts.code) ?? 'UNSPECIFIED' },
|
|
190
196
|
user_id: opts.user_id,
|
|
191
197
|
url: opts.url,
|
|
192
198
|
user_agent: opts.user_agent,
|
|
@@ -48,6 +48,7 @@ exports.extractApiItems = extractApiItems;
|
|
|
48
48
|
// NO MORE GUESSING data.data.data.data - EVER AGAIN!
|
|
49
49
|
// ========================================================================================
|
|
50
50
|
const better_auth_client_1 = require("../client/better-auth-client");
|
|
51
|
+
const log_safe_1 = require("./log-safe");
|
|
51
52
|
/** Unwrap Better Auth getSession() to a flat session-like object for backward compat */
|
|
52
53
|
async function getSession() {
|
|
53
54
|
const { data } = await better_auth_client_1.authClient.getSession();
|
|
@@ -172,7 +173,7 @@ function scheduleLoginRedirect(isImmediate = false) {
|
|
|
172
173
|
window.location.href = `/account-auth/login?returnUrl=${returnUrl}`;
|
|
173
174
|
}
|
|
174
175
|
catch (error) {
|
|
175
|
-
console.error('❌ Error during login redirect:', error);
|
|
176
|
+
console.error('❌ Error during login redirect:', (0, log_safe_1.errForLog)(error));
|
|
176
177
|
// Final fallback
|
|
177
178
|
try {
|
|
178
179
|
window.location.href = '/account-auth/login';
|
|
@@ -640,7 +641,7 @@ class StandardizedClientApiService {
|
|
|
640
641
|
throw error;
|
|
641
642
|
}
|
|
642
643
|
// Wrap unknown errors as network errors
|
|
643
|
-
console.error('❌ API request failed:', error);
|
|
644
|
+
console.error('❌ API request failed:', (0, log_safe_1.errForLog)(error));
|
|
644
645
|
throw new ApiNetworkError(error instanceof Error ? error.message : 'Network error', 0, endpoint);
|
|
645
646
|
}
|
|
646
647
|
}
|
|
@@ -29,6 +29,7 @@ const redis_1 = require("./redis");
|
|
|
29
29
|
const app_slug_1 = require("./app-slug");
|
|
30
30
|
const token_log_id_1 = require("./token-log-id");
|
|
31
31
|
const refresh_window_1 = require("./refresh-window");
|
|
32
|
+
const log_safe_1 = require("./log-safe");
|
|
32
33
|
// "needs refresh" threshold: THE one freshness window (lib/refresh-window), the same value the refresh route, ensureFreshAccessToken and idp-fetch use.
|
|
33
34
|
const REFRESH_THRESHOLD_MS = refresh_window_1.REFRESH_WINDOW_MS;
|
|
34
35
|
// Concurrent refresh handling configuration
|
|
@@ -358,7 +359,7 @@ async function ensureFreshToken(request) {
|
|
|
358
359
|
}
|
|
359
360
|
catch (e) {
|
|
360
361
|
// If we can't decode, proceed with normal logic
|
|
361
|
-
console.warn('[TOKEN_LIFECYCLE] Could not validate JWT exp claim:', e);
|
|
362
|
+
console.warn('[TOKEN_LIFECYCLE] Could not validate JWT exp claim:', (0, log_safe_1.errForLog)(e));
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
console.log('[TOKEN_LIFECYCLE] ensureFreshToken check:', {
|
|
@@ -429,7 +430,7 @@ async function ensureFreshToken(request) {
|
|
|
429
430
|
};
|
|
430
431
|
}
|
|
431
432
|
catch (error) {
|
|
432
|
-
console.error('[TOKEN_LIFECYCLE] Error:', error);
|
|
433
|
+
console.error('[TOKEN_LIFECYCLE] Error:', (0, log_safe_1.errForLog)(error));
|
|
433
434
|
return {
|
|
434
435
|
success: false,
|
|
435
436
|
error: 'NO_SESSION',
|