@payez/next-mvp 4.8.1 → 4.8.3
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 -408
- package/README.md +859 -859
- package/dist/api-handlers/admin/audit.js +13 -21
- package/dist/api-handlers/admin/sessions.js +59 -45
- package/dist/api-handlers/admin/stats.js +1 -1
- package/dist/api-handlers/admin/users.js +15 -23
- package/dist/api-handlers/admin/vibe-data.d.ts +5 -0
- package/dist/api-handlers/admin/vibe-data.js +5 -0
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/session-store.js +21 -21
- package/dist/lib/startup-init.js +19 -19
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/dist/vibe/client.d.ts +1 -1
- package/dist/vibe/client.js +2 -1
- package/dist/vibe/filters.d.ts +36 -0
- package/dist/vibe/filters.js +47 -0
- package/dist/vibe/generic.js +2 -1
- package/dist/vibe/sessions.js +28 -3
- package/package.json +2 -2
- package/scripts/check-publish-eol.mjs +33 -15
- package/src/api/auth-handler.ts +550 -550
- package/src/api/index.ts +17 -17
- package/src/api-handlers/account/change-password.ts +170 -170
- package/src/api-handlers/account/masked-info.ts +46 -46
- package/src/api-handlers/account/profile.ts +99 -99
- package/src/api-handlers/account/recovery/forward.ts +117 -117
- package/src/api-handlers/account/recovery/initiate.ts +59 -59
- package/src/api-handlers/account/recovery/send-code.ts +21 -21
- package/src/api-handlers/account/recovery/verify-code.ts +20 -20
- package/src/api-handlers/account/reset-password.ts +87 -87
- package/src/api-handlers/account/send-code.ts +70 -70
- package/src/api-handlers/account/update-phone.ts +84 -84
- package/src/api-handlers/account/validate-password.ts +139 -139
- package/src/api-handlers/account/verify-email.ts +134 -134
- package/src/api-handlers/account/verify-sms.ts +134 -134
- package/src/api-handlers/admin/analytics.ts +452 -452
- package/src/api-handlers/admin/audit.ts +223 -232
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +259 -259
- package/src/api-handlers/admin/sessions.ts +341 -327
- package/src/api-handlers/admin/site-logs.ts +388 -388
- package/src/api-handlers/admin/stats.ts +260 -260
- package/src/api-handlers/admin/users.ts +240 -251
- package/src/api-handlers/admin/vibe-data.ts +338 -333
- package/src/api-handlers/anon/preferences.ts +123 -123
- package/src/api-handlers/auth/credential-login-email-code.ts +92 -92
- package/src/api-handlers/auth/credential-login.ts +948 -948
- package/src/api-handlers/auth/jwks.ts +31 -31
- package/src/api-handlers/auth/login.ts +252 -252
- package/src/api-handlers/auth/passkey-signin.ts +116 -116
- package/src/api-handlers/auth/refresh.ts +829 -829
- package/src/api-handlers/auth/signout.ts +201 -201
- package/src/api-handlers/auth/status.ts +23 -23
- package/src/api-handlers/auth/update-session.ts +144 -144
- package/src/api-handlers/auth/validate.ts +45 -45
- package/src/api-handlers/auth/verify-code.ts +116 -116
- package/src/api-handlers/mfa/backup-code-verify.ts +170 -170
- package/src/api-handlers/mfa/backup-codes.ts +42 -42
- package/src/api-handlers/mfa/methods.ts +47 -47
- package/src/api-handlers/mfa/totp-confirm.ts +43 -43
- package/src/api-handlers/mfa/totp-enroll.ts +43 -43
- package/src/api-handlers/mfa/totp-verify.ts +172 -172
- package/src/api-handlers/oauth/resume.ts +182 -182
- package/src/api-handlers/onboarding/send-email-code.ts +18 -18
- package/src/api-handlers/onboarding/set-password.ts +21 -21
- package/src/api-handlers/onboarding/verify-code.ts +20 -20
- package/src/api-handlers/onboarding/verify-welcome-email.ts +17 -17
- package/src/api-handlers/passkeys/list.ts +32 -32
- package/src/api-handlers/passkeys/manage.ts +55 -55
- package/src/api-handlers/passkeys/register-options.ts +43 -43
- package/src/api-handlers/passkeys/register-verify.ts +42 -42
- package/src/api-handlers/session/refresh-viability.ts +56 -56
- package/src/api-handlers/session/viability.ts +238 -238
- package/src/api-handlers/test/force-expire.ts +167 -167
- package/src/auth/auth-decision.ts +292 -292
- package/src/auth/better-auth.ts +1688 -1688
- package/src/auth/idp-oauth.ts +310 -310
- package/src/auth/password-reset-bridge.ts +98 -98
- package/src/auth/revocation-fields.ts +16 -16
- package/src/auth/route-config.ts +236 -236
- package/src/auth/safe-callback.ts +199 -199
- package/src/auth/session-cookie-clear.ts +30 -30
- package/src/auth/session-reset-link.ts +159 -159
- package/src/auth/session-response-sanitizer.ts +151 -151
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/unauthenticated-routes.ts +2 -2
- package/src/auth/utils/idp-client.ts +364 -364
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +192 -192
- package/src/client/better-auth-client.ts +70 -70
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/client/fetchWithSession.ts +21 -21
- package/src/client/index.ts +13 -13
- package/src/client/useAnonSession.ts +131 -131
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +277 -277
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +508 -508
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +421 -421
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminContext.tsx +87 -87
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/CredentialSignInForm.tsx +240 -240
- package/src/components/auth/FederatedAuthSection.tsx +129 -129
- package/src/components/auth/ModeAwareLoginPage.tsx +146 -146
- package/src/components/auth/ModeAwareSignupPage.tsx +200 -200
- package/src/components/auth/PasswordResetBridge.tsx +30 -30
- package/src/components/auth/TraditionalAuthSection.tsx +103 -103
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/RequestedStep.tsx +30 -30
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +118 -118
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +55 -55
- package/src/config/logger.ts +62 -62
- package/src/config/logging-config.ts +82 -82
- package/src/config/unauthenticated-routes.ts +19 -19
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/edge/internal-api-url.ts +65 -65
- package/src/edge/middleware.ts +42 -42
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +109 -109
- package/src/hooks/useAvailableProviders.ts +143 -143
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/useProfile.ts +82 -82
- package/src/hooks/usePublicAuthSettings.ts +171 -171
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +337 -337
- package/src/index.ts +64 -64
- package/src/lib/anon-session.ts +213 -213
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +228 -228
- package/src/lib/authorize-resume.ts +172 -172
- package/src/lib/bearer-fetch.ts +31 -31
- package/src/lib/client-key.ts +88 -88
- package/src/lib/code-step-copy.ts +83 -83
- package/src/lib/credential-sign-in-client.ts +101 -101
- package/src/lib/demo-mode.ts +18 -18
- package/src/lib/device-id.ts +67 -67
- package/src/lib/ensure-fresh-access-token.ts +509 -509
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-client-config.ts +1005 -1005
- package/src/lib/idp-fetch.ts +169 -169
- package/src/lib/idp-url-guard.ts +37 -37
- package/src/lib/internal-api.ts +171 -171
- package/src/lib/jwt-decode-client.ts +45 -45
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/lock-window.ts +49 -49
- package/src/lib/login-error-message.ts +41 -41
- package/src/lib/never-in-response.ts +64 -64
- package/src/lib/nextauth-secret.ts +168 -168
- package/src/lib/onboarding-proxy.ts +57 -57
- package/src/lib/password-rules.ts +50 -50
- package/src/lib/rate-limit-service.ts +9 -9
- package/src/lib/redis.ts +105 -105
- package/src/lib/refresh-token-validator.ts +64 -64
- package/src/lib/roles.ts +177 -177
- package/src/lib/safe-error-response.ts +149 -149
- package/src/lib/secret-validation.ts +8 -8
- package/src/lib/session-cookie-shape.ts +185 -185
- package/src/lib/session-store.ts +1092 -1092
- package/src/lib/session.ts +34 -34
- package/src/lib/single-flight.ts +64 -64
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/startup-init.ts +276 -276
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/token-expiry.ts +40 -40
- package/src/lib/token-lifecycle.ts +546 -546
- package/src/lib/token-log-id.ts +69 -69
- package/src/lib/twofa-rejection.ts +91 -91
- package/src/lib/types/api-responses.ts +336 -336
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +1141 -1141
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/models/DecodedAccessToken.ts +16 -16
- package/src/models/SessionModel.ts +331 -331
- package/src/pages/admin-login/page.tsx +111 -111
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/index.ts +9 -9
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +338 -338
- package/src/pages/onboarding/index.ts +11 -11
- package/src/pages/onboarding/page.tsx +212 -212
- package/src/pages/profile/EnhancedProfilePage.tsx +514 -514
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +267 -267
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +660 -660
- package/src/pages/security/index.ts +12 -12
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/service-unavailable/page.tsx +63 -63
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +192 -192
- package/src/pages/test-env/JwtInspectPage.tsx +449 -449
- package/src/pages/test-env/RefreshTokenPage.tsx +178 -178
- package/src/pages/test-env/TestEnvPage.tsx +130 -130
- package/src/pages/test-env/guarded.tsx +81 -81
- package/src/pages/test-env/index.ts +77 -77
- package/src/pages/test-env/redact-credentials.ts +96 -96
- package/src/pages/verify-code/page.tsx +746 -746
- package/src/routes/account/index.ts +31 -31
- package/src/routes/account/masked-info.ts +36 -36
- package/src/routes/account/send-code.ts +39 -39
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/account/verify-email.ts +40 -40
- package/src/routes/account/verify-sms.ts +40 -40
- package/src/routes/auth/credential-login-email-code.ts +6 -6
- package/src/routes/auth/credential-login.ts +6 -6
- package/src/routes/auth/index.ts +22 -22
- package/src/routes/auth/logout.ts +111 -111
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/passkey-signin.ts +13 -13
- package/src/routes/auth/refresh.ts +77 -77
- package/src/routes/auth/session-reset.ts +37 -37
- package/src/routes/auth/session.ts +261 -261
- package/src/routes/auth/settings.ts +181 -181
- package/src/routes/auth/viability.ts +519 -519
- package/src/routes/index.ts +17 -17
- 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/onboarding/index.ts +15 -15
- package/src/routes/onboarding/send-email-code.ts +2 -2
- package/src/routes/onboarding/set-password.ts +2 -2
- package/src/routes/onboarding/verify-code.ts +2 -2
- package/src/routes/onboarding/verify-welcome-email.ts +2 -2
- 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/routes/session/index.ts +7 -7
- package/src/routes/session/refresh-viability.ts +17 -17
- package/src/server/auth-guard.ts +203 -203
- package/src/server/auth.ts +486 -486
- package/src/server/decode-session.ts +309 -309
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/verified-session-token.ts +103 -103
- package/src/server/with-auth.ts +93 -93
- package/src/services/signalrActivityService.ts +258 -258
- package/src/stores/authStore.ts +1900 -1900
- package/src/templates/instrumentation.ts +60 -60
- package/src/theme/ThemeProvider.tsx +39 -39
- package/src/theme/default.ts +33 -33
- package/src/theme/index.ts +31 -31
- package/src/theme/types.ts +69 -69
- package/src/theme/useTheme.ts +57 -57
- package/src/theme/utils.ts +40 -40
- package/src/types/api.ts +12 -12
- package/src/types/auth.d.ts +23 -23
- package/src/types/auth.ts +48 -48
- package/src/types/logging.ts +11 -11
- package/src/types/recovery.ts +54 -54
- package/src/utils/api.ts +401 -401
- package/src/utils/circuitBreaker.ts +40 -40
- package/src/utils/error-message.ts +108 -108
- package/src/utils/layout/reservedSpace.ts +124 -124
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +878 -877
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +209 -209
- package/src/vibe/filters.ts +52 -0
- package/src/vibe/generic.ts +340 -339
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +133 -133
- package/src/vibe/sessions.ts +925 -899
- package/src/vibe/types.ts +577 -577
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PAY-2121: the fragment bridge on `/`.
|
|
3
|
-
*
|
|
4
|
-
* The IdP's post-verify redirect for a passwordless account lands on
|
|
5
|
-
* ORIGIN/#password_reset_required=true&reset_token=<token>&email=<addr>
|
|
6
|
-
* - a FRAGMENT, never a query (ProgressiveAuthController.cs:1204-1222). A fresh
|
|
7
|
-
* @payez/next-mvp app has no fragment handling at all, so the user lands on `/`
|
|
8
|
-
* and nothing happens; their reset is lost.
|
|
9
|
-
*
|
|
10
|
-
* This is the PURE half (data in, data out) so a test can execute it without a
|
|
11
|
-
* DOM. The client component (PasswordResetBridge) calls it on mount with
|
|
12
|
-
* `window.location.hash`. The kit's reset surface is the existing
|
|
13
|
-
* `/account-auth/recovery` page, which reads `?email=` and a reset token - so the
|
|
14
|
-
* bridge rewrites the fragment into that surface's OWN query shape and navigates
|
|
15
|
-
* once. It invents no new reset flow.
|
|
16
|
-
*
|
|
17
|
-
* CONTRACT:
|
|
18
|
-
* '#password_reset_required=true&reset_token=T&email=a%40b.co' -> '/account-auth/recovery?token=T&email=a%40b.co'
|
|
19
|
-
* any other fragment (no flag, flag != true, no token) -> null (leave `/` alone)
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/** The reset surface the bridge hands off to, by its existing path. */
|
|
23
|
-
export const RECOVERY_PATH = '/account-auth/recovery';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* PAY-2121 (BAPert 70576): the recovery page's ENTRY decision, as a pure function.
|
|
27
|
-
*
|
|
28
|
-
* The bridge now sends a VERIFIED passwordless user to
|
|
29
|
-
* /account-auth/recovery?token=<reset_token>&email=<addr>
|
|
30
|
-
* with a REAL reset token in hand. If the page then showed its request-a-code
|
|
31
|
-
* step, it would throw that token away and the flow would still dead-end - just
|
|
32
|
-
* one page later. So when a token is present the page must start at the
|
|
33
|
-
* set-password step and submit straight through resetPasswordWithToken.
|
|
34
|
-
*
|
|
35
|
-
* token present -> { step: 'set-password', resetToken: <token> }
|
|
36
|
-
* no token -> { step: 'initiate', resetToken: null } (today's behaviour)
|
|
37
|
-
*/
|
|
38
|
-
export function recoveryEntry(
|
|
39
|
-
token: string | null | undefined,
|
|
40
|
-
email: string | null | undefined,
|
|
41
|
-
): { step: 'set-password' | 'initiate'; resetToken: string | null; email: string } {
|
|
42
|
-
const tok = (token ?? '').trim();
|
|
43
|
-
return {
|
|
44
|
-
step: tok ? 'set-password' : 'initiate',
|
|
45
|
-
resetToken: tok || null,
|
|
46
|
-
email: (email ?? '').trim(),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* PAY-2121 (BAPert 70582): the set-password SUBMIT precondition, as a pure
|
|
52
|
-
* function. The old guard was `if (!passwordResetToken || !recoverySession) return;`.
|
|
53
|
-
* Removing the session half (so a deep-linked reset can submit) must NOT let a
|
|
54
|
-
* submit with NO token reach the API carrying an empty token - which is exactly
|
|
55
|
-
* what a guard-less submit would do. This decides it, in one testable place:
|
|
56
|
-
*
|
|
57
|
-
* resetSubmitTarget(resetToken, sessionEmail, queryEmail):
|
|
58
|
-
* - no token (after trim) -> null (REFUSE: never call the API with an empty token)
|
|
59
|
-
* - token, session email present -> { token, email: sessionEmail }
|
|
60
|
-
* - token, only a query email -> { token, email: queryEmail } (the deep-link case)
|
|
61
|
-
* - token, but NO email anywhere -> null (REFUSE: nothing to reset)
|
|
62
|
-
*
|
|
63
|
-
* Returns the exact { token, email } the caller must pass to
|
|
64
|
-
* accountApi.resetPasswordWithToken, or null to refuse BEFORE any request.
|
|
65
|
-
*/
|
|
66
|
-
export function resetSubmitTarget(
|
|
67
|
-
resetToken: string | null | undefined,
|
|
68
|
-
sessionEmail: string | null | undefined,
|
|
69
|
-
queryEmail: string | null | undefined,
|
|
70
|
-
): { token: string; email: string } | null {
|
|
71
|
-
const tok = (resetToken ?? '').trim();
|
|
72
|
-
if (!tok) return null;
|
|
73
|
-
const email = ((sessionEmail ?? '').trim() || (queryEmail ?? '').trim());
|
|
74
|
-
if (!email) return null;
|
|
75
|
-
return { token: tok, email };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* The redirect the fragment asks for, or null when the fragment is not a reset
|
|
80
|
-
* request. `hash` is `window.location.hash` (leading '#' optional).
|
|
81
|
-
*
|
|
82
|
-
* Returns null - leaves `/` untouched - when:
|
|
83
|
-
* - the fragment is empty;
|
|
84
|
-
* - `password_reset_required` is absent or not exactly 'true';
|
|
85
|
-
* - no `reset_token` is present (nothing to reset with; not a real link).
|
|
86
|
-
*/
|
|
87
|
-
export function passwordResetRedirect(hash: string | null | undefined): string | null {
|
|
88
|
-
const raw = (hash ?? '').replace(/^#/, '');
|
|
89
|
-
if (!raw) return null;
|
|
90
|
-
const params = new URLSearchParams(raw);
|
|
91
|
-
if (params.get('password_reset_required') !== 'true') return null;
|
|
92
|
-
const token = params.get('reset_token') ?? '';
|
|
93
|
-
if (!token) return null;
|
|
94
|
-
const email = params.get('email') ?? '';
|
|
95
|
-
const qs = new URLSearchParams({ token });
|
|
96
|
-
if (email) qs.set('email', email);
|
|
97
|
-
return `${RECOVERY_PATH}?${qs.toString()}`;
|
|
98
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* PAY-2121: the fragment bridge on `/`.
|
|
3
|
+
*
|
|
4
|
+
* The IdP's post-verify redirect for a passwordless account lands on
|
|
5
|
+
* ORIGIN/#password_reset_required=true&reset_token=<token>&email=<addr>
|
|
6
|
+
* - a FRAGMENT, never a query (ProgressiveAuthController.cs:1204-1222). A fresh
|
|
7
|
+
* @payez/next-mvp app has no fragment handling at all, so the user lands on `/`
|
|
8
|
+
* and nothing happens; their reset is lost.
|
|
9
|
+
*
|
|
10
|
+
* This is the PURE half (data in, data out) so a test can execute it without a
|
|
11
|
+
* DOM. The client component (PasswordResetBridge) calls it on mount with
|
|
12
|
+
* `window.location.hash`. The kit's reset surface is the existing
|
|
13
|
+
* `/account-auth/recovery` page, which reads `?email=` and a reset token - so the
|
|
14
|
+
* bridge rewrites the fragment into that surface's OWN query shape and navigates
|
|
15
|
+
* once. It invents no new reset flow.
|
|
16
|
+
*
|
|
17
|
+
* CONTRACT:
|
|
18
|
+
* '#password_reset_required=true&reset_token=T&email=a%40b.co' -> '/account-auth/recovery?token=T&email=a%40b.co'
|
|
19
|
+
* any other fragment (no flag, flag != true, no token) -> null (leave `/` alone)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** The reset surface the bridge hands off to, by its existing path. */
|
|
23
|
+
export const RECOVERY_PATH = '/account-auth/recovery';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* PAY-2121 (BAPert 70576): the recovery page's ENTRY decision, as a pure function.
|
|
27
|
+
*
|
|
28
|
+
* The bridge now sends a VERIFIED passwordless user to
|
|
29
|
+
* /account-auth/recovery?token=<reset_token>&email=<addr>
|
|
30
|
+
* with a REAL reset token in hand. If the page then showed its request-a-code
|
|
31
|
+
* step, it would throw that token away and the flow would still dead-end - just
|
|
32
|
+
* one page later. So when a token is present the page must start at the
|
|
33
|
+
* set-password step and submit straight through resetPasswordWithToken.
|
|
34
|
+
*
|
|
35
|
+
* token present -> { step: 'set-password', resetToken: <token> }
|
|
36
|
+
* no token -> { step: 'initiate', resetToken: null } (today's behaviour)
|
|
37
|
+
*/
|
|
38
|
+
export function recoveryEntry(
|
|
39
|
+
token: string | null | undefined,
|
|
40
|
+
email: string | null | undefined,
|
|
41
|
+
): { step: 'set-password' | 'initiate'; resetToken: string | null; email: string } {
|
|
42
|
+
const tok = (token ?? '').trim();
|
|
43
|
+
return {
|
|
44
|
+
step: tok ? 'set-password' : 'initiate',
|
|
45
|
+
resetToken: tok || null,
|
|
46
|
+
email: (email ?? '').trim(),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PAY-2121 (BAPert 70582): the set-password SUBMIT precondition, as a pure
|
|
52
|
+
* function. The old guard was `if (!passwordResetToken || !recoverySession) return;`.
|
|
53
|
+
* Removing the session half (so a deep-linked reset can submit) must NOT let a
|
|
54
|
+
* submit with NO token reach the API carrying an empty token - which is exactly
|
|
55
|
+
* what a guard-less submit would do. This decides it, in one testable place:
|
|
56
|
+
*
|
|
57
|
+
* resetSubmitTarget(resetToken, sessionEmail, queryEmail):
|
|
58
|
+
* - no token (after trim) -> null (REFUSE: never call the API with an empty token)
|
|
59
|
+
* - token, session email present -> { token, email: sessionEmail }
|
|
60
|
+
* - token, only a query email -> { token, email: queryEmail } (the deep-link case)
|
|
61
|
+
* - token, but NO email anywhere -> null (REFUSE: nothing to reset)
|
|
62
|
+
*
|
|
63
|
+
* Returns the exact { token, email } the caller must pass to
|
|
64
|
+
* accountApi.resetPasswordWithToken, or null to refuse BEFORE any request.
|
|
65
|
+
*/
|
|
66
|
+
export function resetSubmitTarget(
|
|
67
|
+
resetToken: string | null | undefined,
|
|
68
|
+
sessionEmail: string | null | undefined,
|
|
69
|
+
queryEmail: string | null | undefined,
|
|
70
|
+
): { token: string; email: string } | null {
|
|
71
|
+
const tok = (resetToken ?? '').trim();
|
|
72
|
+
if (!tok) return null;
|
|
73
|
+
const email = ((sessionEmail ?? '').trim() || (queryEmail ?? '').trim());
|
|
74
|
+
if (!email) return null;
|
|
75
|
+
return { token: tok, email };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The redirect the fragment asks for, or null when the fragment is not a reset
|
|
80
|
+
* request. `hash` is `window.location.hash` (leading '#' optional).
|
|
81
|
+
*
|
|
82
|
+
* Returns null - leaves `/` untouched - when:
|
|
83
|
+
* - the fragment is empty;
|
|
84
|
+
* - `password_reset_required` is absent or not exactly 'true';
|
|
85
|
+
* - no `reset_token` is present (nothing to reset with; not a real link).
|
|
86
|
+
*/
|
|
87
|
+
export function passwordResetRedirect(hash: string | null | undefined): string | null {
|
|
88
|
+
const raw = (hash ?? '').replace(/^#/, '');
|
|
89
|
+
if (!raw) return null;
|
|
90
|
+
const params = new URLSearchParams(raw);
|
|
91
|
+
if (params.get('password_reset_required') !== 'true') return null;
|
|
92
|
+
const token = params.get('reset_token') ?? '';
|
|
93
|
+
if (!token) return null;
|
|
94
|
+
const email = params.get('email') ?? '';
|
|
95
|
+
const qs = new URLSearchParams({ token });
|
|
96
|
+
if (email) qs.set('email', email);
|
|
97
|
+
return `${RECOVERY_PATH}?${qs.toString()}`;
|
|
98
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PAY-2339 (BAPert 74633 gap 1): the two revocation fields every viability producer emits, in ONE place so the
|
|
3
|
-
* producers (api-handlers/session/viability.ts and routes/auth/viability.ts, four return sites) cannot drift.
|
|
4
|
-
*
|
|
5
|
-
* forceInvalid - true ONLY for 'revoked'. An 'unknown' answer is no longer a sign-out verdict.
|
|
6
|
-
* unavailable - true when the revocation backend could not answer: 'unknown' and ANY string that is not one of the
|
|
7
|
-
* three recognised states. A consumer that reads this JSON (useSessionCheck, makeAuthDecision) holds
|
|
8
|
-
* instead of signing the member out. `revocationStatus` itself is emitted unchanged.
|
|
9
|
-
*
|
|
10
|
-
* 'not_configured' is a configuration fault, not an outage: neither field is set for it (unchanged).
|
|
11
|
-
*/
|
|
12
|
-
export function revocationFields(status: unknown): { forceInvalid: boolean; unavailable: boolean; revoked: boolean } {
|
|
13
|
-
const revoked = status === 'revoked';
|
|
14
|
-
const unavailable = !(status === 'revoked' || status === 'active' || status === 'not_configured');
|
|
15
|
-
return { forceInvalid: revoked, unavailable, revoked };
|
|
16
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* PAY-2339 (BAPert 74633 gap 1): the two revocation fields every viability producer emits, in ONE place so the
|
|
3
|
+
* producers (api-handlers/session/viability.ts and routes/auth/viability.ts, four return sites) cannot drift.
|
|
4
|
+
*
|
|
5
|
+
* forceInvalid - true ONLY for 'revoked'. An 'unknown' answer is no longer a sign-out verdict.
|
|
6
|
+
* unavailable - true when the revocation backend could not answer: 'unknown' and ANY string that is not one of the
|
|
7
|
+
* three recognised states. A consumer that reads this JSON (useSessionCheck, makeAuthDecision) holds
|
|
8
|
+
* instead of signing the member out. `revocationStatus` itself is emitted unchanged.
|
|
9
|
+
*
|
|
10
|
+
* 'not_configured' is a configuration fault, not an outage: neither field is set for it (unchanged).
|
|
11
|
+
*/
|
|
12
|
+
export function revocationFields(status: unknown): { forceInvalid: boolean; unavailable: boolean; revoked: boolean } {
|
|
13
|
+
const revoked = status === 'revoked';
|
|
14
|
+
const unavailable = !(status === 'revoked' || status === 'active' || status === 'not_configured');
|
|
15
|
+
return { forceInvalid: revoked, unavailable, revoked };
|
|
16
|
+
}
|