@payez/next-mvp 4.10.0 → 4.11.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 +31 -0
- package/dist/api-handlers/auth/refresh.d.ts +17 -58
- package/dist/api-handlers/auth/refresh.js +71 -772
- package/dist/auth/utils/token-utils.d.ts +1 -1
- package/dist/auth/utils/token-utils.js +3 -2
- package/dist/lib/api-handler.js +4 -3
- package/dist/lib/ensure-fresh-access-token.d.ts +24 -1
- package/dist/lib/ensure-fresh-access-token.js +194 -28
- package/dist/lib/idp-fetch.js +3 -2
- package/dist/lib/refresh-in-doubt.d.ts +31 -0
- package/dist/lib/refresh-in-doubt.js +81 -0
- package/dist/lib/refresh-token-validator.js +2 -1
- package/dist/lib/refresh-window.d.ts +16 -0
- package/dist/lib/refresh-window.js +19 -0
- package/dist/lib/safe-trace-id.d.ts +11 -0
- package/dist/lib/safe-trace-id.js +23 -0
- package/dist/lib/session-store.js +14 -4
- package/dist/lib/token-lifecycle.js +3 -2
- package/dist/models/SessionModel.d.ts +7 -0
- package/dist/routes/auth/refresh.d.ts +1 -15
- package/dist/server/auth.d.ts +1 -1
- package/dist/stores/authStore.js +3 -2
- package/package.json +1 -1
- package/scripts/verify-release-provenance.mjs +1 -0
- package/src/api-handlers/auth/refresh.ts +72 -812
- package/src/auth/utils/token-utils.ts +3 -2
- package/src/lib/api-handler.ts +4 -3
- package/src/lib/ensure-fresh-access-token.ts +213 -29
- package/src/lib/idp-fetch.ts +3 -2
- package/src/lib/refresh-in-doubt.ts +79 -0
- package/src/lib/refresh-token-validator.ts +2 -1
- package/src/lib/refresh-window.ts +16 -0
- package/src/lib/safe-trace-id.ts +21 -0
- package/src/lib/session-store.ts +15 -3
- package/src/lib/token-lifecycle.ts +3 -2
- package/src/models/SessionModel.ts +7 -0
- package/src/server/auth.ts +1 -1
- package/src/stores/authStore.ts +3 -2
package/src/lib/session-store.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { SessionData } from '../models/SessionModel';
|
|
|
14
14
|
import { getSessionPrefix, getRefreshLockPrefix, getAppSlug } from './app-slug';
|
|
15
15
|
import { extractKidFromToken, decodeIdpAccessToken } from '../auth/utils/token-utils';
|
|
16
16
|
import { tokenLogId } from './token-log-id';
|
|
17
|
+
import { refreshTokenIdentity } from './refresh-in-doubt';
|
|
17
18
|
|
|
18
19
|
// Re-export SessionData for consumers
|
|
19
20
|
export type { SessionData } from '../models/SessionModel';
|
|
@@ -293,12 +294,23 @@ export async function deleteSessionEverywhere(sessionToken: string, appSlug?: st
|
|
|
293
294
|
* today; this is the defence BAPert ruled to keep anyway.
|
|
294
295
|
*/
|
|
295
296
|
function dropStaleDiscardMarker<T extends object>(session: T): T {
|
|
296
|
-
|
|
297
|
+
let s = session as any;
|
|
297
298
|
if (typeof s.idpRefreshToken === 'string' && s.idpRefreshToken.length > 0 && (s.refreshTokenClearedReason !== undefined || s.refreshTokenClearedAt !== undefined)) {
|
|
298
299
|
const { refreshTokenClearedReason: _reason, refreshTokenClearedAt: _at, ...rest } = s;
|
|
299
|
-
|
|
300
|
+
s = rest;
|
|
300
301
|
}
|
|
301
|
-
|
|
302
|
+
// BAPert 76403: the refresh-IN-DOUBT marker (lib/refresh-in-doubt) is cleared by the same write that stores a NEW refresh token, i.e. one
|
|
303
|
+
// whose identity differs from the one left in doubt. A write that stores the SAME token (any caller spreading the whole row back) does
|
|
304
|
+
// NOT clear it: that token is still the in-doubt one.
|
|
305
|
+
if (
|
|
306
|
+
typeof s.refreshInDoubtTokenHash === 'string' &&
|
|
307
|
+
typeof s.idpRefreshToken === 'string' && s.idpRefreshToken.length > 0 &&
|
|
308
|
+
refreshTokenIdentity(s.idpRefreshToken) !== s.refreshInDoubtTokenHash
|
|
309
|
+
) {
|
|
310
|
+
const { refreshInDoubtTokenHash: _hash, refreshInDoubtAt: _doubtAt, ...rest } = s;
|
|
311
|
+
s = rest;
|
|
312
|
+
}
|
|
313
|
+
return s as T;
|
|
302
314
|
}
|
|
303
315
|
|
|
304
316
|
export async function setSession(sessionToken: string, data: SessionData): Promise<void> {
|
|
@@ -26,9 +26,10 @@ import { getSession as getBetterAuthSession } from '../server/auth';
|
|
|
26
26
|
import { getRedis } from './redis';
|
|
27
27
|
import { getAppSlug } from './app-slug';
|
|
28
28
|
import { tokenLogId } from './token-log-id';
|
|
29
|
+
import { REFRESH_WINDOW_MS } from './refresh-window';
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
-
const REFRESH_THRESHOLD_MS =
|
|
31
|
+
// "needs refresh" threshold: THE one freshness window (lib/refresh-window), the same value the refresh route, ensureFreshAccessToken and idp-fetch use.
|
|
32
|
+
const REFRESH_THRESHOLD_MS = REFRESH_WINDOW_MS;
|
|
32
33
|
|
|
33
34
|
// Concurrent refresh handling configuration
|
|
34
35
|
const CONCURRENT_REFRESH_POLL_INTERVAL_MS = 200; // How often to poll session during concurrent refresh
|
|
@@ -59,6 +59,13 @@ export interface SessionData {
|
|
|
59
59
|
|
|
60
60
|
/** When the IDP refresh token expires (Unix timestamp ms) */
|
|
61
61
|
idpRefreshTokenExpires?: number;
|
|
62
|
+
/**
|
|
63
|
+
* BAPert 76403 (lib/refresh-in-doubt): set when a refresh fetch TIMED OUT. A SHA-256 identity of the refresh token that was sent (never the
|
|
64
|
+
* token), and when. A later attempt whose token-to-send matches refuses without calling the IdP. Cleared by the write that stores a different
|
|
65
|
+
* refresh token.
|
|
66
|
+
*/
|
|
67
|
+
refreshInDoubtTokenHash?: string;
|
|
68
|
+
refreshInDoubtAt?: number;
|
|
62
69
|
|
|
63
70
|
/** Decoded IDP access token claims (for quick access without re-decoding) */
|
|
64
71
|
decodedAccessToken?: any;
|
package/src/server/auth.ts
CHANGED
|
@@ -407,7 +407,7 @@ export interface FreshIdpTokenConfig {
|
|
|
407
407
|
idpBaseUrl: string;
|
|
408
408
|
clientId: string;
|
|
409
409
|
refreshEndpoint?: string;
|
|
410
|
-
/** Refresh if the access token is within this many ms of expiry. Default
|
|
410
|
+
/** Refresh if the access token is within this many ms of expiry. Default REFRESH_WINDOW_MS (5 min, lib/refresh-window). */
|
|
411
411
|
safetyWindowMs?: number;
|
|
412
412
|
}
|
|
413
413
|
|
package/src/stores/authStore.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getSecureCsrfCookieName
|
|
28
28
|
} from '../lib/app-slug';
|
|
29
29
|
import { tokenLogId } from '../lib/token-log-id';
|
|
30
|
+
import { REFRESH_WINDOW_MS } from '../lib/refresh-window';
|
|
30
31
|
|
|
31
32
|
// ===============================
|
|
32
33
|
// INTERFACES & TYPES
|
|
@@ -752,8 +753,8 @@ export const useAuthStore = create<AuthStore>()(
|
|
|
752
753
|
const now = Date.now();
|
|
753
754
|
const timeUntilExpiry = tokenExpiry - now;
|
|
754
755
|
|
|
755
|
-
// Token should be fresh (not expiring
|
|
756
|
-
if (timeUntilExpiry <
|
|
756
|
+
// Token should be fresh (not expiring within the one refresh window)
|
|
757
|
+
if (timeUntilExpiry < REFRESH_WINDOW_MS) {
|
|
757
758
|
throw new Error(`Fresh token still expires soon: ${timeUntilExpiry}ms`);
|
|
758
759
|
}
|
|
759
760
|
|