@payez/next-mvp 3.0.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 +782 -0
- package/dist/api/auth-handler.d.ts +67 -0
- package/dist/api/auth-handler.js +397 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +19 -0
- package/dist/api-handlers/account/change-password.d.ts +9 -0
- package/dist/api-handlers/account/change-password.js +112 -0
- package/dist/api-handlers/account/masked-info.d.ts +2 -0
- package/dist/api-handlers/account/masked-info.js +41 -0
- package/dist/api-handlers/account/profile.d.ts +3 -0
- package/dist/api-handlers/account/profile.js +63 -0
- package/dist/api-handlers/account/recovery/initiate.d.ts +2 -0
- package/dist/api-handlers/account/recovery/initiate.js +26 -0
- package/dist/api-handlers/account/recovery/send-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/send-code.js +28 -0
- package/dist/api-handlers/account/recovery/verify-code.d.ts +2 -0
- package/dist/api-handlers/account/recovery/verify-code.js +28 -0
- package/dist/api-handlers/account/reset-password.d.ts +2 -0
- package/dist/api-handlers/account/reset-password.js +26 -0
- package/dist/api-handlers/account/send-code.d.ts +24 -0
- package/dist/api-handlers/account/send-code.js +60 -0
- package/dist/api-handlers/account/update-phone.d.ts +27 -0
- package/dist/api-handlers/account/update-phone.js +64 -0
- package/dist/api-handlers/account/validate-password.d.ts +17 -0
- package/dist/api-handlers/account/validate-password.js +81 -0
- package/dist/api-handlers/account/verify-email.d.ts +26 -0
- package/dist/api-handlers/account/verify-email.js +106 -0
- package/dist/api-handlers/account/verify-sms.d.ts +26 -0
- package/dist/api-handlers/account/verify-sms.js +106 -0
- package/dist/api-handlers/admin/analytics.d.ts +20 -0
- package/dist/api-handlers/admin/analytics.js +379 -0
- package/dist/api-handlers/admin/audit.d.ts +20 -0
- package/dist/api-handlers/admin/audit.js +214 -0
- package/dist/api-handlers/admin/index.d.ts +21 -0
- package/dist/api-handlers/admin/index.js +41 -0
- package/dist/api-handlers/admin/redis-sessions.d.ts +36 -0
- package/dist/api-handlers/admin/redis-sessions.js +204 -0
- package/dist/api-handlers/admin/sessions.d.ts +21 -0
- package/dist/api-handlers/admin/sessions.js +284 -0
- package/dist/api-handlers/admin/site-logs.d.ts +46 -0
- package/dist/api-handlers/admin/site-logs.js +318 -0
- package/dist/api-handlers/admin/users.d.ts +20 -0
- package/dist/api-handlers/admin/users.js +222 -0
- package/dist/api-handlers/admin/vibe-data.d.ts +80 -0
- package/dist/api-handlers/admin/vibe-data.js +268 -0
- package/dist/api-handlers/anon/preferences.d.ts +37 -0
- package/dist/api-handlers/anon/preferences.js +96 -0
- package/dist/api-handlers/auth/jwks.d.ts +2 -0
- package/dist/api-handlers/auth/jwks.js +24 -0
- package/dist/api-handlers/auth/login.d.ts +42 -0
- package/dist/api-handlers/auth/login.js +178 -0
- package/dist/api-handlers/auth/refresh.d.ts +74 -0
- package/dist/api-handlers/auth/refresh.js +635 -0
- package/dist/api-handlers/auth/signout.d.ts +37 -0
- package/dist/api-handlers/auth/signout.js +187 -0
- package/dist/api-handlers/auth/status.d.ts +8 -0
- package/dist/api-handlers/auth/status.js +26 -0
- package/dist/api-handlers/auth/update-session.d.ts +37 -0
- package/dist/api-handlers/auth/update-session.js +95 -0
- package/dist/api-handlers/auth/validate.d.ts +6 -0
- package/dist/api-handlers/auth/validate.js +43 -0
- package/dist/api-handlers/auth/verify-code.d.ts +43 -0
- package/dist/api-handlers/auth/verify-code.js +94 -0
- package/dist/api-handlers/session/refresh-viability.d.ts +14 -0
- package/dist/api-handlers/session/refresh-viability.js +39 -0
- package/dist/api-handlers/session/viability.d.ts +13 -0
- package/dist/api-handlers/session/viability.js +146 -0
- package/dist/api-handlers/test/force-expire.d.ts +23 -0
- package/dist/api-handlers/test/force-expire.js +65 -0
- package/dist/auth/auth-decision.d.ts +39 -0
- package/dist/auth/auth-decision.js +182 -0
- package/dist/auth/auth-options.d.ts +57 -0
- package/dist/auth/auth-options.js +213 -0
- package/dist/auth/callbacks/index.d.ts +6 -0
- package/dist/auth/callbacks/index.js +12 -0
- package/dist/auth/callbacks/jwt.d.ts +45 -0
- package/dist/auth/callbacks/jwt.js +305 -0
- package/dist/auth/callbacks/session.d.ts +60 -0
- package/dist/auth/callbacks/session.js +170 -0
- package/dist/auth/callbacks/signin.d.ts +23 -0
- package/dist/auth/callbacks/signin.js +44 -0
- package/dist/auth/events/index.d.ts +4 -0
- package/dist/auth/events/index.js +8 -0
- package/dist/auth/events/signout.d.ts +17 -0
- package/dist/auth/events/signout.js +32 -0
- package/dist/auth/providers/credentials.d.ts +32 -0
- package/dist/auth/providers/credentials.js +223 -0
- package/dist/auth/providers/index.d.ts +5 -0
- package/dist/auth/providers/index.js +21 -0
- package/dist/auth/providers/oauth.d.ts +26 -0
- package/dist/auth/providers/oauth.js +105 -0
- package/dist/auth/route-config.d.ts +66 -0
- package/dist/auth/route-config.js +190 -0
- package/dist/auth/types/auth-types.d.ts +417 -0
- package/dist/auth/types/auth-types.js +53 -0
- package/dist/auth/types/index.d.ts +6 -0
- package/dist/auth/types/index.js +22 -0
- package/dist/auth/unauthenticated-routes.d.ts +1 -0
- package/dist/auth/unauthenticated-routes.js +19 -0
- package/dist/auth/utils/idp-client.d.ts +94 -0
- package/dist/auth/utils/idp-client.js +383 -0
- package/dist/auth/utils/index.d.ts +5 -0
- package/dist/auth/utils/index.js +21 -0
- package/dist/auth/utils/token-utils.d.ts +84 -0
- package/dist/auth/utils/token-utils.js +219 -0
- package/dist/client/AuthContext.d.ts +19 -0
- package/dist/client/AuthContext.js +112 -0
- package/dist/client/fetch-with-auth.d.ts +11 -0
- package/dist/client/fetch-with-auth.js +44 -0
- package/dist/client/fetchWithSession.d.ts +3 -0
- package/dist/client/fetchWithSession.js +24 -0
- package/dist/client/index.d.ts +9 -0
- package/dist/client/index.js +20 -0
- package/dist/client/useAnonSession.d.ts +36 -0
- package/dist/client/useAnonSession.js +99 -0
- package/dist/components/SessionSync.d.ts +13 -0
- package/dist/components/SessionSync.js +119 -0
- package/dist/components/SignalRHealthCheck.d.ts +10 -0
- package/dist/components/SignalRHealthCheck.js +97 -0
- package/dist/components/account/UserAvatarMenu.d.ts +20 -0
- package/dist/components/account/UserAvatarMenu.js +80 -0
- package/dist/components/account/index.d.ts +7 -0
- package/dist/components/account/index.js +10 -0
- package/dist/components/admin/AlertSettingsTab.d.ts +48 -0
- package/dist/components/admin/AlertSettingsTab.js +351 -0
- package/dist/components/admin/AnalyticsTab.d.ts +22 -0
- package/dist/components/admin/AnalyticsTab.js +167 -0
- package/dist/components/admin/DataBrowserTab.d.ts +19 -0
- package/dist/components/admin/DataBrowserTab.js +252 -0
- package/dist/components/admin/LoggingSettingsTab.d.ts +73 -0
- package/dist/components/admin/LoggingSettingsTab.js +339 -0
- package/dist/components/admin/SessionsTab.d.ts +37 -0
- package/dist/components/admin/SessionsTab.js +165 -0
- package/dist/components/admin/StatsTab.d.ts +53 -0
- package/dist/components/admin/StatsTab.js +161 -0
- package/dist/components/admin/VibeAdminContext.d.ts +32 -0
- package/dist/components/admin/VibeAdminContext.js +38 -0
- package/dist/components/admin/VibeAdminLayout.d.ts +11 -0
- package/dist/components/admin/VibeAdminLayout.js +69 -0
- package/dist/components/admin/index.d.ts +29 -0
- package/dist/components/admin/index.js +44 -0
- package/dist/components/auth/FederatedAuthSection.d.ts +8 -0
- package/dist/components/auth/FederatedAuthSection.js +45 -0
- package/dist/components/auth/ModeAwareLoginPage.d.ts +10 -0
- package/dist/components/auth/ModeAwareLoginPage.js +42 -0
- package/dist/components/auth/ModeAwareSignupPage.d.ts +9 -0
- package/dist/components/auth/ModeAwareSignupPage.js +78 -0
- package/dist/components/auth/TraditionalAuthSection.d.ts +14 -0
- package/dist/components/auth/TraditionalAuthSection.js +20 -0
- package/dist/components/recovery/CompleteStep.d.ts +5 -0
- package/dist/components/recovery/CompleteStep.js +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.d.ts +8 -0
- package/dist/components/recovery/InitiateRecoveryStep.js +20 -0
- package/dist/components/recovery/SelectMethodStep.d.ts +8 -0
- package/dist/components/recovery/SelectMethodStep.js +8 -0
- package/dist/components/recovery/SetPasswordStep.d.ts +6 -0
- package/dist/components/recovery/SetPasswordStep.js +20 -0
- package/dist/components/recovery/VerifyCodeStep.d.ts +10 -0
- package/dist/components/recovery/VerifyCodeStep.js +24 -0
- package/dist/components/reserved/ReservedRecoveryWarning.d.ts +38 -0
- package/dist/components/reserved/ReservedRecoveryWarning.js +92 -0
- package/dist/components/reserved/ReservedStatusBox.d.ts +30 -0
- package/dist/components/reserved/ReservedStatusBox.js +71 -0
- package/dist/components/ui/BetaBadge.d.ts +29 -0
- package/dist/components/ui/BetaBadge.js +38 -0
- package/dist/components/ui/Footer.d.ts +37 -0
- package/dist/components/ui/Footer.js +41 -0
- package/dist/config/env.d.ts +66 -0
- package/dist/config/env.js +57 -0
- package/dist/config/logger.d.ts +57 -0
- package/dist/config/logger.js +73 -0
- package/dist/config/logging-config.d.ts +30 -0
- package/dist/config/logging-config.js +122 -0
- package/dist/config/unauthenticated-routes.d.ts +17 -0
- package/dist/config/unauthenticated-routes.js +24 -0
- package/dist/config/vibe-log-transport.d.ts +79 -0
- package/dist/config/vibe-log-transport.js +203 -0
- package/dist/edge/internal-api-url.d.ts +53 -0
- package/dist/edge/internal-api-url.js +63 -0
- package/dist/edge/middleware.d.ts +14 -0
- package/dist/edge/middleware.js +32 -0
- package/dist/hooks/useAuth.d.ts +23 -0
- package/dist/hooks/useAuth.js +81 -0
- package/dist/hooks/useAuthSettings.d.ts +59 -0
- package/dist/hooks/useAuthSettings.js +93 -0
- package/dist/hooks/useAvailableProviders.d.ts +45 -0
- package/dist/hooks/useAvailableProviders.js +108 -0
- package/dist/hooks/usePasswordValidation.d.ts +27 -0
- package/dist/hooks/usePasswordValidation.js +102 -0
- package/dist/hooks/useProfile.d.ts +15 -0
- package/dist/hooks/useProfile.js +59 -0
- package/dist/hooks/usePublicAuthSettings.d.ts +56 -0
- package/dist/hooks/usePublicAuthSettings.js +131 -0
- package/dist/hooks/useSessionExpiration.d.ts +57 -0
- package/dist/hooks/useSessionExpiration.js +72 -0
- package/dist/hooks/useViabilitySession.d.ts +75 -0
- package/dist/hooks/useViabilitySession.js +268 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +54 -0
- package/dist/lib/anon-session.d.ts +74 -0
- package/dist/lib/anon-session.js +169 -0
- package/dist/lib/api-handler.d.ts +123 -0
- package/dist/lib/api-handler.js +478 -0
- package/dist/lib/app-slug.d.ts +95 -0
- package/dist/lib/app-slug.js +172 -0
- package/dist/lib/demo-mode.d.ts +6 -0
- package/dist/lib/demo-mode.js +16 -0
- package/dist/lib/geolocation.d.ts +64 -0
- package/dist/lib/geolocation.js +235 -0
- package/dist/lib/idp-client-config.d.ts +75 -0
- package/dist/lib/idp-client-config.js +351 -0
- package/dist/lib/idp-fetch.d.ts +14 -0
- package/dist/lib/idp-fetch.js +91 -0
- package/dist/lib/internal-api.d.ts +87 -0
- package/dist/lib/internal-api.js +122 -0
- package/dist/lib/jwt-decode-client.d.ts +10 -0
- package/dist/lib/jwt-decode-client.js +46 -0
- package/dist/lib/jwt-decode.d.ts +48 -0
- package/dist/lib/jwt-decode.js +57 -0
- package/dist/lib/nextauth-secret.d.ts +10 -0
- package/dist/lib/nextauth-secret.js +104 -0
- package/dist/lib/rate-limit-service.d.ts +23 -0
- package/dist/lib/rate-limit-service.js +6 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.js +28 -0
- package/dist/lib/refresh-token-validator.d.ts +13 -0
- package/dist/lib/refresh-token-validator.js +117 -0
- package/dist/lib/roles.d.ts +145 -0
- package/dist/lib/roles.js +168 -0
- package/dist/lib/secret-validation.d.ts +4 -0
- package/dist/lib/secret-validation.js +14 -0
- package/dist/lib/session-store.d.ts +166 -0
- package/dist/lib/session-store.js +537 -0
- package/dist/lib/session.d.ts +21 -0
- package/dist/lib/session.js +26 -0
- package/dist/lib/site-logger.d.ts +214 -0
- package/dist/lib/site-logger.js +210 -0
- package/dist/lib/standardized-client-api.d.ts +161 -0
- package/dist/lib/standardized-client-api.js +786 -0
- package/dist/lib/startup-init.d.ts +40 -0
- package/dist/lib/startup-init.js +261 -0
- package/dist/lib/test-aware-get-token.d.ts +2 -0
- package/dist/lib/test-aware-get-token.js +81 -0
- package/dist/lib/token-expiry.d.ts +14 -0
- package/dist/lib/token-expiry.js +39 -0
- package/dist/lib/token-lifecycle.d.ts +52 -0
- package/dist/lib/token-lifecycle.js +398 -0
- package/dist/lib/types/api-responses.d.ts +128 -0
- package/dist/lib/types/api-responses.js +171 -0
- package/dist/lib/user-agent-parser.d.ts +50 -0
- package/dist/lib/user-agent-parser.js +220 -0
- package/dist/logging/api/admin-analytics.d.ts +3 -0
- package/dist/logging/api/admin-analytics.js +45 -0
- package/dist/logging/api/audit-log.d.ts +3 -0
- package/dist/logging/api/audit-log.js +52 -0
- package/dist/logging/components/AdminAnalyticsLayout.d.ts +10 -0
- package/dist/logging/components/AdminAnalyticsLayout.js +11 -0
- package/dist/logging/components/AuditLogViewer.d.ts +7 -0
- package/dist/logging/components/AuditLogViewer.js +51 -0
- package/dist/logging/components/ErrorMetricsCard.d.ts +7 -0
- package/dist/logging/components/ErrorMetricsCard.js +16 -0
- package/dist/logging/components/HealthMetricsCard.d.ts +7 -0
- package/dist/logging/components/HealthMetricsCard.js +19 -0
- package/dist/logging/hooks/useAdminAnalytics.d.ts +24 -0
- package/dist/logging/hooks/useAdminAnalytics.js +22 -0
- package/dist/logging/hooks/useAuditLog.d.ts +6 -0
- package/dist/logging/hooks/useAuditLog.js +25 -0
- package/dist/logging/hooks/useErrorMetrics.d.ts +6 -0
- package/dist/logging/hooks/useErrorMetrics.js +38 -0
- package/dist/logging/hooks/useHealthMetrics.d.ts +6 -0
- package/dist/logging/hooks/useHealthMetrics.js +41 -0
- package/dist/logging/index.d.ts +11 -0
- package/dist/logging/index.js +40 -0
- package/dist/logging/types/analytics.d.ts +68 -0
- package/dist/logging/types/analytics.js +3 -0
- package/dist/logging/types/audit.d.ts +29 -0
- package/dist/logging/types/audit.js +2 -0
- package/dist/logging/types/index.d.ts +2 -0
- package/dist/logging/types/index.js +19 -0
- package/dist/middleware/auth-decision.d.ts +33 -0
- package/dist/middleware/auth-decision.js +65 -0
- package/dist/middleware/create-middleware.d.ts +100 -0
- package/dist/middleware/create-middleware.js +445 -0
- package/dist/middleware/rbac-check.d.ts +44 -0
- package/dist/middleware/rbac-check.js +191 -0
- package/dist/middleware/twofa-presets.d.ts +134 -0
- package/dist/middleware/twofa-presets.js +175 -0
- package/dist/models/DecodedAccessToken.d.ts +17 -0
- package/dist/models/DecodedAccessToken.js +2 -0
- package/dist/models/SessionModel.d.ts +122 -0
- package/dist/models/SessionModel.js +136 -0
- package/dist/pages/admin-login/page.d.ts +31 -0
- package/dist/pages/admin-login/page.js +83 -0
- package/dist/pages/admin-roles/RolesAdminPage.d.ts +15 -0
- package/dist/pages/admin-roles/RolesAdminPage.js +78 -0
- package/dist/pages/admin-roles/index.d.ts +8 -0
- package/dist/pages/admin-roles/index.js +15 -0
- package/dist/pages/admin-roles/modals.d.ts +72 -0
- package/dist/pages/admin-roles/modals.js +154 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.d.ts +79 -0
- package/dist/pages/client-admin/ClientSiteAdminPage.js +177 -0
- package/dist/pages/client-admin/index.d.ts +32 -0
- package/dist/pages/client-admin/index.js +37 -0
- package/dist/pages/login/page.d.ts +22 -0
- package/dist/pages/login/page.js +239 -0
- package/dist/pages/profile/EnhancedProfilePage.d.ts +13 -0
- package/dist/pages/profile/EnhancedProfilePage.js +150 -0
- package/dist/pages/profile/index.d.ts +8 -0
- package/dist/pages/profile/index.js +16 -0
- package/dist/pages/profile/page.d.ts +19 -0
- package/dist/pages/profile/page.js +47 -0
- package/dist/pages/profile/profile-patch.d.ts +1 -0
- package/dist/pages/profile/profile-patch.js +281 -0
- package/dist/pages/recovery/page.d.ts +1 -0
- package/dist/pages/recovery/page.js +142 -0
- package/dist/pages/roles/MyRolesPage.d.ts +24 -0
- package/dist/pages/roles/MyRolesPage.js +71 -0
- package/dist/pages/roles/components.d.ts +63 -0
- package/dist/pages/roles/components.js +108 -0
- package/dist/pages/roles/index.d.ts +8 -0
- package/dist/pages/roles/index.js +19 -0
- package/dist/pages/security/EnhancedSecurityPage.d.ts +14 -0
- package/dist/pages/security/EnhancedSecurityPage.js +248 -0
- package/dist/pages/security/index.d.ts +8 -0
- package/dist/pages/security/index.js +16 -0
- package/dist/pages/security/page.d.ts +21 -0
- package/dist/pages/security/page.js +212 -0
- package/dist/pages/security/security-patch.d.ts +1 -0
- package/dist/pages/security/security-patch.js +302 -0
- package/dist/pages/settings/EnhancedSettingsPage.d.ts +46 -0
- package/dist/pages/settings/EnhancedSettingsPage.js +231 -0
- package/dist/pages/settings/index.d.ts +8 -0
- package/dist/pages/settings/index.js +16 -0
- package/dist/pages/settings/page.d.ts +7 -0
- package/dist/pages/settings/page.js +26 -0
- package/dist/pages/showcase/ShowcasePage.d.ts +13 -0
- package/dist/pages/showcase/ShowcasePage.js +140 -0
- package/dist/pages/showcase/index.d.ts +12 -0
- package/dist/pages/showcase/index.js +17 -0
- package/dist/pages/test-env/EmergencyLogoutPage.d.ts +14 -0
- package/dist/pages/test-env/EmergencyLogoutPage.js +98 -0
- package/dist/pages/test-env/JwtInspectPage.d.ts +14 -0
- package/dist/pages/test-env/JwtInspectPage.js +114 -0
- package/dist/pages/test-env/RefreshTokenPage.d.ts +15 -0
- package/dist/pages/test-env/RefreshTokenPage.js +91 -0
- package/dist/pages/test-env/TestEnvPage.d.ts +13 -0
- package/dist/pages/test-env/TestEnvPage.js +49 -0
- package/dist/pages/test-env/index.d.ts +24 -0
- package/dist/pages/test-env/index.js +32 -0
- package/dist/pages/verify-code/page.d.ts +30 -0
- package/dist/pages/verify-code/page.js +408 -0
- package/dist/routes/account/index.d.ts +28 -0
- package/dist/routes/account/index.js +71 -0
- package/dist/routes/account/masked-info.d.ts +33 -0
- package/dist/routes/account/masked-info.js +39 -0
- package/dist/routes/account/send-code.d.ts +37 -0
- package/dist/routes/account/send-code.js +42 -0
- package/dist/routes/account/update-phone.d.ts +13 -0
- package/dist/routes/account/update-phone.js +17 -0
- package/dist/routes/account/verify-email.d.ts +38 -0
- package/dist/routes/account/verify-email.js +43 -0
- package/dist/routes/account/verify-sms.d.ts +38 -0
- package/dist/routes/account/verify-sms.js +43 -0
- package/dist/routes/auth/index.d.ts +19 -0
- package/dist/routes/auth/index.js +64 -0
- package/dist/routes/auth/logout.d.ts +31 -0
- package/dist/routes/auth/logout.js +113 -0
- package/dist/routes/auth/nextauth.d.ts +19 -0
- package/dist/routes/auth/nextauth.js +72 -0
- package/dist/routes/auth/refresh.d.ts +30 -0
- package/dist/routes/auth/refresh.js +51 -0
- package/dist/routes/auth/session.d.ts +72 -0
- package/dist/routes/auth/session.js +180 -0
- package/dist/routes/auth/settings.d.ts +25 -0
- package/dist/routes/auth/settings.js +55 -0
- package/dist/routes/auth/viability.d.ts +52 -0
- package/dist/routes/auth/viability.js +201 -0
- package/dist/routes/index.d.ts +12 -0
- package/dist/routes/index.js +54 -0
- package/dist/routes/session/index.d.ts +6 -0
- package/dist/routes/session/index.js +10 -0
- package/dist/routes/session/refresh-viability.d.ts +16 -0
- package/dist/routes/session/refresh-viability.js +20 -0
- package/dist/services/signalrActivityService.d.ts +44 -0
- package/dist/services/signalrActivityService.js +257 -0
- package/dist/stores/authStore.d.ts +154 -0
- package/dist/stores/authStore.js +1531 -0
- package/dist/theme/ThemeProvider.d.ts +14 -0
- package/dist/theme/ThemeProvider.js +28 -0
- package/dist/theme/default.d.ts +8 -0
- package/dist/theme/default.js +33 -0
- package/dist/theme/index.d.ts +15 -0
- package/dist/theme/index.js +25 -0
- package/dist/theme/types.d.ts +56 -0
- package/dist/theme/types.js +8 -0
- package/dist/theme/useTheme.d.ts +60 -0
- package/dist/theme/useTheme.js +63 -0
- package/dist/theme/utils.d.ts +13 -0
- package/dist/theme/utils.js +39 -0
- package/dist/types/api.d.ts +134 -0
- package/dist/types/api.js +44 -0
- package/dist/types/auth.d.ts +19 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/logging.d.ts +42 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/recovery.d.ts +48 -0
- package/dist/types/recovery.js +2 -0
- package/dist/types/security.d.ts +1 -0
- package/dist/types/security.js +2 -0
- package/dist/utils/api.d.ts +85 -0
- package/dist/utils/api.js +287 -0
- package/dist/utils/circuitBreaker.d.ts +43 -0
- package/dist/utils/circuitBreaker.js +91 -0
- package/dist/utils/error-message.d.ts +1 -0
- package/dist/utils/error-message.js +103 -0
- package/dist/utils/layout/reservedSpace.d.ts +59 -0
- package/dist/utils/layout/reservedSpace.js +102 -0
- package/dist/utils/logout.d.ts +14 -0
- package/dist/utils/logout.js +32 -0
- package/dist/vibe/client.d.ts +261 -0
- package/dist/vibe/client.js +445 -0
- package/dist/vibe/errors.d.ts +83 -0
- package/dist/vibe/errors.js +146 -0
- package/dist/vibe/generic.d.ts +234 -0
- package/dist/vibe/generic.js +369 -0
- package/dist/vibe/hooks/index.d.ts +169 -0
- package/dist/vibe/hooks/index.js +252 -0
- package/dist/vibe/index.d.ts +23 -0
- package/dist/vibe/index.js +67 -0
- package/dist/vibe/sessions.d.ts +161 -0
- package/dist/vibe/sessions.js +391 -0
- package/dist/vibe/types.d.ts +353 -0
- package/dist/vibe/types.js +315 -0
- package/package.json +855 -0
- package/scripts/check-internal-url-usage.sh +73 -0
- package/scripts/dev-broker.ps1 +35 -0
- package/scripts/dev-local.ps1 +45 -0
- package/src/api/auth-handler.ts +550 -0
- package/src/api/index.ts +18 -0
- package/src/api-handlers/account/change-password.ts +145 -0
- package/src/api-handlers/account/masked-info.ts +45 -0
- package/src/api-handlers/account/profile.ts +80 -0
- package/src/api-handlers/account/recovery/initiate.ts +23 -0
- package/src/api-handlers/account/recovery/send-code.ts +25 -0
- package/src/api-handlers/account/recovery/verify-code.ts +25 -0
- package/src/api-handlers/account/reset-password.ts +23 -0
- package/src/api-handlers/account/send-code.ts +76 -0
- package/src/api-handlers/account/update-phone.ts +79 -0
- package/src/api-handlers/account/validate-password.ts +118 -0
- package/src/api-handlers/account/verify-email.ts +125 -0
- package/src/api-handlers/account/verify-sms.ts +125 -0
- package/src/api-handlers/admin/analytics.ts +445 -0
- package/src/api-handlers/admin/audit.ts +225 -0
- package/src/api-handlers/admin/index.ts +59 -0
- package/src/api-handlers/admin/redis-sessions.ts +253 -0
- package/src/api-handlers/admin/sessions.ts +320 -0
- package/src/api-handlers/admin/site-logs.ts +367 -0
- package/src/api-handlers/admin/users.ts +244 -0
- package/src/api-handlers/admin/vibe-data.ts +326 -0
- package/src/api-handlers/anon/preferences.ts +123 -0
- package/src/api-handlers/auth/jwks.ts +20 -0
- package/src/api-handlers/auth/login.ts +240 -0
- package/src/api-handlers/auth/refresh.ts +687 -0
- package/src/api-handlers/auth/signout.ts +212 -0
- package/src/api-handlers/auth/status.ts +23 -0
- package/src/api-handlers/auth/update-session.ts +125 -0
- package/src/api-handlers/auth/validate.ts +44 -0
- package/src/api-handlers/auth/verify-code.ts +129 -0
- package/src/api-handlers/session/refresh-viability.ts +36 -0
- package/src/api-handlers/session/viability.ts +166 -0
- package/src/api-handlers/test/force-expire.ts +67 -0
- package/src/auth/auth-decision.ts +230 -0
- package/src/auth/auth-options.ts +237 -0
- package/src/auth/callbacks/index.ts +7 -0
- package/src/auth/callbacks/jwt.ts +382 -0
- package/src/auth/callbacks/session.ts +243 -0
- package/src/auth/callbacks/signin.ts +56 -0
- package/src/auth/events/index.ts +5 -0
- package/src/auth/events/signout.ts +33 -0
- package/src/auth/providers/credentials.ts +256 -0
- package/src/auth/providers/index.ts +6 -0
- package/src/auth/providers/oauth.ts +114 -0
- package/src/auth/route-config.ts +220 -0
- package/src/auth/types/auth-types.ts +555 -0
- package/src/auth/types/index.ts +7 -0
- package/src/auth/unauthenticated-routes.ts +3 -0
- package/src/auth/utils/idp-client.ts +444 -0
- package/src/auth/utils/index.ts +6 -0
- package/src/auth/utils/token-utils.ts +244 -0
- package/src/client/AuthContext.tsx +140 -0
- package/src/client/fetch-with-auth.ts +48 -0
- package/src/client/fetchWithSession.ts +21 -0
- package/src/client/index.ts +13 -0
- package/src/client/useAnonSession.ts +131 -0
- package/src/components/SessionSync.tsx +137 -0
- package/src/components/SignalRHealthCheck.tsx +131 -0
- package/src/components/account/UserAvatarMenu.tsx +217 -0
- package/src/components/account/index.ts +8 -0
- package/src/components/admin/AlertSettingsTab.tsx +728 -0
- package/src/components/admin/AnalyticsTab.tsx +703 -0
- package/src/components/admin/DataBrowserTab.tsx +505 -0
- package/src/components/admin/LoggingSettingsTab.tsx +665 -0
- package/src/components/admin/SessionsTab.tsx +414 -0
- package/src/components/admin/StatsTab.tsx +379 -0
- package/src/components/admin/VibeAdminContext.tsx +87 -0
- package/src/components/admin/VibeAdminLayout.tsx +185 -0
- package/src/components/admin/index.ts +59 -0
- package/src/components/auth/FederatedAuthSection.tsx +95 -0
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -0
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -0
- package/src/components/auth/TraditionalAuthSection.tsx +99 -0
- package/src/components/recovery/CompleteStep.tsx +36 -0
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -0
- package/src/components/recovery/SelectMethodStep.tsx +73 -0
- package/src/components/recovery/SetPasswordStep.tsx +97 -0
- package/src/components/recovery/VerifyCodeStep.tsx +90 -0
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -0
- package/src/components/reserved/ReservedStatusBox.tsx +118 -0
- package/src/components/ui/BetaBadge.tsx +58 -0
- package/src/components/ui/Footer.tsx +93 -0
- package/src/config/env.ts +57 -0
- package/src/config/logger.ts +62 -0
- package/src/config/logging-config.ts +82 -0
- package/src/config/unauthenticated-routes.ts +19 -0
- package/src/config/vibe-log-transport.ts +250 -0
- package/src/edge/internal-api-url.ts +65 -0
- package/src/edge/middleware.ts +42 -0
- package/src/hooks/useAuth.ts +115 -0
- package/src/hooks/useAuthSettings.ts +97 -0
- package/src/hooks/useAvailableProviders.ts +118 -0
- package/src/hooks/usePasswordValidation.ts +127 -0
- package/src/hooks/useProfile.ts +75 -0
- package/src/hooks/usePublicAuthSettings.ts +149 -0
- package/src/hooks/useSessionExpiration.ts +102 -0
- package/src/hooks/useViabilitySession.ts +335 -0
- package/src/index.ts +63 -0
- package/src/lib/anon-session.ts +213 -0
- package/src/lib/api-handler.ts +625 -0
- package/src/lib/app-slug.ts +178 -0
- package/src/lib/demo-mode.ts +13 -0
- package/src/lib/geolocation.ts +265 -0
- package/src/lib/idp-client-config.ts +442 -0
- package/src/lib/idp-fetch.ts +101 -0
- package/src/lib/internal-api.ts +171 -0
- package/src/lib/jwt-decode-client.ts +45 -0
- package/src/lib/jwt-decode.ts +83 -0
- package/src/lib/nextauth-secret.ts +126 -0
- package/src/lib/rate-limit-service.ts +9 -0
- package/src/lib/redis.ts +27 -0
- package/src/lib/refresh-token-validator.ts +64 -0
- package/src/lib/roles.ts +177 -0
- package/src/lib/secret-validation.ts +8 -0
- package/src/lib/session-store.ts +637 -0
- package/src/lib/session.ts +34 -0
- package/src/lib/site-logger.ts +245 -0
- package/src/lib/standardized-client-api.ts +896 -0
- package/src/lib/startup-init.ts +247 -0
- package/src/lib/test-aware-get-token.ts +30 -0
- package/src/lib/token-expiry.ts +40 -0
- package/src/lib/token-lifecycle.ts +477 -0
- package/src/lib/types/api-responses.ts +336 -0
- package/src/lib/user-agent-parser.ts +252 -0
- package/src/logging/api/admin-analytics.ts +51 -0
- package/src/logging/api/audit-log.ts +53 -0
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -0
- package/src/logging/components/AuditLogViewer.tsx +125 -0
- package/src/logging/components/ErrorMetricsCard.tsx +98 -0
- package/src/logging/components/HealthMetricsCard.tsx +70 -0
- package/src/logging/hooks/useAdminAnalytics.ts +22 -0
- package/src/logging/hooks/useAuditLog.ts +24 -0
- package/src/logging/hooks/useErrorMetrics.ts +40 -0
- package/src/logging/hooks/useHealthMetrics.ts +44 -0
- package/src/logging/index.ts +18 -0
- package/src/logging/types/analytics.ts +81 -0
- package/src/logging/types/audit.ts +31 -0
- package/src/logging/types/index.ts +3 -0
- package/src/middleware/auth-decision.ts +43 -0
- package/src/middleware/create-middleware.ts +626 -0
- package/src/middleware/rbac-check.ts +244 -0
- package/src/middleware/twofa-presets.ts +224 -0
- package/src/models/DecodedAccessToken.ts +17 -0
- package/src/models/SessionModel.ts +258 -0
- package/src/pages/admin-login/page.tsx +229 -0
- package/src/pages/admin-roles/RolesAdminPage.tsx +357 -0
- package/src/pages/admin-roles/index.ts +9 -0
- package/src/pages/admin-roles/modals.tsx +469 -0
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +380 -0
- package/src/pages/client-admin/index.ts +33 -0
- package/src/pages/login/page.tsx +463 -0
- package/src/pages/profile/EnhancedProfilePage.tsx +479 -0
- package/src/pages/profile/index.ts +9 -0
- package/src/pages/profile/page.tsx +166 -0
- package/src/pages/recovery/page.tsx +234 -0
- package/src/pages/roles/MyRolesPage.tsx +211 -0
- package/src/pages/roles/components.tsx +294 -0
- package/src/pages/roles/index.ts +17 -0
- package/src/pages/security/EnhancedSecurityPage.tsx +574 -0
- package/src/pages/security/index.ts +9 -0
- package/src/pages/security/page.tsx +507 -0
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -0
- package/src/pages/settings/index.ts +9 -0
- package/src/pages/settings/page.tsx +47 -0
- package/src/pages/showcase/ShowcasePage.tsx +530 -0
- package/src/pages/showcase/index.ts +13 -0
- package/src/pages/test-env/EmergencyLogoutPage.tsx +179 -0
- package/src/pages/test-env/JwtInspectPage.tsx +418 -0
- package/src/pages/test-env/RefreshTokenPage.tsx +155 -0
- package/src/pages/test-env/TestEnvPage.tsx +116 -0
- package/src/pages/test-env/index.ts +25 -0
- package/src/pages/verify-code/page.tsx +648 -0
- package/src/routes/account/index.ts +32 -0
- package/src/routes/account/masked-info.ts +37 -0
- package/src/routes/account/send-code.ts +40 -0
- package/src/routes/account/update-phone.ts +13 -0
- package/src/routes/account/verify-email.ts +41 -0
- package/src/routes/account/verify-sms.ts +41 -0
- package/src/routes/auth/index.ts +23 -0
- package/src/routes/auth/logout.ts +127 -0
- package/src/routes/auth/nextauth.ts +71 -0
- package/src/routes/auth/refresh.ts +54 -0
- package/src/routes/auth/session.ts +193 -0
- package/src/routes/auth/settings.ts +75 -0
- package/src/routes/auth/viability.ts +220 -0
- package/src/routes/index.ts +18 -0
- package/src/routes/session/index.ts +7 -0
- package/src/routes/session/refresh-viability.ts +17 -0
- package/src/services/signalrActivityService.ts +258 -0
- package/src/stores/authStore.ts +1904 -0
- package/src/templates/instrumentation.ts +41 -0
- package/src/theme/ThemeProvider.tsx +39 -0
- package/src/theme/default.ts +33 -0
- package/src/theme/index.ts +31 -0
- package/src/theme/types.ts +69 -0
- package/src/theme/useTheme.ts +57 -0
- package/src/theme/utils.ts +40 -0
- package/src/types/api.ts +13 -0
- package/src/types/auth.d.ts +15 -0
- package/src/types/auth.ts +22 -0
- package/src/types/logging.ts +11 -0
- package/src/types/next-auth.d.ts +15 -0
- package/src/types/recovery.ts +54 -0
- package/src/types/security.ts +1 -0
- package/src/utils/api.ts +353 -0
- package/src/utils/circuitBreaker.ts +40 -0
- package/src/utils/error-message.ts +108 -0
- package/src/utils/layout/reservedSpace.ts +124 -0
- package/src/utils/logout.ts +30 -0
- package/src/vibe/client.ts +590 -0
- package/src/vibe/errors.ts +185 -0
- package/src/vibe/generic.ts +429 -0
- package/src/vibe/hooks/index.ts +367 -0
- package/src/vibe/index.ts +121 -0
- package/src/vibe/sessions.ts +551 -0
- package/src/vibe/types.ts +577 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Lifecycle Management for @payez/next-mvp
|
|
3
|
+
*
|
|
4
|
+
* Ensures tokens are fresh before making API calls.
|
|
5
|
+
* Checks expiration and triggers refresh if needed.
|
|
6
|
+
*
|
|
7
|
+
* Pattern: Check first, refresh if needed, fail gracefully if refresh fails.
|
|
8
|
+
*
|
|
9
|
+
* HANDLES CONCURRENT REFRESH: When multiple API calls arrive simultaneously
|
|
10
|
+
* with expired tokens, only one will actually perform the refresh. Others
|
|
11
|
+
* receive 409 (conflict) and wait for the refresh to complete, then use
|
|
12
|
+
* the freshly refreshed tokens.
|
|
13
|
+
*
|
|
14
|
+
* REQUIRED: Your app must expose the refresh route:
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // app/api/auth/refresh/route.ts
|
|
17
|
+
* export { POST } from '@payez/next-mvp/routes/auth/refresh';
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @version 2.0.0
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { NextRequest } from 'next/server';
|
|
24
|
+
import { getToken } from 'next-auth/jwt';
|
|
25
|
+
import { getSession, SessionData } from './session-store';
|
|
26
|
+
import { getJwtCookieName } from './app-slug';
|
|
27
|
+
import { getIDPClientConfig } from './idp-client-config';
|
|
28
|
+
|
|
29
|
+
// 5 minute threshold for "needs refresh" - matches refresh handler pattern
|
|
30
|
+
const REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
// Concurrent refresh handling configuration
|
|
33
|
+
const CONCURRENT_REFRESH_POLL_INTERVAL_MS = 200; // How often to poll session during concurrent refresh
|
|
34
|
+
const CONCURRENT_REFRESH_MAX_WAIT_MS = 8000; // Max time to wait for concurrent refresh to complete
|
|
35
|
+
const REFRESH_RETRY_DELAY_MS = 500; // Delay before retrying after failed concurrent refresh
|
|
36
|
+
const KEY_PROPAGATION_DELAY_MS = 150; // Delay after refresh to allow JWKS cache updates in downstream services
|
|
37
|
+
|
|
38
|
+
export interface TokenResult {
|
|
39
|
+
success: true;
|
|
40
|
+
accessToken: string;
|
|
41
|
+
sessionData: SessionData;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TokenError {
|
|
45
|
+
success: false;
|
|
46
|
+
error: 'NO_SESSION' | 'NO_TOKEN' | 'EXPIRED' | 'REFRESH_FAILED' | 'SESSION_EXPIRED_NO_REFRESH';
|
|
47
|
+
message: string;
|
|
48
|
+
terminal?: boolean; // If true, don't retry - redirect to login
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type EnsureFreshTokenResult = TokenResult | TokenError;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if token needs refresh based on expiration time
|
|
55
|
+
*/
|
|
56
|
+
function needsRefresh(accessTokenExpires: number | undefined): boolean {
|
|
57
|
+
if (!accessTokenExpires) return true;
|
|
58
|
+
const timeUntilExpiry = accessTokenExpires - Date.now();
|
|
59
|
+
return timeUntilExpiry <= REFRESH_THRESHOLD_MS;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Helper to delay execution
|
|
64
|
+
*/
|
|
65
|
+
function delay(ms: number): Promise<void> {
|
|
66
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Wait for a concurrent refresh to complete by polling the session.
|
|
71
|
+
* Returns true if session becomes fresh, false if timeout reached.
|
|
72
|
+
*/
|
|
73
|
+
async function waitForConcurrentRefresh(
|
|
74
|
+
sessionToken: string,
|
|
75
|
+
maxWaitMs: number = CONCURRENT_REFRESH_MAX_WAIT_MS
|
|
76
|
+
): Promise<{ success: boolean; sessionData?: SessionData }> {
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
|
|
79
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
80
|
+
await delay(CONCURRENT_REFRESH_POLL_INTERVAL_MS);
|
|
81
|
+
|
|
82
|
+
const sessionData = await getSession(sessionToken);
|
|
83
|
+
|
|
84
|
+
if (!sessionData) {
|
|
85
|
+
return { success: false };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if token is now fresh
|
|
89
|
+
if (!needsRefresh(sessionData.idpAccessTokenExpires)) {
|
|
90
|
+
return { success: true, sessionData };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check if session has a new access token (even if still within threshold)
|
|
94
|
+
if (sessionData.idpAccessToken && sessionData.idpAccessTokenExpires &&
|
|
95
|
+
sessionData.idpAccessTokenExpires > Date.now()) {
|
|
96
|
+
return { success: true, sessionData };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { success: false };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the internal API URL for making internal service calls.
|
|
105
|
+
* INTERNAL_API_URL is REQUIRED - no fallbacks.
|
|
106
|
+
*/
|
|
107
|
+
function getInternalApiUrl(request: NextRequest): string {
|
|
108
|
+
const internalUrl = process.env.INTERNAL_API_URL;
|
|
109
|
+
if (!internalUrl) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
'[INTERNAL_API_URL] FATAL: INTERNAL_API_URL environment variable is REQUIRED. ' +
|
|
112
|
+
'Set it to this app\'s internal K8s service URL (e.g., http://myapp.namespace.svc.cluster.local:80) ' +
|
|
113
|
+
'or http://localhost:3000 for local development.'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return internalUrl;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Result of triggerRefresh - includes terminal flag for unrecoverable errors
|
|
121
|
+
*/
|
|
122
|
+
interface RefreshResult {
|
|
123
|
+
success: boolean;
|
|
124
|
+
terminal?: boolean; // If true, session is dead - don't retry, redirect to login
|
|
125
|
+
code?: string; // Error code from refresh endpoint
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Trigger a token refresh via the refresh API endpoint.
|
|
130
|
+
*
|
|
131
|
+
* HANDLES CONCURRENT REFRESH (409):
|
|
132
|
+
* When another request is already refreshing the token, this function
|
|
133
|
+
* waits for that refresh to complete instead of failing immediately.
|
|
134
|
+
* This prevents race conditions where multiple parallel API calls
|
|
135
|
+
* could cause unnecessary refresh failures.
|
|
136
|
+
*
|
|
137
|
+
* TERMINAL STATES:
|
|
138
|
+
* Returns { success: false, terminal: true } when the session cannot be
|
|
139
|
+
* recovered (e.g., no refresh token). Callers should redirect to login.
|
|
140
|
+
*/
|
|
141
|
+
async function triggerRefresh(
|
|
142
|
+
request: NextRequest,
|
|
143
|
+
sessionToken: string,
|
|
144
|
+
retryCount: number = 0
|
|
145
|
+
): Promise<RefreshResult> {
|
|
146
|
+
const maxRetries = 2;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const baseUrl = getInternalApiUrl(request);
|
|
150
|
+
const requestId = `refresh_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
151
|
+
|
|
152
|
+
const response = await fetch(`${baseUrl}/api/auth/refresh`, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
'Cookie': request.headers.get('cookie') || '',
|
|
157
|
+
'X-Session-Token': sessionToken,
|
|
158
|
+
'X-Request-Id': requestId,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Handle 409 Conflict - another refresh is in progress
|
|
163
|
+
if (response.status === 409) {
|
|
164
|
+
// Wait for the concurrent refresh to complete
|
|
165
|
+
const waitResult = await waitForConcurrentRefresh(sessionToken);
|
|
166
|
+
|
|
167
|
+
if (waitResult.success) {
|
|
168
|
+
return { success: true };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Concurrent refresh didn't produce a fresh token - try again if we have retries left
|
|
172
|
+
if (retryCount < maxRetries) {
|
|
173
|
+
await delay(REFRESH_RETRY_DELAY_MS);
|
|
174
|
+
return triggerRefresh(request, sessionToken, retryCount + 1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { success: false };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Handle other non-OK responses
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
// Parse response body to check for terminal errors
|
|
183
|
+
let responseData: any = {};
|
|
184
|
+
try {
|
|
185
|
+
responseData = await response.json();
|
|
186
|
+
} catch {
|
|
187
|
+
// Ignore parse errors
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Log the failure for debugging
|
|
191
|
+
console.warn('[TOKEN_LIFECYCLE] Refresh request failed:', {
|
|
192
|
+
status: response.status,
|
|
193
|
+
statusText: response.statusText,
|
|
194
|
+
baseUrl,
|
|
195
|
+
retryCount,
|
|
196
|
+
code: responseData.code,
|
|
197
|
+
terminal: responseData.terminal
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// CHECK FOR TERMINAL STATE: No refresh token = session is dead
|
|
201
|
+
// Don't retry - user must re-authenticate
|
|
202
|
+
if (responseData.code === 'NO_REFRESH_TOKEN' || responseData.terminal === true) {
|
|
203
|
+
console.error('[TOKEN_LIFECYCLE] TERMINAL: Session has no refresh token - user must re-login');
|
|
204
|
+
return { success: false, terminal: true, code: responseData.code };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// For other 401s, check if maybe session was refreshed by another request
|
|
208
|
+
if (response.status === 401 && retryCount < maxRetries) {
|
|
209
|
+
await delay(REFRESH_RETRY_DELAY_MS);
|
|
210
|
+
|
|
211
|
+
const sessionData = await getSession(sessionToken);
|
|
212
|
+
if (sessionData && !needsRefresh(sessionData.idpAccessTokenExpires)) {
|
|
213
|
+
return { success: true };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return { success: false, code: responseData.code };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const result = await response.json();
|
|
221
|
+
const success = result.refreshed === true || result.reason === 'already_fresh';
|
|
222
|
+
return { success };
|
|
223
|
+
} catch (error) {
|
|
224
|
+
// Log network errors for debugging
|
|
225
|
+
console.error('[TOKEN_LIFECYCLE] Refresh network error:', {
|
|
226
|
+
error: error instanceof Error ? error.message : String(error),
|
|
227
|
+
retryCount
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// On network error, check if maybe another request refreshed the token
|
|
231
|
+
if (retryCount < maxRetries) {
|
|
232
|
+
await delay(REFRESH_RETRY_DELAY_MS);
|
|
233
|
+
const sessionData = await getSession(sessionToken);
|
|
234
|
+
if (sessionData && !needsRefresh(sessionData.idpAccessTokenExpires)) {
|
|
235
|
+
return { success: true };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { success: false };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Ensures we have a fresh access token before making API calls.
|
|
245
|
+
*
|
|
246
|
+
* This utility checks token expiration and triggers a refresh if needed,
|
|
247
|
+
* preventing 401 errors from expired tokens being sent to downstream APIs.
|
|
248
|
+
*
|
|
249
|
+
* @param request - The incoming NextRequest
|
|
250
|
+
* @returns TokenResult with accessToken and sessionData, or TokenError
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* import { ensureFreshToken } from '@payez/next-mvp/lib/token-lifecycle';
|
|
255
|
+
*
|
|
256
|
+
* export async function GET(request: NextRequest) {
|
|
257
|
+
* const tokenResult = await ensureFreshToken(request);
|
|
258
|
+
* if (!tokenResult.success) {
|
|
259
|
+
* return NextResponse.json({ error: tokenResult.error }, { status: 401 });
|
|
260
|
+
* }
|
|
261
|
+
*
|
|
262
|
+
* // Use tokenResult.accessToken for downstream API calls
|
|
263
|
+
* const response = await fetch('https://api.example.com/data', {
|
|
264
|
+
* headers: { 'Authorization': `Bearer ${tokenResult.accessToken}` }
|
|
265
|
+
* });
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
/**
|
|
270
|
+
* Get NextAuth secret from IDP config (cached).
|
|
271
|
+
* NEVER use process.env.NEXTAUTH_SECRET directly - it may not be set.
|
|
272
|
+
*/
|
|
273
|
+
async function getNextAuthSecret(): Promise<string> {
|
|
274
|
+
const config = await getIDPClientConfig();
|
|
275
|
+
return config.nextAuthSecret || '';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function ensureFreshToken(
|
|
279
|
+
request: NextRequest
|
|
280
|
+
): Promise<EnsureFreshTokenResult> {
|
|
281
|
+
try {
|
|
282
|
+
// 1. Get NextAuth JWT to extract sessionToken
|
|
283
|
+
// Use IDP config to get the secret (same as viability.ts)
|
|
284
|
+
const secret = await getNextAuthSecret();
|
|
285
|
+
if (!secret) {
|
|
286
|
+
console.error('[TOKEN_LIFECYCLE] NEXTAUTH_SECRET not available from IDP config');
|
|
287
|
+
return { success: false, error: 'NO_SESSION', message: 'Auth not configured' };
|
|
288
|
+
}
|
|
289
|
+
const cookieName = getJwtCookieName();
|
|
290
|
+
const token = await getToken({
|
|
291
|
+
req: request,
|
|
292
|
+
secret,
|
|
293
|
+
cookieName,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
|
|
297
|
+
const sessionTokenFromJwt = (token?.sessionToken || token?.redisSessionId) as string | undefined;
|
|
298
|
+
if (!sessionTokenFromJwt) {
|
|
299
|
+
// Debug: log what we got including cookie presence and value info
|
|
300
|
+
const cookieHeader = request.headers.get('cookie') || '';
|
|
301
|
+
const hasCookie = cookieHeader.includes(cookieName);
|
|
302
|
+
|
|
303
|
+
// Extract the actual cookie value to check if it's empty
|
|
304
|
+
const cookieMatch = cookieHeader.match(new RegExp(`${cookieName}=([^;]*)`));
|
|
305
|
+
const cookieValue = cookieMatch ? cookieMatch[1] : null;
|
|
306
|
+
const cookieValueLength = cookieValue?.length || 0;
|
|
307
|
+
|
|
308
|
+
console.warn('[TOKEN_LIFECYCLE] NO_SESSION -', {
|
|
309
|
+
token: token ? 'exists but no sessionToken/redisSessionId' : 'null',
|
|
310
|
+
cookieName,
|
|
311
|
+
hasCookie,
|
|
312
|
+
cookieValueLength,
|
|
313
|
+
cookieValuePreview: cookieValue ? cookieValue.substring(0, 20) + '...' : 'EMPTY',
|
|
314
|
+
cookieHeaderLength: cookieHeader.length,
|
|
315
|
+
secretLength: secret.length,
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
success: false,
|
|
319
|
+
error: 'NO_SESSION',
|
|
320
|
+
message: 'No session available',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const sessionToken = sessionTokenFromJwt;
|
|
325
|
+
|
|
326
|
+
// 2. Get session data from Redis
|
|
327
|
+
let sessionData = await getSession(sessionToken);
|
|
328
|
+
|
|
329
|
+
if (!sessionData) {
|
|
330
|
+
return {
|
|
331
|
+
success: false,
|
|
332
|
+
error: 'NO_SESSION',
|
|
333
|
+
message: 'Session expired or not found',
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// DEBUG: Log session data before refresh check
|
|
338
|
+
const tokenExpiresStr = sessionData.idpAccessTokenExpires
|
|
339
|
+
? new Date(sessionData.idpAccessTokenExpires).toISOString()
|
|
340
|
+
: 'undefined';
|
|
341
|
+
let needsRefreshNow = needsRefresh(sessionData.idpAccessTokenExpires);
|
|
342
|
+
|
|
343
|
+
// VALIDATION: Check if the actual JWT token's exp matches what Redis claims
|
|
344
|
+
// This catches cases where accessTokenExpires was updated but accessToken wasn't
|
|
345
|
+
let tokenMismatch = false;
|
|
346
|
+
if (sessionData.idpAccessToken && !needsRefreshNow) {
|
|
347
|
+
try {
|
|
348
|
+
const tokenParts = sessionData.idpAccessToken.split('.');
|
|
349
|
+
if (tokenParts.length === 3) {
|
|
350
|
+
const payload = JSON.parse(Buffer.from(tokenParts[1], 'base64url').toString());
|
|
351
|
+
const jwtExpMs = (payload.exp || 0) * 1000;
|
|
352
|
+
const now = Date.now();
|
|
353
|
+
|
|
354
|
+
// If the JWT is actually expired, force a refresh regardless of what Redis says
|
|
355
|
+
if (jwtExpMs < now) {
|
|
356
|
+
console.warn('[TOKEN_LIFECYCLE] Token mismatch detected! JWT expired but Redis claims valid', {
|
|
357
|
+
jwtExp: new Date(jwtExpMs).toISOString(),
|
|
358
|
+
redisAccessTokenExpires: tokenExpiresStr,
|
|
359
|
+
now: new Date(now).toISOString(),
|
|
360
|
+
mismatchMs: sessionData.idpAccessTokenExpires ? sessionData.idpAccessTokenExpires - jwtExpMs : 'N/A'
|
|
361
|
+
});
|
|
362
|
+
needsRefreshNow = true;
|
|
363
|
+
tokenMismatch = true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} catch (e) {
|
|
367
|
+
// If we can't decode, proceed with normal logic
|
|
368
|
+
console.warn('[TOKEN_LIFECYCLE] Could not validate JWT exp claim:', e);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
console.log('[TOKEN_LIFECYCLE] ensureFreshToken check:', {
|
|
373
|
+
sessionToken: sessionToken.substring(0, 8) + '...',
|
|
374
|
+
accessTokenExpires: tokenExpiresStr,
|
|
375
|
+
now: new Date().toISOString(),
|
|
376
|
+
needsRefresh: needsRefreshNow,
|
|
377
|
+
tokenMismatch,
|
|
378
|
+
hasRefreshToken: !!sessionData.idpRefreshToken
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// 3. Check if token needs refresh
|
|
382
|
+
if (needsRefreshNow) {
|
|
383
|
+
// 4. Trigger refresh
|
|
384
|
+
console.log('[TOKEN_LIFECYCLE] Triggering refresh...');
|
|
385
|
+
const refreshResult = await triggerRefresh(request, sessionToken);
|
|
386
|
+
|
|
387
|
+
if (!refreshResult.success) {
|
|
388
|
+
// Check for terminal state - session cannot be recovered
|
|
389
|
+
if (refreshResult.terminal) {
|
|
390
|
+
console.error('[TOKEN_LIFECYCLE] TERMINAL: Session expired with no refresh token - redirect to login');
|
|
391
|
+
return {
|
|
392
|
+
success: false,
|
|
393
|
+
error: 'SESSION_EXPIRED_NO_REFRESH',
|
|
394
|
+
message: 'Session expired. Please sign in again.',
|
|
395
|
+
terminal: true,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.warn('[TOKEN_LIFECYCLE] Refresh failed');
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
error: 'REFRESH_FAILED',
|
|
403
|
+
message: 'Token refresh failed',
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 5. Re-fetch session data after refresh
|
|
408
|
+
sessionData = await getSession(sessionToken);
|
|
409
|
+
console.log('[TOKEN_LIFECYCLE] After refresh:', {
|
|
410
|
+
hasAccessToken: !!sessionData?.idpAccessToken,
|
|
411
|
+
newAccessTokenExpires: sessionData?.idpAccessTokenExpires
|
|
412
|
+
? new Date(sessionData.idpAccessTokenExpires).toISOString()
|
|
413
|
+
: 'undefined'
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
if (!sessionData?.idpAccessToken) {
|
|
417
|
+
return {
|
|
418
|
+
success: false,
|
|
419
|
+
error: 'REFRESH_FAILED',
|
|
420
|
+
message: 'No access token after refresh',
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 5.5. Key propagation delay - allow downstream services (like Vibe) to cache new JWKS
|
|
425
|
+
// This is critical when IDP rotates signing keys - the new token's kid may not be
|
|
426
|
+
// immediately available in Vibe's JWKS cache
|
|
427
|
+
await delay(KEY_PROPAGATION_DELAY_MS);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// 6. Validate we have a token
|
|
431
|
+
if (!sessionData.idpAccessToken) {
|
|
432
|
+
return {
|
|
433
|
+
success: false,
|
|
434
|
+
error: 'NO_TOKEN',
|
|
435
|
+
message: 'No access token available',
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
success: true,
|
|
441
|
+
accessToken: sessionData.idpAccessToken,
|
|
442
|
+
sessionData,
|
|
443
|
+
};
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('[TOKEN_LIFECYCLE] Error:', error);
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
error: 'NO_SESSION',
|
|
449
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Get authorization header from fresh token.
|
|
456
|
+
* Convenience wrapper for API routes.
|
|
457
|
+
*
|
|
458
|
+
* @param request - The incoming NextRequest
|
|
459
|
+
* @returns Authorization header string or null if token unavailable
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```typescript
|
|
463
|
+
* const authHeader = await getFreshAuthHeader(request);
|
|
464
|
+
* if (!authHeader) {
|
|
465
|
+
* return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
466
|
+
* }
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
export async function getFreshAuthHeader(
|
|
470
|
+
request: NextRequest
|
|
471
|
+
): Promise<string | null> {
|
|
472
|
+
const result = await ensureFreshToken(request);
|
|
473
|
+
if (!result.success) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
return `Bearer ${result.accessToken}`;
|
|
477
|
+
}
|