@payez/next-mvp 4.1.21 → 4.4.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/POST-INSTALL.md +408 -0
- package/README.md +7 -0
- package/dist/api-handlers/account/recovery/initiate.js +35 -2
- package/dist/api-handlers/account/recovery/send-code.d.ts +0 -33
- package/dist/api-handlers/account/recovery/send-code.js +35 -2
- package/dist/api-handlers/account/recovery/verify-code.d.ts +0 -33
- package/dist/api-handlers/account/recovery/verify-code.js +35 -2
- package/dist/api-handlers/account/reset-password.js +35 -2
- package/dist/api-handlers/account/validate-password.js +21 -3
- package/dist/api-handlers/admin/analytics.js +2 -1
- package/dist/api-handlers/admin/audit.js +2 -1
- package/dist/api-handlers/admin/sessions.js +2 -1
- package/dist/api-handlers/admin/stats.js +2 -1
- package/dist/api-handlers/admin/users.js +2 -1
- package/dist/api-handlers/admin/vibe-data.js +2 -1
- package/dist/api-handlers/auth/jwks.js +10 -0
- package/dist/api-handlers/auth/login.js +5 -2
- package/dist/api-handlers/auth/passkey-signin.js +16 -1
- package/dist/api-handlers/auth/refresh.d.ts +23 -3
- package/dist/api-handlers/auth/refresh.js +107 -16
- package/dist/api-handlers/auth/signout.js +3 -2
- package/dist/api-handlers/auth/verify-code.js +5 -2
- package/dist/api-handlers/session/viability.js +27 -1
- package/dist/api-handlers/test/force-expire.d.ts +29 -15
- package/dist/api-handlers/test/force-expire.js +109 -14
- package/dist/auth/better-auth.d.ts +263 -6
- package/dist/auth/better-auth.js +437 -59
- package/dist/auth/utils/idp-client.js +1 -1
- package/dist/client/AuthContext.js +49 -17
- package/dist/client/better-auth-client.d.ts +62 -62
- package/dist/components/account/UserAvatarMenu.js +12 -0
- package/dist/components/auth/ModeAwareLoginPage.js +10 -8
- package/dist/components/auth/ModeAwareSignupPage.js +5 -2
- package/dist/components/auth/TraditionalAuthSection.js +5 -1
- package/dist/hooks/useAuthSettings.js +14 -2
- package/dist/hooks/usePublicAuthSettings.d.ts +2 -2
- package/dist/hooks/usePublicAuthSettings.js +28 -14
- package/dist/lib/client-key.d.ts +61 -0
- package/dist/lib/client-key.js +72 -0
- package/dist/lib/demo-mode.js +2 -1
- package/dist/lib/ensure-fresh-access-token.js +5 -1
- package/dist/lib/idp-client-config.d.ts +25 -2
- package/dist/lib/idp-client-config.js +381 -36
- package/dist/lib/idp-fetch.d.ts +2 -0
- package/dist/lib/idp-fetch.js +31 -1
- package/dist/lib/idp-url-guard.d.ts +29 -0
- package/dist/lib/idp-url-guard.js +38 -0
- package/dist/lib/redis.js +77 -5
- package/dist/lib/session-cookie-shape.d.ts +69 -0
- package/dist/lib/session-cookie-shape.js +126 -0
- package/dist/lib/session-store.d.ts +35 -2
- package/dist/lib/session-store.js +135 -4
- package/dist/lib/single-flight.d.ts +35 -0
- package/dist/lib/single-flight.js +30 -0
- package/dist/lib/token-lifecycle.js +12 -0
- package/dist/middleware/create-middleware.js +12 -0
- package/dist/models/SessionModel.d.ts +41 -0
- package/dist/models/SessionModel.js +9 -0
- package/dist/pages/admin-login/page.d.ts +1 -1
- package/dist/pages/admin-login/page.js +5 -41
- package/dist/pages/security/EnhancedSecurityPage.d.ts +35 -1
- package/dist/pages/security/EnhancedSecurityPage.js +42 -2
- package/dist/pages/security/index.d.ts +1 -0
- package/dist/pages/security/index.js +3 -0
- package/dist/pages/test-env/EmergencyLogoutPage.d.ts +17 -5
- package/dist/pages/test-env/EmergencyLogoutPage.js +17 -5
- package/dist/pages/test-env/JwtInspectPage.d.ts +30 -5
- package/dist/pages/test-env/JwtInspectPage.js +166 -7
- package/dist/pages/test-env/RefreshTokenPage.d.ts +22 -6
- package/dist/pages/test-env/RefreshTokenPage.js +22 -6
- package/dist/pages/test-env/TestEnvPage.d.ts +16 -4
- package/dist/pages/test-env/TestEnvPage.js +16 -4
- package/dist/pages/test-env/guarded.d.ts +4 -0
- package/dist/pages/test-env/guarded.js +86 -0
- package/dist/pages/test-env/index.d.ts +50 -7
- package/dist/pages/test-env/index.js +58 -12
- package/dist/routes/auth/logout.js +3 -2
- package/dist/routes/auth/refresh.js +27 -1
- package/dist/routes/auth/session.d.ts +12 -3
- package/dist/routes/auth/session.js +92 -9
- package/dist/routes/auth/settings.d.ts +34 -4
- package/dist/routes/auth/settings.js +87 -33
- package/dist/routes/auth/viability.d.ts +1 -1
- package/dist/routes/auth/viability.js +122 -11
- package/dist/server/auth-guard.js +22 -15
- package/dist/server/auth.d.ts +177 -20
- package/dist/server/auth.js +34 -75
- package/dist/server/decode-session.js +84 -10
- package/dist/types/auth.d.ts +18 -0
- package/package.json +969 -958
- package/scripts/check-internal-url-usage.sh +73 -73
- package/scripts/check-publish-health.mjs +120 -0
- package/scripts/dev-broker.ps1 +71 -6
- package/scripts/print-client-config.mjs +162 -0
- package/scripts/server-only-stub.cjs +4 -0
- package/src/api-handlers/account/recovery/initiate.ts +35 -2
- package/src/api-handlers/account/recovery/send-code.ts +36 -2
- package/src/api-handlers/account/recovery/verify-code.ts +36 -2
- package/src/api-handlers/account/reset-password.ts +35 -2
- package/src/api-handlers/account/validate-password.ts +22 -3
- package/src/api-handlers/admin/analytics.ts +2 -1
- package/src/api-handlers/admin/audit.ts +2 -1
- package/src/api-handlers/admin/sessions.ts +2 -1
- package/src/api-handlers/admin/stats.ts +2 -1
- package/src/api-handlers/admin/users.ts +2 -1
- package/src/api-handlers/admin/vibe-data.ts +2 -1
- package/src/api-handlers/auth/jwks.ts +10 -0
- package/src/api-handlers/auth/login.ts +5 -2
- package/src/api-handlers/auth/passkey-signin.ts +112 -98
- package/src/api-handlers/auth/refresh.ts +112 -15
- package/src/api-handlers/auth/signout.ts +4 -3
- package/src/api-handlers/auth/verify-code.ts +5 -2
- package/src/api-handlers/mfa/backup-code-verify.ts +175 -175
- package/src/api-handlers/mfa/backup-codes.ts +41 -41
- package/src/api-handlers/mfa/methods.ts +46 -46
- package/src/api-handlers/mfa/totp-confirm.ts +42 -42
- package/src/api-handlers/mfa/totp-enroll.ts +42 -42
- package/src/api-handlers/passkeys/list.ts +31 -31
- package/src/api-handlers/passkeys/manage.ts +54 -54
- package/src/api-handlers/passkeys/register-options.ts +42 -42
- package/src/api-handlers/passkeys/register-verify.ts +41 -41
- package/src/api-handlers/session/viability.ts +29 -1
- package/src/api-handlers/test/force-expire.ts +118 -13
- package/src/auth/better-auth.ts +486 -51
- package/src/auth/utils/idp-client.ts +1 -1
- package/src/client/AuthContext.tsx +55 -19
- package/src/components/account/UserAvatarMenu.tsx +11 -0
- package/src/components/auth/ModeAwareLoginPage.tsx +40 -49
- package/src/components/auth/ModeAwareSignupPage.tsx +5 -2
- package/src/components/auth/TraditionalAuthSection.tsx +5 -1
- package/src/hooks/useAuthSettings.ts +14 -2
- package/src/hooks/usePublicAuthSettings.ts +38 -16
- package/src/lib/client-key.ts +88 -0
- package/src/lib/demo-mode.ts +2 -1
- package/src/lib/ensure-fresh-access-token.ts +5 -1
- package/src/lib/idp-client-config.ts +399 -36
- package/src/lib/idp-fetch.ts +36 -1
- package/src/lib/idp-url-guard.ts +37 -0
- package/src/lib/redis.ts +84 -6
- package/src/lib/session-cookie-shape.ts +184 -0
- package/src/lib/session-store.ts +152 -6
- package/src/lib/single-flight.ts +64 -0
- package/src/lib/token-lifecycle.ts +12 -0
- package/src/middleware/create-middleware.ts +11 -0
- package/src/models/SessionModel.ts +49 -0
- package/src/pages/admin-login/page.tsx +17 -127
- package/src/pages/security/EnhancedSecurityPage.tsx +95 -9
- package/src/pages/security/index.ts +3 -0
- package/src/pages/test-env/EmergencyLogoutPage.tsx +17 -5
- package/src/pages/test-env/JwtInspectPage.tsx +235 -20
- package/src/pages/test-env/RefreshTokenPage.tsx +22 -6
- package/src/pages/test-env/TestEnvPage.tsx +16 -4
- package/src/pages/test-env/guarded.tsx +81 -0
- package/src/pages/test-env/index.ts +59 -7
- package/src/routes/auth/logout.ts +4 -3
- package/src/routes/auth/passkey-signin.ts +13 -13
- package/src/routes/auth/refresh.ts +30 -1
- package/src/routes/auth/session.ts +98 -9
- package/src/routes/auth/settings.ts +144 -42
- package/src/routes/auth/viability.ts +129 -11
- package/src/routes/mfa/backup-code-verify.ts +1 -1
- package/src/routes/mfa/backup-codes.ts +10 -10
- package/src/routes/mfa/methods.ts +22 -22
- package/src/routes/mfa/totp-confirm.ts +10 -10
- package/src/routes/mfa/totp-enroll.ts +10 -10
- package/src/routes/mfa/totp-verify.ts +14 -14
- package/src/routes/passkeys/list.ts +10 -10
- package/src/routes/passkeys/manage.ts +23 -23
- package/src/routes/passkeys/register-options.ts +10 -10
- package/src/routes/passkeys/register-verify.ts +10 -10
- package/src/server/auth-guard.ts +24 -17
- package/src/server/auth.ts +37 -73
- package/src/server/decode-session.ts +95 -11
- package/src/templates/instrumentation.ts +21 -2
- package/src/types/auth.ts +20 -0
- package/dist/lib/auth-secret.d.ts +0 -17
- package/dist/lib/auth-secret.js +0 -116
- package/src/lib/auth-secret.ts +0 -137
|
@@ -56,7 +56,14 @@ function useAuthSettings() {
|
|
|
56
56
|
*/
|
|
57
57
|
function useRequires2FA() {
|
|
58
58
|
const authSettings = useAuthSettings();
|
|
59
|
-
|
|
59
|
+
// 257255: DELIBERATELY LEFT FAIL-CLOSED, and flagged rather than silently kept.
|
|
60
|
+
// QAPert proved any hardcoded fallback is wrong in one environment (same client
|
|
61
|
+
// is require_2fa TRUE on AKS, FALSE on dev-93), so `?? true` is not correct - it
|
|
62
|
+
// is merely the SAFE direction: it demands more auth rather than less, and
|
|
63
|
+
// flipping it to false would be fail-OPEN. The genuinely correct answer is to
|
|
64
|
+
// propagate "unknown" to the caller, which is a breaking signature change
|
|
65
|
+
// (boolean -> boolean | null) and needs its own decision. Tracked on 257255.
|
|
66
|
+
return authSettings?.require2FA ?? true;
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
62
69
|
* Hook to get allowed 2FA methods.
|
|
@@ -65,7 +72,12 @@ function useRequires2FA() {
|
|
|
65
72
|
*/
|
|
66
73
|
function useAllowed2FAMethods() {
|
|
67
74
|
const authSettings = useAuthSettings();
|
|
68
|
-
|
|
75
|
+
// 257255 / v32(a) OFFER NOTHING YOU CANNOT CONFIRM.
|
|
76
|
+
// Was `?? ['email','sms']` - a fabricated method list rendered whenever the
|
|
77
|
+
// policy could not be read. On 2026-08-24 that told users to use email+sms
|
|
78
|
+
// while the database said emailcode+totp. An empty list is the honest answer;
|
|
79
|
+
// the caller must show "we could not load sign-in options" rather than guess.
|
|
80
|
+
return authSettings?.allowed2FAMethods ?? [];
|
|
69
81
|
}
|
|
70
82
|
/**
|
|
71
83
|
* Hook to get session timeout settings.
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
import type { FederatedProvider } from '../types/auth';
|
|
8
8
|
export interface PublicAuthSettings {
|
|
9
9
|
enabledProviders: FederatedProvider[];
|
|
10
|
-
allowPublicRegistration: boolean;
|
|
11
10
|
allowFederatedLogin: boolean;
|
|
12
|
-
enablePasswordReset: boolean;
|
|
13
11
|
require2FA: boolean;
|
|
14
12
|
allowed2FAMethods: string[];
|
|
13
|
+
allowPublicRegistration?: boolean;
|
|
14
|
+
enablePasswordReset?: boolean;
|
|
15
15
|
}
|
|
16
16
|
export interface UsePublicAuthSettingsResult {
|
|
17
17
|
settings: PublicAuthSettings | null;
|
|
@@ -27,14 +27,19 @@ const PROVIDER_MAP = {
|
|
|
27
27
|
'microsoft': 'microsoft',
|
|
28
28
|
'azure-ad': 'microsoft',
|
|
29
29
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
// 257255 / BAPert ruling v32(a): DEFAULT_SETTINGS DELETED.
|
|
31
|
+
//
|
|
32
|
+
// It was substituted on BOTH failure branches below, so a page kept rendering
|
|
33
|
+
// require2FA=true with methods ['email','sms'] whenever the fetch failed - a
|
|
34
|
+
// FABRICATED AUTHORIZATION POLICY, client-side. Hard-failing the endpoint alone
|
|
35
|
+
// does not fix that: it just moves the lie from the server to the browser.
|
|
36
|
+
//
|
|
37
|
+
// QAPert's measurement makes this a proof rather than a preference: the SAME
|
|
38
|
+
// client is require_2fa=TRUE on AKS and FALSE on dev-93, so any hardcoded
|
|
39
|
+
// fallback is wrong in at least one environment BY CONSTRUCTION.
|
|
40
|
+
//
|
|
41
|
+
// On failure `settings` stays NULL and `error` is set. Callers must render an
|
|
42
|
+
// honest "we could not load sign-in options" state and OFFER NOTHING.
|
|
38
43
|
/**
|
|
39
44
|
* Hook to fetch public auth settings for login/signup pages.
|
|
40
45
|
*
|
|
@@ -84,7 +89,12 @@ function usePublicAuthSettings() {
|
|
|
84
89
|
});
|
|
85
90
|
}
|
|
86
91
|
else {
|
|
87
|
-
|
|
92
|
+
// The endpoint told us it could not read a policy (503 +
|
|
93
|
+
// success:false + code). Do NOT invent one - surface it.
|
|
94
|
+
const code = data?.error?.code ?? 'POLICY_UNAVAILABLE';
|
|
95
|
+
setSettings(null);
|
|
96
|
+
setError(new Error(`[auth-settings] ${code}: sign-in options unavailable`));
|
|
97
|
+
return;
|
|
88
98
|
}
|
|
89
99
|
setError(null);
|
|
90
100
|
}
|
|
@@ -93,7 +103,7 @@ function usePublicAuthSettings() {
|
|
|
93
103
|
return;
|
|
94
104
|
console.warn('[usePublicAuthSettings] Failed to fetch settings:', err);
|
|
95
105
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
96
|
-
setSettings(
|
|
106
|
+
setSettings(null); // 257255: never fabricate a policy
|
|
97
107
|
}
|
|
98
108
|
finally {
|
|
99
109
|
if (mounted) {
|
|
@@ -119,13 +129,17 @@ function useFederatedLoginEnabled() {
|
|
|
119
129
|
* Hook to check if public registration is enabled.
|
|
120
130
|
*/
|
|
121
131
|
function usePublicRegistrationEnabled() {
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
// 257255: was `?? true`, which OFFERED a sign-up lane the IDP may forbid -
|
|
133
|
+
// card 240447 measured exactly that on portal. The field is no longer served
|
|
134
|
+
// (it was never read from config), so this is now always fail-closed until
|
|
135
|
+
// the config document carries it.
|
|
136
|
+
return false;
|
|
124
137
|
}
|
|
125
138
|
/**
|
|
126
139
|
* Hook to check if password reset is enabled.
|
|
127
140
|
*/
|
|
128
141
|
function usePasswordResetEnabled() {
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
// 257255: was `?? true` and was never read from config. Fail-closed until
|
|
143
|
+
// the config document carries it.
|
|
144
|
+
return false;
|
|
131
145
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE resolver for "which client is this request for?" — card 268410.
|
|
3
|
+
*
|
|
4
|
+
* THE ASYMMETRY THIS EXISTS TO SURVIVE (measured at source in the IDP,
|
|
5
|
+
* RequirePayEzUnAuthorizedAttribute.cs):
|
|
6
|
+
*
|
|
7
|
+
* :31-33 int.TryParse succeeds -> Items[PayEzClientId] = numericId; return; NO existence check
|
|
8
|
+
* :41-47 slug -> GetClientIdByNameAsync; if null -> NotFoundResult() VALIDATED
|
|
9
|
+
* :55 no client id at all -> NotFoundResult()
|
|
10
|
+
*
|
|
11
|
+
* So a WRONG SLUG 404s loudly. A WRONG NUMERIC is silently accepted and the
|
|
12
|
+
* request is answered for whatever client that number happens to be. On a
|
|
13
|
+
* multi-tenant identity service that is a WRONG-TENANT ANSWER, not merely a bug.
|
|
14
|
+
*
|
|
15
|
+
* And a stale numeric is the DOCUMENTED NORMAL CASE, not a hypothetical: numeric
|
|
16
|
+
* ids are environment-specific (card 250596 — 79 was dev-93's id for the SAME
|
|
17
|
+
* slug, and the merge collapsed 76 into 12), while the slug is stable across
|
|
18
|
+
* environments.
|
|
19
|
+
*
|
|
20
|
+
* WHY THE FALLBACK WAS SILENT: idp-client-config.ts:462 and :669 resolve
|
|
21
|
+
* `clientSlug: ... ?? ''`. Empty string is FALSY, so the idiom every call site
|
|
22
|
+
* used —
|
|
23
|
+
*
|
|
24
|
+
* config.clientSlug || String(config.clientId)
|
|
25
|
+
*
|
|
26
|
+
* — falls through to the numeric arm whenever the IDP config carries no slug,
|
|
27
|
+
* with nothing emitted to say it happened. Absence collapsed into a
|
|
28
|
+
* usable-looking default: the same class the graft's passkey plugin already
|
|
29
|
+
* called out when `|| ''` made an unset env var look like a valid client_id.
|
|
30
|
+
*
|
|
31
|
+
* WHAT THIS DOES **NOT** DO: it does not flip precedence. Slug still wins, the
|
|
32
|
+
* numeric fallback is still taken, and every deployment that works today keeps
|
|
33
|
+
* working byte-for-byte. Blanket-flipping these to numeric-first is the
|
|
34
|
+
* dangerous-looking-safe change the 268410 ruling explicitly holds.
|
|
35
|
+
*
|
|
36
|
+
* WHAT IT DOES: it makes the dangerous arm ATTRIBUTABLE. Taking the numeric
|
|
37
|
+
* fallback now emits a warning naming the call site, so a wrong-tenant answer
|
|
38
|
+
* is diagnosable in seconds instead of being indistinguishable from a correct
|
|
39
|
+
* one. That ambiguity is what cost hours on S12.
|
|
40
|
+
*/
|
|
41
|
+
export interface ClientKeySource {
|
|
42
|
+
clientSlug?: string | null;
|
|
43
|
+
clientId?: string | number | null;
|
|
44
|
+
}
|
|
45
|
+
export interface ResolvedClientKey {
|
|
46
|
+
/** The routing key to send as X-Client-Id / client_id, or null if none resolved. */
|
|
47
|
+
key: string | null;
|
|
48
|
+
/** True when the environment-specific numeric arm was taken. */
|
|
49
|
+
usedNumericFallback: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the IDP routing key from a client config.
|
|
53
|
+
*
|
|
54
|
+
* `site` names the caller (e.g. 'admin/analytics') and appears verbatim in the
|
|
55
|
+
* fallback warning — that string is the whole diagnostic value, so pass a real one.
|
|
56
|
+
*
|
|
57
|
+
* Pure and non-throwing: callers decide whether a null key is fatal (refuse) or
|
|
58
|
+
* merely means "omit the header". Both shapes exist in this package and both
|
|
59
|
+
* are correct for their path.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveClientKey(config: ClientKeySource | null | undefined, site: string): ResolvedClientKey;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ONE resolver for "which client is this request for?" — card 268410.
|
|
4
|
+
*
|
|
5
|
+
* THE ASYMMETRY THIS EXISTS TO SURVIVE (measured at source in the IDP,
|
|
6
|
+
* RequirePayEzUnAuthorizedAttribute.cs):
|
|
7
|
+
*
|
|
8
|
+
* :31-33 int.TryParse succeeds -> Items[PayEzClientId] = numericId; return; NO existence check
|
|
9
|
+
* :41-47 slug -> GetClientIdByNameAsync; if null -> NotFoundResult() VALIDATED
|
|
10
|
+
* :55 no client id at all -> NotFoundResult()
|
|
11
|
+
*
|
|
12
|
+
* So a WRONG SLUG 404s loudly. A WRONG NUMERIC is silently accepted and the
|
|
13
|
+
* request is answered for whatever client that number happens to be. On a
|
|
14
|
+
* multi-tenant identity service that is a WRONG-TENANT ANSWER, not merely a bug.
|
|
15
|
+
*
|
|
16
|
+
* And a stale numeric is the DOCUMENTED NORMAL CASE, not a hypothetical: numeric
|
|
17
|
+
* ids are environment-specific (card 250596 — 79 was dev-93's id for the SAME
|
|
18
|
+
* slug, and the merge collapsed 76 into 12), while the slug is stable across
|
|
19
|
+
* environments.
|
|
20
|
+
*
|
|
21
|
+
* WHY THE FALLBACK WAS SILENT: idp-client-config.ts:462 and :669 resolve
|
|
22
|
+
* `clientSlug: ... ?? ''`. Empty string is FALSY, so the idiom every call site
|
|
23
|
+
* used —
|
|
24
|
+
*
|
|
25
|
+
* config.clientSlug || String(config.clientId)
|
|
26
|
+
*
|
|
27
|
+
* — falls through to the numeric arm whenever the IDP config carries no slug,
|
|
28
|
+
* with nothing emitted to say it happened. Absence collapsed into a
|
|
29
|
+
* usable-looking default: the same class the graft's passkey plugin already
|
|
30
|
+
* called out when `|| ''` made an unset env var look like a valid client_id.
|
|
31
|
+
*
|
|
32
|
+
* WHAT THIS DOES **NOT** DO: it does not flip precedence. Slug still wins, the
|
|
33
|
+
* numeric fallback is still taken, and every deployment that works today keeps
|
|
34
|
+
* working byte-for-byte. Blanket-flipping these to numeric-first is the
|
|
35
|
+
* dangerous-looking-safe change the 268410 ruling explicitly holds.
|
|
36
|
+
*
|
|
37
|
+
* WHAT IT DOES: it makes the dangerous arm ATTRIBUTABLE. Taking the numeric
|
|
38
|
+
* fallback now emits a warning naming the call site, so a wrong-tenant answer
|
|
39
|
+
* is diagnosable in seconds instead of being indistinguishable from a correct
|
|
40
|
+
* one. That ambiguity is what cost hours on S12.
|
|
41
|
+
*/
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.resolveClientKey = resolveClientKey;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the IDP routing key from a client config.
|
|
46
|
+
*
|
|
47
|
+
* `site` names the caller (e.g. 'admin/analytics') and appears verbatim in the
|
|
48
|
+
* fallback warning — that string is the whole diagnostic value, so pass a real one.
|
|
49
|
+
*
|
|
50
|
+
* Pure and non-throwing: callers decide whether a null key is fatal (refuse) or
|
|
51
|
+
* merely means "omit the header". Both shapes exist in this package and both
|
|
52
|
+
* are correct for their path.
|
|
53
|
+
*/
|
|
54
|
+
function resolveClientKey(config, site) {
|
|
55
|
+
const slug = typeof config?.clientSlug === 'string' ? config.clientSlug.trim() : '';
|
|
56
|
+
if (slug)
|
|
57
|
+
return { key: slug, usedNumericFallback: false };
|
|
58
|
+
const rawNumeric = config?.clientId;
|
|
59
|
+
// Guard String(undefined) === 'undefined' — a 13-char string that is not a
|
|
60
|
+
// slug and not a number, so the IDP would 404 it with no hint as to why.
|
|
61
|
+
if (rawNumeric === null || rawNumeric === undefined) {
|
|
62
|
+
return { key: null, usedNumericFallback: false };
|
|
63
|
+
}
|
|
64
|
+
const numeric = String(rawNumeric).trim();
|
|
65
|
+
if (!numeric)
|
|
66
|
+
return { key: null, usedNumericFallback: false };
|
|
67
|
+
console.warn(`[client-key] ${site}: no clientSlug on the resolved IDP config — falling back to the ` +
|
|
68
|
+
`NUMERIC client id ${numeric}. Numeric ids are ENVIRONMENT-SPECIFIC and the IDP accepts ` +
|
|
69
|
+
`them with NO existence check, so if this number is stale for this environment the ` +
|
|
70
|
+
`request is being answered for the WRONG CLIENT, silently. Card 268410.`);
|
|
71
|
+
return { key: numeric, usedNumericFallback: true };
|
|
72
|
+
}
|
package/dist/lib/demo-mode.js
CHANGED
|
@@ -14,5 +14,6 @@ function isAuthConfigured() {
|
|
|
14
14
|
return false;
|
|
15
15
|
return !!(process.env.BETTER_AUTH_SECRET ||
|
|
16
16
|
process.env.NEXTAUTH_SECRET ||
|
|
17
|
-
|
|
17
|
+
// was NEXT_CLIENT_ID - a third name for CLIENT_ID, read only here. Collapsed 2026-08-26.
|
|
18
|
+
(process.env.CLIENT_ID && process.env.NEXT_CLIENT_PRIVATE_KEY_PEM));
|
|
18
19
|
}
|
|
@@ -311,7 +311,11 @@ async function ensureFreshAccessToken(sessionToken, config, options = {}) {
|
|
|
311
311
|
bearerKeyId: newKid || session.bearerKeyId,
|
|
312
312
|
authenticationMethods: amrClaims,
|
|
313
313
|
authenticationLevel: acrLevel,
|
|
314
|
-
|
|
314
|
+
// 170007 rule 2 (BAPert ruling on 121350): re-derive from the token
|
|
315
|
+
// every exchange, never carry forward. See the identical fix + full
|
|
316
|
+
// rationale in api-handlers/auth/refresh.ts's sessionUpdate — this is
|
|
317
|
+
// the same defect duplicated at a second call site.
|
|
318
|
+
mfaVerified: amrClaims.includes('mfa'),
|
|
315
319
|
mfaCompletedAt: decoded?.mfa_time ? parseInt(decoded.mfa_time) * 1000 : session.mfaCompletedAt,
|
|
316
320
|
mfaExpiresAt: decoded?.mfa_expires ? parseInt(decoded.mfa_expires) * 1000 : session.mfaExpiresAt,
|
|
317
321
|
mfaValidityHours: decoded?.mfa_validity_hours ? parseInt(decoded.mfa_validity_hours) : session.mfaValidityHours,
|
|
@@ -27,8 +27,8 @@ export interface OAuthProviderConfig {
|
|
|
27
27
|
additionalParams?: Record<string, any>;
|
|
28
28
|
}
|
|
29
29
|
export interface AuthSettings {
|
|
30
|
-
require2FA
|
|
31
|
-
allowed2FAMethods
|
|
30
|
+
require2FA?: boolean;
|
|
31
|
+
allowed2FAMethods?: string[];
|
|
32
32
|
mfaGracePeriodHours: number;
|
|
33
33
|
mfaRememberDeviceDays: number;
|
|
34
34
|
sessionTimeoutMinutes: number;
|
|
@@ -37,6 +37,29 @@ export interface AuthSettings {
|
|
|
37
37
|
rememberMeDays: number;
|
|
38
38
|
lockoutThreshold: number;
|
|
39
39
|
lockoutDurationMinutes: number;
|
|
40
|
+
/**
|
|
41
|
+
* 240447: tenant policy the IDP has always sent and this loader never read.
|
|
42
|
+
*
|
|
43
|
+
* The IDP emits these inside `auth_settings` on the internal client-config
|
|
44
|
+
* response (PayEz.Identity.Api InternalClientConfigController, the
|
|
45
|
+
* `auth_settings` object: allow_public_registration, allow_social_login,
|
|
46
|
+
* enable_password_reset — backed by IDPClientSecurityPolicy).
|
|
47
|
+
* routes/auth/settings was answering with hardcoded `true` literals
|
|
48
|
+
* carrying the comment "Could come from config.authSettings in future",
|
|
49
|
+
* so the portal advertised self-serve registration for a tenant whose IDP
|
|
50
|
+
* forbids it (measured same-client same-minute: IDP
|
|
51
|
+
* allow_public_registration=FALSE, portal allowPublicRegistration=TRUE).
|
|
52
|
+
*
|
|
53
|
+
* OPTIONAL ON PURPOSE — `undefined` must stay distinguishable from
|
|
54
|
+
* `false`. undefined = the IDP did not tell us (older IDP build, or
|
|
55
|
+
* auth_settings absent entirely); false = the IDP affirmatively said no.
|
|
56
|
+
* Collapsing the first into a boolean default is exactly how the previous
|
|
57
|
+
* hardcoded `true` became a lie that read like a measurement. Consumers
|
|
58
|
+
* must decide explicitly what an unknown means for them.
|
|
59
|
+
*/
|
|
60
|
+
allowPublicRegistration?: boolean;
|
|
61
|
+
allowSocialLogin?: boolean;
|
|
62
|
+
enablePasswordReset?: boolean;
|
|
40
63
|
}
|
|
41
64
|
export interface BrandingConfig {
|
|
42
65
|
theme?: string;
|