@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,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* IDP Client Configuration
|
|
4
|
+
*
|
|
5
|
+
* Fetches full client configuration from IDP including:
|
|
6
|
+
* - OAuth provider credentials (from Key Vault)
|
|
7
|
+
* - 2FA/MFA settings
|
|
8
|
+
* - Session configuration
|
|
9
|
+
* - NextAuth secret
|
|
10
|
+
* - Branding
|
|
11
|
+
*
|
|
12
|
+
* CACHING STRATEGY:
|
|
13
|
+
* 1. In-memory cache (fastest, but lost on module reload in dev)
|
|
14
|
+
* 2. Redis cache (survives module reloads, shared across instances)
|
|
15
|
+
* 3. IDP fetch (when both caches miss)
|
|
16
|
+
*
|
|
17
|
+
* NO FALLBACKS. If IDP doesn't respond correctly, we fail loud.
|
|
18
|
+
*
|
|
19
|
+
* @version 2.0.0 - Added Redis-backed caching
|
|
20
|
+
*/
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.getIDPClientConfig = getIDPClientConfig;
|
|
26
|
+
exports.clearConfigCache = clearConfigCache;
|
|
27
|
+
exports.getEnabledProviders = getEnabledProviders;
|
|
28
|
+
require("server-only");
|
|
29
|
+
const crypto_1 = require("crypto");
|
|
30
|
+
const redis_1 = __importDefault(require("./redis"));
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Cache & Fetch Deduplication
|
|
33
|
+
// ============================================================================
|
|
34
|
+
let cachedConfig = null;
|
|
35
|
+
let cacheExpiry = 0;
|
|
36
|
+
let pendingFetch = null; // Prevents parallel fetches
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Redis Cache Configuration
|
|
39
|
+
// ============================================================================
|
|
40
|
+
const REDIS_CONFIG_KEY_PREFIX = 'idp_config:';
|
|
41
|
+
function getRedisConfigKey() {
|
|
42
|
+
const clientId = process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID || 'default';
|
|
43
|
+
return `${REDIS_CONFIG_KEY_PREFIX}${clientId}`;
|
|
44
|
+
}
|
|
45
|
+
async function getConfigFromRedis() {
|
|
46
|
+
try {
|
|
47
|
+
const key = getRedisConfigKey();
|
|
48
|
+
const cached = await redis_1.default.get(key);
|
|
49
|
+
if (!cached)
|
|
50
|
+
return null;
|
|
51
|
+
const parsed = JSON.parse(cached);
|
|
52
|
+
if (Date.now() >= parsed.expiresAt) {
|
|
53
|
+
// Expired, delete it
|
|
54
|
+
await redis_1.default.del(key);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return parsed.config;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn('[IDP_CONFIG] Failed to read from Redis cache:', error);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function setConfigInRedis(config) {
|
|
65
|
+
try {
|
|
66
|
+
const key = getRedisConfigKey();
|
|
67
|
+
const ttlSeconds = config.configCacheTtlSeconds || 300;
|
|
68
|
+
const data = {
|
|
69
|
+
config,
|
|
70
|
+
expiresAt: Date.now() + (ttlSeconds * 1000)
|
|
71
|
+
};
|
|
72
|
+
// Store with TTL slightly longer than the logical expiry to allow for clock skew
|
|
73
|
+
await redis_1.default.set(key, JSON.stringify(data), 'EX', ttlSeconds + 10);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.warn('[IDP_CONFIG] Failed to write to Redis cache:', error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Circuit Breaker & Backoff State
|
|
81
|
+
// ============================================================================
|
|
82
|
+
let consecutiveFailures = 0;
|
|
83
|
+
let lastFailureTime = 0;
|
|
84
|
+
const MAX_FAILURES = 3;
|
|
85
|
+
const CIRCUIT_OPEN_MS = 300000; // 5 minutes
|
|
86
|
+
const MAX_BACKOFF_MS = 30000; // 30 seconds max backoff
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Main Functions
|
|
89
|
+
// ============================================================================
|
|
90
|
+
/**
|
|
91
|
+
* Get IDP client configuration with multi-tier caching.
|
|
92
|
+
*
|
|
93
|
+
* Caching layers (checked in order):
|
|
94
|
+
* 1. In-memory cache (fastest, lost on module reload in dev)
|
|
95
|
+
* 2. Redis cache (survives module reloads)
|
|
96
|
+
* 3. IDP fetch (when both caches miss)
|
|
97
|
+
*
|
|
98
|
+
* THROWS if IDP is unavailable or misconfigured. No fallbacks.
|
|
99
|
+
*/
|
|
100
|
+
async function getIDPClientConfig(forceRefresh = false) {
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
// Layer 1: Return in-memory cached if still valid (skip if forceRefresh)
|
|
103
|
+
if (!forceRefresh && cachedConfig && now < cacheExpiry) {
|
|
104
|
+
return cachedConfig;
|
|
105
|
+
}
|
|
106
|
+
// If a fetch is already in progress, wait for it instead of starting another
|
|
107
|
+
if (pendingFetch) {
|
|
108
|
+
return pendingFetch;
|
|
109
|
+
}
|
|
110
|
+
// Layer 2: Check Redis cache (skip if forceRefresh - startup should always get fresh data)
|
|
111
|
+
if (!forceRefresh) {
|
|
112
|
+
const redisConfig = await getConfigFromRedis();
|
|
113
|
+
if (redisConfig) {
|
|
114
|
+
// Restore to in-memory cache
|
|
115
|
+
cachedConfig = redisConfig;
|
|
116
|
+
cacheExpiry = Date.now() + ((redisConfig.configCacheTtlSeconds || 300) * 1000);
|
|
117
|
+
// Set NEXTAUTH_SECRET from cached config
|
|
118
|
+
if (redisConfig.nextAuthSecret) {
|
|
119
|
+
process.env.NEXTAUTH_SECRET = redisConfig.nextAuthSecret;
|
|
120
|
+
}
|
|
121
|
+
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from cached config
|
|
122
|
+
// AUTH_TRUST_HOST=true tells NextAuth to derive OAuth callback URLs from headers.
|
|
123
|
+
if (redisConfig.baseClientUrl) {
|
|
124
|
+
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = redisConfig.baseClientUrl;
|
|
125
|
+
}
|
|
126
|
+
return redisConfig;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Layer 3: Fetch from IDP
|
|
130
|
+
const idpUrl = process.env.IDP_URL;
|
|
131
|
+
const clientIdStr = process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID;
|
|
132
|
+
if (!idpUrl) {
|
|
133
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP_URL must be set');
|
|
134
|
+
}
|
|
135
|
+
if (!clientIdStr) {
|
|
136
|
+
throw new Error('[IDP_CONFIG] FATAL: CLIENT_ID or NEXT_PUBLIC_CLIENT_ID must be set');
|
|
137
|
+
}
|
|
138
|
+
// Start fetch and store promise so concurrent callers wait for same result
|
|
139
|
+
pendingFetch = fetchConfigFromIDP(idpUrl, clientIdStr)
|
|
140
|
+
.then(async (config) => {
|
|
141
|
+
// Cache with TTL from response (default 5 minutes)
|
|
142
|
+
cachedConfig = config;
|
|
143
|
+
cacheExpiry = Date.now() + ((config.configCacheTtlSeconds || 300) * 1000);
|
|
144
|
+
// Store in Redis for persistence across module reloads
|
|
145
|
+
await setConfigInRedis(config);
|
|
146
|
+
// Set NEXTAUTH_SECRET from config
|
|
147
|
+
if (config.nextAuthSecret) {
|
|
148
|
+
process.env.NEXTAUTH_SECRET = config.nextAuthSecret;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP did not return nextAuthSecret');
|
|
152
|
+
}
|
|
153
|
+
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from config
|
|
154
|
+
// AUTH_TRUST_HOST=true tells NextAuth to derive OAuth callback URLs from headers.
|
|
155
|
+
if (config.baseClientUrl) {
|
|
156
|
+
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = config.baseClientUrl;
|
|
157
|
+
console.log("[IDP_CONFIG] Set IDENTITY_CLIENT_BASE_EXTERNAL_URL:", config.baseClientUrl);
|
|
158
|
+
}
|
|
159
|
+
return config;
|
|
160
|
+
})
|
|
161
|
+
.finally(() => {
|
|
162
|
+
pendingFetch = null; // Clear so next cache miss can fetch again
|
|
163
|
+
});
|
|
164
|
+
return pendingFetch;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Clear the config cache (useful for testing or forced refresh)
|
|
168
|
+
*/
|
|
169
|
+
function clearConfigCache() {
|
|
170
|
+
cachedConfig = null;
|
|
171
|
+
cacheExpiry = 0;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get enabled OAuth providers from config
|
|
175
|
+
*/
|
|
176
|
+
function getEnabledProviders(config) {
|
|
177
|
+
return config.oauthProviders?.filter(p => p.enabled) || [];
|
|
178
|
+
}
|
|
179
|
+
// ============================================================================
|
|
180
|
+
// Internal Functions
|
|
181
|
+
// ============================================================================
|
|
182
|
+
async function fetchConfigFromIDP(idpUrl, clientIdStr) {
|
|
183
|
+
// =========================================================================
|
|
184
|
+
// Circuit Breaker Check
|
|
185
|
+
// =========================================================================
|
|
186
|
+
if (consecutiveFailures >= MAX_FAILURES) {
|
|
187
|
+
const timeSinceFailure = Date.now() - lastFailureTime;
|
|
188
|
+
if (timeSinceFailure < CIRCUIT_OPEN_MS) {
|
|
189
|
+
// Circuit is open - return stale cache if available
|
|
190
|
+
if (cachedConfig) {
|
|
191
|
+
return cachedConfig;
|
|
192
|
+
}
|
|
193
|
+
throw new Error(`[IDP_CONFIG] Circuit breaker OPEN - no cached config available. Retry in ${Math.round((CIRCUIT_OPEN_MS - timeSinceFailure) / 1000)}s`);
|
|
194
|
+
}
|
|
195
|
+
// Half-open state: allow one request to test
|
|
196
|
+
consecutiveFailures = MAX_FAILURES - 1;
|
|
197
|
+
}
|
|
198
|
+
// =========================================================================
|
|
199
|
+
// Exponential Backoff Check
|
|
200
|
+
// =========================================================================
|
|
201
|
+
if (consecutiveFailures > 0) {
|
|
202
|
+
const backoffMs = Math.min(1000 * Math.pow(2, consecutiveFailures), MAX_BACKOFF_MS);
|
|
203
|
+
const timeSinceFailure = Date.now() - lastFailureTime;
|
|
204
|
+
if (timeSinceFailure < backoffMs) {
|
|
205
|
+
const remainingMs = backoffMs - timeSinceFailure;
|
|
206
|
+
// Return stale cache during backoff if available
|
|
207
|
+
if (cachedConfig) {
|
|
208
|
+
return cachedConfig;
|
|
209
|
+
}
|
|
210
|
+
throw new Error(`[IDP_CONFIG] In backoff period - retry in ${Math.round(remainingMs)}ms`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
// Step 1: Get signed client assertion from IDP
|
|
215
|
+
const signingUrl = `${idpUrl.replace(/\/$/, '')}/api/ExternalAuth/sign-client-assertion`;
|
|
216
|
+
const signingPayload = {
|
|
217
|
+
issuer: clientIdStr,
|
|
218
|
+
subject: clientIdStr,
|
|
219
|
+
audience: 'urn:payez:externalauth:clientconfig',
|
|
220
|
+
expires_in: 60
|
|
221
|
+
};
|
|
222
|
+
const signingResp = await fetch(signingUrl, {
|
|
223
|
+
method: 'POST',
|
|
224
|
+
headers: {
|
|
225
|
+
'Accept': 'application/json',
|
|
226
|
+
'Content-Type': 'application/json',
|
|
227
|
+
'X-Client-Id': clientIdStr,
|
|
228
|
+
'X-Correlation-Id': (0, crypto_1.randomUUID)().replace(/-/g, ''),
|
|
229
|
+
},
|
|
230
|
+
body: JSON.stringify(signingPayload),
|
|
231
|
+
cache: 'no-store'
|
|
232
|
+
});
|
|
233
|
+
if (!signingResp.ok) {
|
|
234
|
+
const txt = await signingResp.text().catch(() => 'Unknown error');
|
|
235
|
+
throw new Error(`[IDP_CONFIG] FATAL: Failed to sign client assertion: ${signingResp.status} - ${txt}`);
|
|
236
|
+
}
|
|
237
|
+
const signingBody = await signingResp.json().catch(() => null);
|
|
238
|
+
if (!signingBody) {
|
|
239
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP returned empty or invalid JSON for sign-client-assertion');
|
|
240
|
+
}
|
|
241
|
+
// Per PayEz API standard: response is { success, data: { client_assertion }, ... }
|
|
242
|
+
// But IDP might use camelCase (clientAssertion) - check both
|
|
243
|
+
const client_assertion = (signingBody?.data?.client_assertion ??
|
|
244
|
+
signingBody?.data?.clientAssertion);
|
|
245
|
+
if (!client_assertion) {
|
|
246
|
+
console.error('[IDP_CONFIG] FATAL: Full response body:', JSON.stringify(signingBody, null, 2));
|
|
247
|
+
throw new Error(`[IDP_CONFIG] FATAL: IDP response missing client_assertion. Got keys: ${JSON.stringify(Object.keys(signingBody?.data || signingBody || {}))}`);
|
|
248
|
+
}
|
|
249
|
+
// Step 2: Fetch client config using the assertion
|
|
250
|
+
const configUrl = `${idpUrl.replace(/\/$/, '')}/api/ExternalAuth/client-config`;
|
|
251
|
+
const configResp = await fetch(configUrl, {
|
|
252
|
+
method: 'POST',
|
|
253
|
+
headers: {
|
|
254
|
+
'Accept': 'application/json',
|
|
255
|
+
'Content-Type': 'application/json',
|
|
256
|
+
'X-Client-Id': clientIdStr,
|
|
257
|
+
'X-Correlation-Id': (0, crypto_1.randomUUID)().replace(/-/g, ''),
|
|
258
|
+
},
|
|
259
|
+
body: JSON.stringify({ client_assertion }),
|
|
260
|
+
cache: 'no-store'
|
|
261
|
+
});
|
|
262
|
+
if (!configResp.ok) {
|
|
263
|
+
const txt = await configResp.text().catch(() => 'Unknown error');
|
|
264
|
+
throw new Error(`[IDP_CONFIG] FATAL: Failed to fetch client config: ${configResp.status} - ${txt}`);
|
|
265
|
+
}
|
|
266
|
+
const configBody = await configResp.json().catch(() => null);
|
|
267
|
+
if (!configBody) {
|
|
268
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP returned empty or invalid JSON for client-config');
|
|
269
|
+
}
|
|
270
|
+
// Per PayEz API standard: response is wrapped in { success, data: {...} }
|
|
271
|
+
const configData = configBody?.data;
|
|
272
|
+
if (!configData || typeof configData !== 'object') {
|
|
273
|
+
console.error('[IDP_CONFIG] FATAL: Full config response body:', JSON.stringify(configBody, null, 2));
|
|
274
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP client-config response missing data envelope');
|
|
275
|
+
}
|
|
276
|
+
// Validate required fields - handle both number and string client_id
|
|
277
|
+
const rawClientId = configData.clientId ?? configData.client_id;
|
|
278
|
+
if (rawClientId === undefined || rawClientId === null) {
|
|
279
|
+
throw new Error(`[IDP_CONFIG] FATAL: IDP response missing clientId/client_id. Got: ${JSON.stringify(Object.keys(configData))}`);
|
|
280
|
+
}
|
|
281
|
+
// Map response to our interface (IDP always returns snake_case)
|
|
282
|
+
const config = {
|
|
283
|
+
clientId: typeof rawClientId === 'string' ? parseInt(rawClientId, 10) : rawClientId,
|
|
284
|
+
clientSlug: configData.clientSlug ?? configData.client_slug ?? configData.slug ?? '',
|
|
285
|
+
nextAuthSecret: configData.nextAuthSecret ?? configData.next_auth_secret ?? '',
|
|
286
|
+
configCacheTtlSeconds: configData.configCacheTtlSeconds ?? configData.config_cache_ttl_seconds ?? 300,
|
|
287
|
+
oauthProviders: (configData.oauthProviders ?? configData.oauth_providers ?? []).map((p) => ({
|
|
288
|
+
provider: p.provider ?? '',
|
|
289
|
+
enabled: p.enabled ?? false,
|
|
290
|
+
clientId: p.clientId ?? p.client_id ?? '',
|
|
291
|
+
clientSecret: p.clientSecret ?? p.client_secret ?? '',
|
|
292
|
+
scopes: p.scopes,
|
|
293
|
+
additionalParams: p.additionalParams ?? p.additional_params
|
|
294
|
+
})),
|
|
295
|
+
authSettings: {
|
|
296
|
+
require2FA: (() => {
|
|
297
|
+
// Check nested locations first (canonical)
|
|
298
|
+
const nested = configData.authSettings?.require2FA ?? configData.auth_settings?.require_2fa;
|
|
299
|
+
if (nested !== undefined)
|
|
300
|
+
return nested;
|
|
301
|
+
// TRANSITION FALLBACK: Check top-level (deprecated)
|
|
302
|
+
const topLevel = configData.require2FA ?? configData.require_2fa;
|
|
303
|
+
if (topLevel !== undefined) {
|
|
304
|
+
console.warn('[IDP_CONFIG] DEPRECATION: require2FA found at top-level. Should be nested under auth_settings. Update IDP.');
|
|
305
|
+
return topLevel;
|
|
306
|
+
}
|
|
307
|
+
return true; // Default to true for security
|
|
308
|
+
})(),
|
|
309
|
+
allowed2FAMethods: configData.authSettings?.allowed2FAMethods ?? configData.auth_settings?.allowed_2fa_methods ?? ['email', 'sms'],
|
|
310
|
+
mfaGracePeriodHours: configData.authSettings?.mfaGracePeriodHours ?? configData.auth_settings?.mfa_grace_period_hours ?? 24,
|
|
311
|
+
mfaRememberDeviceDays: configData.authSettings?.mfaRememberDeviceDays ?? configData.auth_settings?.mfa_remember_device_days ?? 30,
|
|
312
|
+
sessionTimeoutMinutes: configData.authSettings?.sessionTimeoutMinutes ?? configData.auth_settings?.session_timeout_minutes ?? 60,
|
|
313
|
+
idleTimeoutMinutes: configData.authSettings?.idleTimeoutMinutes ?? configData.auth_settings?.idle_timeout_minutes ?? 15,
|
|
314
|
+
allowRememberMe: configData.authSettings?.allowRememberMe ?? configData.auth_settings?.allow_remember_me ?? true,
|
|
315
|
+
rememberMeDays: configData.authSettings?.rememberMeDays ?? configData.auth_settings?.remember_me_days ?? 30,
|
|
316
|
+
lockoutThreshold: configData.authSettings?.lockoutThreshold ?? configData.auth_settings?.lockout_threshold ?? 5,
|
|
317
|
+
lockoutDurationMinutes: configData.authSettings?.lockoutDurationMinutes ?? configData.auth_settings?.lockout_duration_minutes ?? 15
|
|
318
|
+
},
|
|
319
|
+
branding: {
|
|
320
|
+
theme: configData.branding?.theme,
|
|
321
|
+
primaryColor: configData.branding?.primaryColor ?? configData.branding?.primary_color,
|
|
322
|
+
secondaryColor: configData.branding?.secondaryColor ?? configData.branding?.secondary_color,
|
|
323
|
+
logoUrl: configData.branding?.logoUrl ?? configData.branding?.logo_url
|
|
324
|
+
},
|
|
325
|
+
baseClientUrl: configData.baseClientUrl ?? configData.base_client_url ?? configData.BaseClientUrl
|
|
326
|
+
};
|
|
327
|
+
// Debug: log what we got for baseClientUrl
|
|
328
|
+
console.log(`[IDP_CONFIG] Parsed baseClientUrl:`, config.baseClientUrl, `| raw keys:`, Object.keys(configData).filter(k => k.toLowerCase().includes('client')));
|
|
329
|
+
// Validate we got what we need
|
|
330
|
+
if (!config.clientId) {
|
|
331
|
+
throw new Error('[IDP_CONFIG] FATAL: clientId is 0 or missing after parsing');
|
|
332
|
+
}
|
|
333
|
+
if (!config.nextAuthSecret) {
|
|
334
|
+
throw new Error('[IDP_CONFIG] FATAL: nextAuthSecret is empty after parsing');
|
|
335
|
+
}
|
|
336
|
+
// Success - reset failure tracking
|
|
337
|
+
consecutiveFailures = 0;
|
|
338
|
+
return config;
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
// Track failure for circuit breaker
|
|
342
|
+
consecutiveFailures++;
|
|
343
|
+
lastFailureTime = Date.now();
|
|
344
|
+
console.error('[IDP_CONFIG] Fetch failed', {
|
|
345
|
+
consecutiveFailures,
|
|
346
|
+
maxFailures: MAX_FAILURES,
|
|
347
|
+
error: error instanceof Error ? error.message : String(error)
|
|
348
|
+
});
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
/**
|
|
3
|
+
* Centralized IDP fetch helper
|
|
4
|
+
* - Injects Bearer from Redis session
|
|
5
|
+
* - If access token is expired/near-expiry, triggers one refresh and retries fetch once
|
|
6
|
+
* - Returns parsed JSON and HTTP status
|
|
7
|
+
*/
|
|
8
|
+
export interface IdpFetchResult<T = any> {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
json: T | null;
|
|
12
|
+
attemptedRefresh: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function idpFetchJSON<T = any>(req: NextRequest, targetUrl: string, init?: RequestInit): Promise<IdpFetchResult<T>>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.idpFetchJSON = idpFetchJSON;
|
|
4
|
+
const test_aware_get_token_1 = require("./test-aware-get-token");
|
|
5
|
+
const session_store_1 = require("./session-store");
|
|
6
|
+
const internal_api_1 = require("./internal-api");
|
|
7
|
+
function buildHeaders(req, bearer, extra) {
|
|
8
|
+
const headers = {
|
|
9
|
+
Accept: 'application/json',
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
...extra,
|
|
12
|
+
};
|
|
13
|
+
const xfwd = req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip');
|
|
14
|
+
if (xfwd)
|
|
15
|
+
headers['X-Forwarded-For'] = xfwd.split(',')[0].trim();
|
|
16
|
+
const ua = req.headers.get('user-agent');
|
|
17
|
+
if (ua)
|
|
18
|
+
headers['User-Agent'] = ua;
|
|
19
|
+
if (bearer)
|
|
20
|
+
headers['Authorization'] = `Bearer ${bearer}`;
|
|
21
|
+
return headers;
|
|
22
|
+
}
|
|
23
|
+
async function ensureFreshAccessToken(req) {
|
|
24
|
+
const token = await (0, test_aware_get_token_1.getTokenTestAware)(req);
|
|
25
|
+
// Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
|
|
26
|
+
const sessionToken = (token?.sessionToken || token?.redisSessionId);
|
|
27
|
+
console.log('[IDP_FETCH] ensureFreshAccessToken:', {
|
|
28
|
+
hasToken: !!token,
|
|
29
|
+
sessionToken: sessionToken?.substring(0, 20) + '...',
|
|
30
|
+
pathname: req.nextUrl.pathname
|
|
31
|
+
});
|
|
32
|
+
if (!sessionToken) {
|
|
33
|
+
console.warn('[IDP_FETCH] No sessionToken found in JWT - Bearer token will NOT be sent!');
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
let session = await (0, session_store_1.getSession)(sessionToken);
|
|
37
|
+
console.log('[IDP_FETCH] Redis session lookup:', {
|
|
38
|
+
hasSession: !!session,
|
|
39
|
+
hasAccessToken: !!session?.idpAccessToken,
|
|
40
|
+
bearerKeyId: session?.bearerKeyId || 'NOT_SET',
|
|
41
|
+
idpClientId: session?.idpClientId || 'NOT_SET',
|
|
42
|
+
});
|
|
43
|
+
if (!session?.idpAccessToken)
|
|
44
|
+
return { sessionToken };
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const timeLeft = (session.idpAccessTokenExpires ?? now) - now;
|
|
47
|
+
// Treat tokens as effectively expired if within 5 minutes of expiry
|
|
48
|
+
if (timeLeft > 5 * 60 * 1000) {
|
|
49
|
+
return { sessionToken, accessToken: session.idpAccessToken };
|
|
50
|
+
}
|
|
51
|
+
// attempt refresh once via centralized internal API helper
|
|
52
|
+
try {
|
|
53
|
+
await (0, internal_api_1.internalRefresh)(req.headers.get('cookie') || '', sessionToken);
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
session = await (0, session_store_1.getSession)(sessionToken);
|
|
57
|
+
return { sessionToken, accessToken: session?.idpAccessToken };
|
|
58
|
+
}
|
|
59
|
+
async function idpFetchJSON(req, targetUrl, init = {}) {
|
|
60
|
+
let attemptedRefresh = false;
|
|
61
|
+
let { accessToken, sessionToken } = await ensureFreshAccessToken(req);
|
|
62
|
+
const makeCall = async (bearer) => {
|
|
63
|
+
const res = await fetch(targetUrl, {
|
|
64
|
+
...init,
|
|
65
|
+
headers: buildHeaders(req, bearer, init.headers),
|
|
66
|
+
});
|
|
67
|
+
let json = null;
|
|
68
|
+
try {
|
|
69
|
+
json = await res.json();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
json = null;
|
|
73
|
+
}
|
|
74
|
+
return { res, json };
|
|
75
|
+
};
|
|
76
|
+
// First attempt
|
|
77
|
+
let { res, json } = await makeCall(accessToken);
|
|
78
|
+
if ((res.status === 401 || res.status === 403) && sessionToken && !attemptedRefresh) {
|
|
79
|
+
attemptedRefresh = true;
|
|
80
|
+
try {
|
|
81
|
+
// Use centralized internal API helper for server-to-server calls
|
|
82
|
+
const rf = await (0, internal_api_1.internalRefresh)(req.headers.get('cookie') || '', sessionToken);
|
|
83
|
+
if (rf.ok) {
|
|
84
|
+
const fresh = await (0, session_store_1.getSession)(sessionToken);
|
|
85
|
+
({ res, json } = await makeCall(fresh?.idpAccessToken));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
}
|
|
90
|
+
return { ok: res.ok, status: res.status, json: json, attemptedRefresh };
|
|
91
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized internal API helper for the app to call ITSELF.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: All calls from the Next.js server to its own API routes MUST use
|
|
5
|
+
* these functions. Never use req.url, req.nextUrl.origin, or construct URLs
|
|
6
|
+
* from the incoming request.
|
|
7
|
+
*
|
|
8
|
+
* WHY HTTP IS REQUIRED (not optional):
|
|
9
|
+
* - This is the app calling its OWN backend within the same pod/container
|
|
10
|
+
* - NextAuth cookies are encrypted based on request protocol
|
|
11
|
+
* - TLS is terminated at ingress, so the pod receives HTTP internally
|
|
12
|
+
* - Using HTTPS here causes cookie decryption failures and 403 errors
|
|
13
|
+
* - This is NOT about "K8s traffic doesn't need TLS" - it's about
|
|
14
|
+
* protocol consistency for cookie/session encryption
|
|
15
|
+
*
|
|
16
|
+
* Environment:
|
|
17
|
+
* - INTERNAL_API_URL: Required in production (e.g., http://service.namespace.svc.cluster.local:80)
|
|
18
|
+
* - Falls back to http://localhost:3200 in development only
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Get the internal API base URL for the app to call itself.
|
|
22
|
+
*
|
|
23
|
+
* @throws Error in production if INTERNAL_API_URL is not set
|
|
24
|
+
* @returns The base URL (no trailing slash)
|
|
25
|
+
*/
|
|
26
|
+
export declare function getInternalApiUrl(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Options for internal fetch calls
|
|
29
|
+
*/
|
|
30
|
+
export interface InternalFetchOptions extends Omit<RequestInit, 'headers'> {
|
|
31
|
+
/** Additional headers to include */
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
/** Cookie header to forward (typically from req.headers.get('cookie')) */
|
|
34
|
+
cookie?: string;
|
|
35
|
+
/** Session token for x-session-token header */
|
|
36
|
+
sessionToken?: string;
|
|
37
|
+
/** Request ID for tracing */
|
|
38
|
+
requestId?: string;
|
|
39
|
+
/** Parse response as JSON (default: true) */
|
|
40
|
+
parseJson?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of an internal fetch call
|
|
44
|
+
*/
|
|
45
|
+
export interface InternalFetchResult<T = unknown> {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
status: number;
|
|
48
|
+
statusText: string;
|
|
49
|
+
data: T | null;
|
|
50
|
+
response: Response;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Make a fetch call to an internal API route (app calling itself).
|
|
54
|
+
*
|
|
55
|
+
* @param path - The API path (e.g., '/api/auth/refresh')
|
|
56
|
+
* @param options - Fetch options
|
|
57
|
+
* @returns The fetch result with parsed data
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* // Simple GET
|
|
62
|
+
* const result = await internalFetch('/api/health');
|
|
63
|
+
*
|
|
64
|
+
* // POST with session
|
|
65
|
+
* const result = await internalFetch('/api/auth/refresh', {
|
|
66
|
+
* method: 'POST',
|
|
67
|
+
* cookie: req.headers.get('cookie') || '',
|
|
68
|
+
* sessionToken: token.redisSessionId,
|
|
69
|
+
* body: JSON.stringify({ refresh_token: refreshToken }),
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function internalFetch<T = unknown>(path: string, options?: InternalFetchOptions): Promise<InternalFetchResult<T>>;
|
|
74
|
+
/**
|
|
75
|
+
* Trigger a token refresh via the internal API.
|
|
76
|
+
* This is a convenience wrapper for the common refresh pattern.
|
|
77
|
+
*
|
|
78
|
+
* @param cookie - The cookie header from the incoming request
|
|
79
|
+
* @param sessionToken - The session token
|
|
80
|
+
* @param refreshToken - Optional refresh token to include in body
|
|
81
|
+
* @param requestId - Optional request ID for tracing
|
|
82
|
+
* @returns Whether the refresh was successful
|
|
83
|
+
*/
|
|
84
|
+
export declare function internalRefresh(cookie: string, sessionToken: string, refreshToken?: string, requestId?: string): Promise<{
|
|
85
|
+
ok: boolean;
|
|
86
|
+
status: number;
|
|
87
|
+
}>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized internal API helper for the app to call ITSELF.
|
|
4
|
+
*
|
|
5
|
+
* IMPORTANT: All calls from the Next.js server to its own API routes MUST use
|
|
6
|
+
* these functions. Never use req.url, req.nextUrl.origin, or construct URLs
|
|
7
|
+
* from the incoming request.
|
|
8
|
+
*
|
|
9
|
+
* WHY HTTP IS REQUIRED (not optional):
|
|
10
|
+
* - This is the app calling its OWN backend within the same pod/container
|
|
11
|
+
* - NextAuth cookies are encrypted based on request protocol
|
|
12
|
+
* - TLS is terminated at ingress, so the pod receives HTTP internally
|
|
13
|
+
* - Using HTTPS here causes cookie decryption failures and 403 errors
|
|
14
|
+
* - This is NOT about "K8s traffic doesn't need TLS" - it's about
|
|
15
|
+
* protocol consistency for cookie/session encryption
|
|
16
|
+
*
|
|
17
|
+
* Environment:
|
|
18
|
+
* - INTERNAL_API_URL: Required in production (e.g., http://service.namespace.svc.cluster.local:80)
|
|
19
|
+
* - Falls back to http://localhost:3200 in development only
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.getInternalApiUrl = getInternalApiUrl;
|
|
23
|
+
exports.internalFetch = internalFetch;
|
|
24
|
+
exports.internalRefresh = internalRefresh;
|
|
25
|
+
/**
|
|
26
|
+
* Get the internal API base URL for the app to call itself.
|
|
27
|
+
*
|
|
28
|
+
* @throws Error in production if INTERNAL_API_URL is not set
|
|
29
|
+
* @returns The base URL (no trailing slash)
|
|
30
|
+
*/
|
|
31
|
+
function getInternalApiUrl() {
|
|
32
|
+
const url = process.env.INTERNAL_API_URL;
|
|
33
|
+
if (url)
|
|
34
|
+
return url.replace(/\/$/, ''); // strip trailing slash
|
|
35
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
36
|
+
return 'http://localhost:3200';
|
|
37
|
+
}
|
|
38
|
+
throw new Error('[INTERNAL_API_URL] FATAL: INTERNAL_API_URL environment variable is REQUIRED in production. ' +
|
|
39
|
+
'This is for the app to call ITSELF. MUST be HTTP (not HTTPS) due to cookie encryption. ' +
|
|
40
|
+
'Set to http://service.namespace.svc.cluster.local:80');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Make a fetch call to an internal API route (app calling itself).
|
|
44
|
+
*
|
|
45
|
+
* @param path - The API path (e.g., '/api/auth/refresh')
|
|
46
|
+
* @param options - Fetch options
|
|
47
|
+
* @returns The fetch result with parsed data
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // Simple GET
|
|
52
|
+
* const result = await internalFetch('/api/health');
|
|
53
|
+
*
|
|
54
|
+
* // POST with session
|
|
55
|
+
* const result = await internalFetch('/api/auth/refresh', {
|
|
56
|
+
* method: 'POST',
|
|
57
|
+
* cookie: req.headers.get('cookie') || '',
|
|
58
|
+
* sessionToken: token.redisSessionId,
|
|
59
|
+
* body: JSON.stringify({ refresh_token: refreshToken }),
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
async function internalFetch(path, options = {}) {
|
|
64
|
+
const { headers: extraHeaders = {}, cookie, sessionToken, requestId, parseJson = true, ...fetchOptions } = options;
|
|
65
|
+
const baseUrl = getInternalApiUrl();
|
|
66
|
+
const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
|
67
|
+
// Build headers
|
|
68
|
+
const headers = {
|
|
69
|
+
'Accept': 'application/json',
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
...extraHeaders,
|
|
72
|
+
};
|
|
73
|
+
if (cookie) {
|
|
74
|
+
headers['Cookie'] = cookie;
|
|
75
|
+
}
|
|
76
|
+
if (sessionToken) {
|
|
77
|
+
headers['X-Session-Token'] = sessionToken;
|
|
78
|
+
}
|
|
79
|
+
if (requestId) {
|
|
80
|
+
headers['X-Request-Id'] = requestId;
|
|
81
|
+
}
|
|
82
|
+
const response = await fetch(url, {
|
|
83
|
+
...fetchOptions,
|
|
84
|
+
headers,
|
|
85
|
+
});
|
|
86
|
+
let data = null;
|
|
87
|
+
if (parseJson) {
|
|
88
|
+
try {
|
|
89
|
+
data = await response.json();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
data = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
ok: response.ok,
|
|
97
|
+
status: response.status,
|
|
98
|
+
statusText: response.statusText,
|
|
99
|
+
data,
|
|
100
|
+
response,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Trigger a token refresh via the internal API.
|
|
105
|
+
* This is a convenience wrapper for the common refresh pattern.
|
|
106
|
+
*
|
|
107
|
+
* @param cookie - The cookie header from the incoming request
|
|
108
|
+
* @param sessionToken - The session token
|
|
109
|
+
* @param refreshToken - Optional refresh token to include in body
|
|
110
|
+
* @param requestId - Optional request ID for tracing
|
|
111
|
+
* @returns Whether the refresh was successful
|
|
112
|
+
*/
|
|
113
|
+
async function internalRefresh(cookie, sessionToken, refreshToken, requestId) {
|
|
114
|
+
const result = await internalFetch('/api/auth/refresh', {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
cookie,
|
|
117
|
+
sessionToken,
|
|
118
|
+
requestId,
|
|
119
|
+
body: refreshToken ? JSON.stringify({ refresh_token: refreshToken }) : undefined,
|
|
120
|
+
});
|
|
121
|
+
return { ok: result.ok, status: result.status };
|
|
122
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-safe JWT decode (no Node.js dependencies)
|
|
3
|
+
* This is a lightweight version for browser usage
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Simple JWT decode for client-side use (no signature verification)
|
|
7
|
+
* @param token - JWT token string
|
|
8
|
+
* @returns Decoded payload or null if invalid
|
|
9
|
+
*/
|
|
10
|
+
export declare function jwtDecode<T = any>(token: string): T | null;
|