@payez/next-mvp 4.4.8 → 4.5.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/README.md +53 -0
- package/dist/api-handlers/account/reset-password.js +3 -1
- package/dist/api-handlers/onboarding/send-email-code.d.ts +11 -0
- package/dist/api-handlers/onboarding/send-email-code.js +11 -0
- package/dist/api-handlers/onboarding/set-password.d.ts +12 -0
- package/dist/api-handlers/onboarding/set-password.js +15 -0
- package/dist/api-handlers/onboarding/verify-code.d.ts +11 -0
- package/dist/api-handlers/onboarding/verify-code.js +14 -0
- package/dist/api-handlers/onboarding/verify-welcome-email.d.ts +10 -0
- package/dist/api-handlers/onboarding/verify-welcome-email.js +11 -0
- package/dist/api-handlers/session/viability.js +32 -0
- package/dist/auth/password-reset-bridge.d.ts +70 -0
- package/dist/auth/password-reset-bridge.js +99 -0
- package/dist/auth/route-config.js +17 -0
- package/dist/components/auth/FederatedAuthSection.js +50 -21
- package/dist/components/auth/PasswordResetBridge.d.ts +17 -0
- package/dist/components/auth/PasswordResetBridge.js +32 -0
- package/dist/hooks/useAvailableProviders.js +39 -17
- package/dist/lib/onboarding-proxy.d.ts +30 -0
- package/dist/lib/onboarding-proxy.js +62 -0
- package/dist/lib/safe-error-response.d.ts +8 -0
- package/dist/lib/safe-error-response.js +27 -0
- package/dist/middleware/create-middleware.d.ts +41 -0
- package/dist/middleware/create-middleware.js +74 -2
- package/dist/pages/login/page.js +1 -1
- package/dist/pages/onboarding/index.d.ts +11 -0
- package/dist/pages/onboarding/index.js +20 -0
- package/dist/pages/onboarding/page.d.ts +1 -0
- package/dist/pages/onboarding/page.js +118 -0
- package/dist/pages/recovery/page.js +20 -7
- package/dist/routes/auth/viability.d.ts +5 -0
- package/dist/routes/auth/viability.js +53 -0
- package/dist/routes/onboarding/index.d.ts +15 -0
- package/dist/routes/onboarding/index.js +22 -0
- package/dist/routes/onboarding/send-email-code.d.ts +2 -0
- package/dist/routes/onboarding/send-email-code.js +6 -0
- package/dist/routes/onboarding/set-password.d.ts +2 -0
- package/dist/routes/onboarding/set-password.js +6 -0
- package/dist/routes/onboarding/verify-code.d.ts +2 -0
- package/dist/routes/onboarding/verify-code.js +6 -0
- package/dist/routes/onboarding/verify-welcome-email.d.ts +2 -0
- package/dist/routes/onboarding/verify-welcome-email.js +6 -0
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.js +10 -2
- package/dist/vibe/sessions.d.ts +88 -1
- package/dist/vibe/sessions.js +260 -19
- package/package.json +58 -2
- package/scripts/check-publish-eol.mjs +135 -0
- package/src/api-handlers/account/reset-password.ts +4 -2
- package/src/api-handlers/onboarding/send-email-code.ts +18 -0
- package/src/api-handlers/onboarding/set-password.ts +21 -0
- package/src/api-handlers/onboarding/verify-code.ts +20 -0
- package/src/api-handlers/onboarding/verify-welcome-email.ts +17 -0
- package/src/api-handlers/session/viability.ts +31 -0
- package/src/auth/password-reset-bridge.ts +98 -0
- package/src/auth/route-config.ts +17 -0
- package/src/components/auth/FederatedAuthSection.tsx +56 -22
- package/src/components/auth/PasswordResetBridge.tsx +30 -0
- package/src/hooks/useAvailableProviders.ts +41 -18
- package/src/lib/onboarding-proxy.ts +57 -0
- package/src/lib/safe-error-response.ts +23 -0
- package/src/middleware/create-middleware.ts +106 -2
- package/src/pages/login/page.tsx +4 -7
- package/src/pages/onboarding/index.ts +11 -0
- package/src/pages/onboarding/page.tsx +212 -0
- package/src/pages/recovery/page.tsx +24 -8
- package/src/routes/auth/viability.ts +52 -0
- package/src/routes/onboarding/index.ts +15 -0
- package/src/routes/onboarding/send-email-code.ts +2 -0
- package/src/routes/onboarding/set-password.ts +2 -0
- package/src/routes/onboarding/verify-code.ts +2 -0
- package/src/routes/onboarding/verify-welcome-email.ts +2 -0
- package/src/utils/api.ts +10 -1
- package/src/vibe/document-unwrap.ts +103 -103
- package/src/vibe/sessions.ts +291 -24
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* @since auth-ready-v2
|
|
15
15
|
*/
|
|
16
16
|
import { NextRequest, NextResponse } from 'next/server';
|
|
17
|
+
import { type RevocationStatus } from '../../vibe/sessions';
|
|
17
18
|
/**
|
|
18
19
|
* GET /api/session/viability - Check if session is viable
|
|
19
20
|
*
|
|
@@ -34,6 +35,9 @@ export declare function GET(req: NextRequest): Promise<NextResponse<{
|
|
|
34
35
|
hasRefreshToken: boolean;
|
|
35
36
|
authenticated: boolean;
|
|
36
37
|
sessionTokenId: string | null;
|
|
38
|
+
revocationStatus: RevocationStatus;
|
|
39
|
+
forceInvalid: boolean;
|
|
40
|
+
revoked: boolean;
|
|
37
41
|
tenantRequiresTwoFactor: boolean;
|
|
38
42
|
userHasCompletedTenantTwoFactorRequirements: boolean;
|
|
39
43
|
userStillNeedsTwoFactor: boolean;
|
|
@@ -49,6 +53,7 @@ export declare function GET(req: NextRequest): Promise<NextResponse<{
|
|
|
49
53
|
viable: boolean;
|
|
50
54
|
needsRefresh: boolean;
|
|
51
55
|
authenticated: boolean;
|
|
56
|
+
revocationStatus: RevocationStatus;
|
|
52
57
|
error: string;
|
|
53
58
|
code: string;
|
|
54
59
|
correlationId: string;
|
|
@@ -22,6 +22,7 @@ const session_store_1 = require("../../lib/session-store");
|
|
|
22
22
|
const idp_client_config_1 = require("../../lib/idp-client-config");
|
|
23
23
|
const safe_error_response_1 = require("../../lib/safe-error-response");
|
|
24
24
|
const token_log_id_1 = require("../../lib/token-log-id");
|
|
25
|
+
const sessions_1 = require("../../vibe/sessions");
|
|
25
26
|
/**
|
|
26
27
|
* Get tenant-wide 2FA requirement from cached client config (from broker handshake)
|
|
27
28
|
*/
|
|
@@ -248,6 +249,41 @@ async function GET(req) {
|
|
|
248
249
|
const oauthSessionNeedsTwoFactor = session?.mfaSignalPresent === true &&
|
|
249
250
|
session?.mfaVerified === false &&
|
|
250
251
|
!(session.idpRefreshToken || session?.refreshToken);
|
|
252
|
+
// --- Session revocation status (PAY-1882 step 4 producer half) ---
|
|
253
|
+
/*
|
|
254
|
+
* Resolve the four-state revocation status for this user (BAPert 66320).
|
|
255
|
+
* Read it HERE, above every authenticated return, so all three responses
|
|
256
|
+
* carry the SAME value - the same discipline as nextStep above.
|
|
257
|
+
*
|
|
258
|
+
* The middleware fails CLOSED by default on 'unknown' (a failed Vibe read)
|
|
259
|
+
* and always on 'revoked'; 'active' and 'not_configured' stay viable. A
|
|
260
|
+
* consumer that does NOT read revocationStatus is unaffected - absent is not
|
|
261
|
+
* revoked - so shipping this changes nothing for them until they opt in.
|
|
262
|
+
*/
|
|
263
|
+
let revocationStatus = 'not_configured';
|
|
264
|
+
if (session) {
|
|
265
|
+
// BAPert 66480 condition 2 / ruling (ii) 66700: the numeric IdP id comes from
|
|
266
|
+
// the token's own `sub` claim FIRST (session.userId can be an email).
|
|
267
|
+
const revocationUserId = (0, sessions_1.resolveNumericIdpUserId)(session, session.idpAccessToken);
|
|
268
|
+
if (revocationUserId > 0) {
|
|
269
|
+
try {
|
|
270
|
+
revocationStatus = await (0, sessions_1.getRevocationStatus)(revocationUserId, sessionToken);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
// getRevocationStatus does not throw by contract, but never let a
|
|
274
|
+
// revocation probe take down the viability answer: fail closed.
|
|
275
|
+
revocationStatus = 'unknown';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// N1 (NightHawk 66392): a session exists but carries no usable numeric
|
|
280
|
+
// IdP id, so the check could not run -> 'unknown' (could not determine),
|
|
281
|
+
// NOT 'not_configured' (no revocation source).
|
|
282
|
+
// BAPert 66480 condition 1: emit ONE WARN (shape only) so a non-numeric
|
|
283
|
+
// id surfaces as a log line, not an unexplained sign-out.
|
|
284
|
+
revocationStatus = (0, sessions_1.warnNonNumericUserId)(sessionToken, session.userId);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
251
287
|
// Check access token expiry
|
|
252
288
|
const now = Math.floor(Date.now() / 1000);
|
|
253
289
|
// 240654 addendum 2: fall back to the Redis session's own expiry.
|
|
@@ -292,6 +328,11 @@ async function GET(req) {
|
|
|
292
328
|
needsRefresh: true,
|
|
293
329
|
authenticated: true,
|
|
294
330
|
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null,
|
|
331
|
+
// Session revocation (PAY-1882 step 4): the four-state status, plus the
|
|
332
|
+
// boolean the middleware's existing force-invalid branch already reads.
|
|
333
|
+
revocationStatus,
|
|
334
|
+
forceInvalid: revocationStatus === 'revoked' || revocationStatus === 'unknown',
|
|
335
|
+
revoked: revocationStatus === 'revoked',
|
|
295
336
|
// Clear names for middleware decision-making
|
|
296
337
|
tenantRequiresTwoFactor,
|
|
297
338
|
userHasCompletedTenantTwoFactorRequirements,
|
|
@@ -363,6 +404,10 @@ async function GET(req) {
|
|
|
363
404
|
twoFactorComplete: userHasCompletedTenantTwoFactorRequirements,
|
|
364
405
|
accessTokenExpired: true,
|
|
365
406
|
reason: 'Token expired',
|
|
407
|
+
// Session revocation (PAY-1882 step 4) - same value on every return.
|
|
408
|
+
revocationStatus,
|
|
409
|
+
forceInvalid: revocationStatus === 'revoked' || revocationStatus === 'unknown',
|
|
410
|
+
revoked: revocationStatus === 'revoked',
|
|
366
411
|
// RBAC fields
|
|
367
412
|
roles: session?.roles || [],
|
|
368
413
|
clientId: resolveClientId(session?.idpClientId),
|
|
@@ -375,6 +420,10 @@ async function GET(req) {
|
|
|
375
420
|
hasRefreshToken,
|
|
376
421
|
authenticated: true,
|
|
377
422
|
sessionTokenId: sessionToken ? (0, token_log_id_1.tokenLogId)(sessionToken) : null,
|
|
423
|
+
// Session revocation (PAY-1882 step 4) - same value on every return.
|
|
424
|
+
revocationStatus,
|
|
425
|
+
forceInvalid: revocationStatus === 'revoked' || revocationStatus === 'unknown',
|
|
426
|
+
revoked: revocationStatus === 'revoked',
|
|
378
427
|
// Clear names
|
|
379
428
|
tenantRequiresTwoFactor,
|
|
380
429
|
userHasCompletedTenantTwoFactorRequirements,
|
|
@@ -398,6 +447,10 @@ async function GET(req) {
|
|
|
398
447
|
viable: false,
|
|
399
448
|
needsRefresh: false,
|
|
400
449
|
authenticated: false,
|
|
450
|
+
// PAY-1882 step 4: an exception here means the status could not be read.
|
|
451
|
+
// Report 'unknown' (not a bare 500 with no revocation signal) so the
|
|
452
|
+
// middleware's fail-closed default applies rather than reading it as viable.
|
|
453
|
+
revocationStatus: 'unknown',
|
|
401
454
|
error: 'Failed to check session',
|
|
402
455
|
code: 'INTERNAL_ERROR',
|
|
403
456
|
correlationId
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ready-to-use onboarding route handlers (PAY-2121).
|
|
3
|
+
*
|
|
4
|
+
* Import directly into your app's API routes with zero configuration:
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // app/api/onboarding/verify-code/route.ts
|
|
9
|
+
* export { POST } from '@payez/next-mvp/routes/onboarding/verify-code';
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export { POST as verifyCodePOST } from '../../api-handlers/onboarding/verify-code';
|
|
13
|
+
export { POST as sendEmailCodePOST } from '../../api-handlers/onboarding/send-email-code';
|
|
14
|
+
export { POST as verifyWelcomeEmailPOST } from '../../api-handlers/onboarding/verify-welcome-email';
|
|
15
|
+
export { POST as setPasswordPOST } from '../../api-handlers/onboarding/set-password';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setPasswordPOST = exports.verifyWelcomeEmailPOST = exports.sendEmailCodePOST = exports.verifyCodePOST = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Ready-to-use onboarding route handlers (PAY-2121).
|
|
6
|
+
*
|
|
7
|
+
* Import directly into your app's API routes with zero configuration:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // app/api/onboarding/verify-code/route.ts
|
|
12
|
+
* export { POST } from '@payez/next-mvp/routes/onboarding/verify-code';
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
var verify_code_1 = require("../../api-handlers/onboarding/verify-code");
|
|
16
|
+
Object.defineProperty(exports, "verifyCodePOST", { enumerable: true, get: function () { return verify_code_1.POST; } });
|
|
17
|
+
var send_email_code_1 = require("../../api-handlers/onboarding/send-email-code");
|
|
18
|
+
Object.defineProperty(exports, "sendEmailCodePOST", { enumerable: true, get: function () { return send_email_code_1.POST; } });
|
|
19
|
+
var verify_welcome_email_1 = require("../../api-handlers/onboarding/verify-welcome-email");
|
|
20
|
+
Object.defineProperty(exports, "verifyWelcomeEmailPOST", { enumerable: true, get: function () { return verify_welcome_email_1.POST; } });
|
|
21
|
+
var set_password_1 = require("../../api-handlers/onboarding/set-password");
|
|
22
|
+
Object.defineProperty(exports, "setPasswordPOST", { enumerable: true, get: function () { return set_password_1.POST; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
/** POST /api/onboarding/send-email-code -> IdP (PAY-2121). */
|
|
5
|
+
var send_email_code_1 = require("../../api-handlers/onboarding/send-email-code");
|
|
6
|
+
Object.defineProperty(exports, "POST", { enumerable: true, get: function () { return send_email_code_1.POST; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
/** POST /api/onboarding/set-password -> IdP (PAY-2121). */
|
|
5
|
+
var set_password_1 = require("../../api-handlers/onboarding/set-password");
|
|
6
|
+
Object.defineProperty(exports, "POST", { enumerable: true, get: function () { return set_password_1.POST; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
/** POST /api/onboarding/verify-code -> IdP (PAY-2121). */
|
|
5
|
+
var verify_code_1 = require("../../api-handlers/onboarding/verify-code");
|
|
6
|
+
Object.defineProperty(exports, "POST", { enumerable: true, get: function () { return verify_code_1.POST; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
/** POST /api/onboarding/verify-welcome-email -> IdP (PAY-2121). */
|
|
5
|
+
var verify_welcome_email_1 = require("../../api-handlers/onboarding/verify-welcome-email");
|
|
6
|
+
Object.defineProperty(exports, "POST", { enumerable: true, get: function () { return verify_welcome_email_1.POST; } });
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ export declare function apiFetch<T>(url: string, options?: ApiOptions): Promise<
|
|
|
33
33
|
*/
|
|
34
34
|
export declare const RECOVERY_EXPIRED_TEXT = "This reset link has expired or was already used. Start password recovery again.";
|
|
35
35
|
export declare const RESET_NEUTRAL_TEXT = "We couldn't set that password. Check it meets the requirements shown and try again.";
|
|
36
|
+
/**
|
|
37
|
+
* PAY-2103 (AUTH-009): the IdP refuses a password found in a breach corpus; the route projects a
|
|
38
|
+
* `password_breached: true` boolean, and the user reads this. Distinct from the policy sentence,
|
|
39
|
+
* which would wrongly tell them to re-read the requirements.
|
|
40
|
+
*/
|
|
41
|
+
export declare const RESET_BREACHED_TEXT = "This password appears in a known data breach. Choose a different one.";
|
|
36
42
|
/**
|
|
37
43
|
* Three tiers for a 400/422 from reset (rigpert 60822, QAPert 60820), decided ONLY on codes the IdP actually sends:
|
|
38
44
|
* policy details.code password_policy (projected by the handler as password_policy) -> the rule sentences
|
package/dist/utils/api.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.accountApi = exports.AccountApi = exports.RESET_NEUTRAL_TEXT = exports.RECOVERY_EXPIRED_TEXT = exports.ApiError = void 0;
|
|
3
|
+
exports.accountApi = exports.AccountApi = exports.RESET_BREACHED_TEXT = exports.RESET_NEUTRAL_TEXT = exports.RECOVERY_EXPIRED_TEXT = exports.ApiError = void 0;
|
|
4
4
|
exports.apiFetch = apiFetch;
|
|
5
5
|
exports.resetFailureText = resetFailureText;
|
|
6
6
|
const standardized_client_api_1 = require("../lib/standardized-client-api");
|
|
@@ -84,6 +84,12 @@ async function apiFetch(url, options = {}) {
|
|
|
84
84
|
*/
|
|
85
85
|
exports.RECOVERY_EXPIRED_TEXT = 'This reset link has expired or was already used. Start password recovery again.';
|
|
86
86
|
exports.RESET_NEUTRAL_TEXT = "We couldn't set that password. Check it meets the requirements shown and try again.";
|
|
87
|
+
/**
|
|
88
|
+
* PAY-2103 (AUTH-009): the IdP refuses a password found in a breach corpus; the route projects a
|
|
89
|
+
* `password_breached: true` boolean, and the user reads this. Distinct from the policy sentence,
|
|
90
|
+
* which would wrongly tell them to re-read the requirements.
|
|
91
|
+
*/
|
|
92
|
+
exports.RESET_BREACHED_TEXT = 'This password appears in a known data breach. Choose a different one.';
|
|
87
93
|
/**
|
|
88
94
|
* Three tiers for a 400/422 from reset (rigpert 60822, QAPert 60820), decided ONLY on codes the IdP actually sends:
|
|
89
95
|
* policy details.code password_policy (projected by the handler as password_policy) -> the rule sentences
|
|
@@ -196,7 +202,9 @@ class AccountApi {
|
|
|
196
202
|
const policy = !!body?.password_policy;
|
|
197
203
|
const rules = (0, password_rules_1.knownPasswordRules)(body?.password_policy?.rules);
|
|
198
204
|
const tokenInvalid = body?.reset_token_invalid === true;
|
|
199
|
-
|
|
205
|
+
// PAY-2103: a breached password is its own outcome, not a policy refusal.
|
|
206
|
+
const breached = body?.password_breached === true;
|
|
207
|
+
const err = new Error(breached ? exports.RESET_BREACHED_TEXT : resetFailureText(res.status, rules, policy, tokenInvalid));
|
|
200
208
|
if (rules.length)
|
|
201
209
|
err.rules = rules;
|
|
202
210
|
if ((res.status === 400 || res.status === 422) && tokenInvalid && !policy)
|
package/dist/vibe/sessions.d.ts
CHANGED
|
@@ -144,8 +144,95 @@ export declare function isSessionValid(sessionId: number, accessToken: string):
|
|
|
144
144
|
* @param accessToken - Access token for auth
|
|
145
145
|
* @param sessionTokenHash - Optional token hash to check specific session
|
|
146
146
|
* @returns Revoked session info if enforcement needed, null otherwise
|
|
147
|
+
*
|
|
148
|
+
* @deprecated PAY-2037 step 1 (BAPert 69153): kept so no consumer breaks. It is a thin call into the
|
|
149
|
+
* same hash-first decision as getRevocationStatus; prefer getRevocationStatus, which distinguishes
|
|
150
|
+
* a failed read ('unknown') from 'active' instead of throwing.
|
|
151
|
+
*/
|
|
152
|
+
export declare function checkSessionRevocation(idpUserId: number, _accessToken: string, sessionTokenHash?: string): Promise<IVibeLoginSession | null>;
|
|
153
|
+
/** The four states a viability producer can report (BAPert 66320). */
|
|
154
|
+
export type RevocationStatus = 'active' | 'revoked' | 'unknown' | 'not_configured';
|
|
155
|
+
/**
|
|
156
|
+
* Hash a session token the same way record-login stores it.
|
|
157
|
+
* SHA256 hex, 64 chars. This must stay byte-identical to
|
|
158
|
+
* `hashSessionToken` in the consumers (idealresume.online/lib/session-token-hash.ts)
|
|
159
|
+
* or per-session matching silently falls back to the user-wide path.
|
|
160
|
+
*/
|
|
161
|
+
export declare function hashSessionToken(token: string): string;
|
|
162
|
+
/**
|
|
163
|
+
* BAPert 66480 condition 2 (ruling (ii), 66700; corrected per QAPert 66729):
|
|
164
|
+
* resolve the NUMERIC IdP user id the Vibe query filters on.
|
|
165
|
+
*
|
|
166
|
+
* *** DO NOT READ `sub` FOR THE USER ID. *** In the IdP's bearer token `sub` is the
|
|
167
|
+
* USERNAME, not the id: AESGCM_Encryption.cs:815 writes `{ "sub", payload.Username }`
|
|
168
|
+
* and :822 writes `{ "user_id", payload.UserId.ToString() }`. A username is commonly
|
|
169
|
+
* an email, so reading `sub` yields 0 for the common case - and for a username that
|
|
170
|
+
* happens to be all digits it yields the WRONG id, which getRevocationStatus answers
|
|
171
|
+
* 'active' for a user with no rows: a fail-OPEN.
|
|
172
|
+
*
|
|
173
|
+
* Resolution order:
|
|
174
|
+
* 1. the token's `user_id` claim, when it is all digits;
|
|
175
|
+
* 2. a digits-only session.userId / session.user.id;
|
|
176
|
+
* 3. otherwise 0 (unresolvable), which the caller reports as 'unknown' (fail closed).
|
|
177
|
+
*
|
|
178
|
+
* session.userId is not a safe source on its own either: session-store.ts:143 falls
|
|
179
|
+
* back to user.email, and better-auth.ts stringifies whichever id it finds.
|
|
180
|
+
*/
|
|
181
|
+
export declare function resolveNumericIdpUserId(session: unknown, accessToken?: string): number;
|
|
182
|
+
/**
|
|
183
|
+
* Public entry point for the twins: call this when a session exists but its
|
|
184
|
+
* userId is not a usable numeric IdP id. Returns 'unknown' (fail closed) and
|
|
185
|
+
* emits the once-per-session WARN. Shape only, never the value.
|
|
186
|
+
*/
|
|
187
|
+
export declare function warnNonNumericUserId(sessionToken: string | undefined, rawUserId: unknown): 'unknown';
|
|
188
|
+
/**
|
|
189
|
+
* HMAC-ONLY session read for the revocation check (PAY-1882 step 4).
|
|
190
|
+
*
|
|
191
|
+
* *** DO NOT "SIMPLIFY" THIS ONTO vibeProxyRequest/getUserSessions. ***
|
|
192
|
+
* It is deliberately separate and deliberately sends NO Authorization header.
|
|
193
|
+
*
|
|
194
|
+
* WHY: the twin this must match is idealresume.online's checkSessionRevocation
|
|
195
|
+
* read. That repo's vibe-client adds `Authorization: Bearer` ONLY for member
|
|
196
|
+
* paths (`path.startsWith('/v1/my/')`) - see idealresume.online/lib/vibe-client.ts
|
|
197
|
+
* :207-210 - so its /v1/collections/.../query for this check goes out HMAC-only.
|
|
198
|
+
* The comment at vibe-client.ts:188-206 there records why: a bearer on the
|
|
199
|
+
* collections path caused USER_REQUIRED and broke signups. getUserSessions()
|
|
200
|
+
* here ALWAYS sends the bearer, so it cannot stand in for this read without
|
|
201
|
+
* changing the credential the two twins are supposed to share.
|
|
202
|
+
*
|
|
203
|
+
* BAPert 66344 (option a): match the twin, HMAC-only.
|
|
204
|
+
*
|
|
205
|
+
* Also distinguishes a FAILED read from an EMPTY one - getUserSessions()
|
|
206
|
+
* collapses a proxy outage and "user has no sessions" to `[]`, which is
|
|
207
|
+
* fail-OPEN for a revocation decision. Returns `ok:false` on an outage so the
|
|
208
|
+
* caller fails closed, and `ok:true, sessions:[]` only for a healthy empty.
|
|
209
|
+
*/
|
|
210
|
+
export declare function readSessionRevocationHmac(idpUserId: number): Promise<{
|
|
211
|
+
ok: boolean;
|
|
212
|
+
sessions: IVibeLoginSession[];
|
|
213
|
+
}>;
|
|
214
|
+
/**
|
|
215
|
+
* Determine whether a session is revoked, with an explicit UNKNOWN state
|
|
216
|
+
* (PAY-1882 step 4 producer half).
|
|
217
|
+
*
|
|
218
|
+
* States:
|
|
219
|
+
* 'not_configured' - this deployment has no revocation source (IDP_URL /
|
|
220
|
+
* VIBE_CLIENT_ID / VIBE_HMAC_KEY absent). Viable; today's
|
|
221
|
+
* behaviour. Logged once, not per request.
|
|
222
|
+
* 'unknown' - revocation IS configured but the read FAILED (non-OK,
|
|
223
|
+
* timeout, throw). Enforcing middleware fails this CLOSED
|
|
224
|
+
* by default (revocationFailMode 'closed'); a Vibe-less
|
|
225
|
+
* consumer is 'not_configured', not 'unknown', so it is not
|
|
226
|
+
* signed out.
|
|
227
|
+
* 'revoked' - the session's own token hash matches a revoked row, or
|
|
228
|
+
* the user has no active session at all.
|
|
229
|
+
* 'active' - read OK and the session is not revoked.
|
|
230
|
+
*
|
|
231
|
+
* @param idpUserId - The user's IDP ID
|
|
232
|
+
* @param sessionToken - The live session token; hashed to match the stored row.
|
|
233
|
+
* When absent, the user-wide check applies (legacy path).
|
|
147
234
|
*/
|
|
148
|
-
export declare function
|
|
235
|
+
export declare function getRevocationStatus(idpUserId: number, sessionToken?: string): Promise<RevocationStatus>;
|
|
149
236
|
/**
|
|
150
237
|
* Get session statistics for admin dashboard.
|
|
151
238
|
*
|