@nextsparkjs/core 0.1.0-beta.185 → 0.1.0-beta.187
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/dist/components/app/guards/DeveloperGuard.d.ts.map +1 -1
- package/dist/components/app/guards/DeveloperGuard.js +8 -4
- package/dist/components/app/guards/SuperAdminGuard.d.ts.map +1 -1
- package/dist/components/app/guards/SuperAdminGuard.js +8 -4
- package/dist/components/app/layouts/PublicFooter.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicFooter.js +2 -2
- package/dist/components/app/layouts/PublicNavbar.d.ts.map +1 -1
- package/dist/components/app/layouts/PublicNavbar.js +2 -2
- package/dist/components/app/misc/ThemeToggle.d.ts.map +1 -1
- package/dist/components/app/misc/ThemeToggle.js +4 -1
- package/dist/components/auth/DevKeyring.d.ts.map +1 -1
- package/dist/components/auth/DevKeyring.js +10 -2
- package/dist/components/auth/SessionCookieRefresher.d.ts +21 -0
- package/dist/components/auth/SessionCookieRefresher.d.ts.map +1 -0
- package/dist/components/auth/SessionCookieRefresher.js +11 -0
- package/dist/components/auth/forms/LoginForm.d.ts.map +1 -1
- package/dist/components/auth/forms/LoginForm.js +300 -6
- package/dist/components/auth/index.d.ts +1 -0
- package/dist/components/auth/index.d.ts.map +1 -1
- package/dist/components/auth/index.js +2 -0
- package/dist/components/billing/InvoicesTable.d.ts.map +1 -1
- package/dist/components/billing/InvoicesTable.js +4 -3
- package/dist/components/dashboard/block-editor/dynamic-form.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthGuard.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/AuthGuard.js +2 -2
- package/dist/components/dashboard/layouts/DashboardAuthLayout.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/DashboardAuthLayout.js +2 -2
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts +17 -0
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.d.ts.map +1 -0
- package/dist/components/dashboard/layouts/DashboardAuthSkeleton.js +52 -0
- package/dist/components/dashboard/layouts/Sidebar.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/Sidebar.js +2 -3
- package/dist/components/dashboard/layouts/TopNavbar.js +2 -2
- package/dist/components/dashboard/layouts/index.d.ts +1 -0
- package/dist/components/dashboard/layouts/index.d.ts.map +1 -1
- package/dist/components/dashboard/layouts/index.js +2 -0
- package/dist/components/devtools/api-explorer/ApiExplorer.js +4 -4
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +3 -0
- package/dist/components/entities/EntityTable.d.ts.map +1 -1
- package/dist/components/entities/EntityTable.js +2 -1
- package/dist/components/entities/wrappers/EntityFormWrapper.d.ts.map +1 -1
- package/dist/components/entities/wrappers/EntityFormWrapper.js +7 -2
- package/dist/components/patterns/pages/PatternEditPage.d.ts +5 -1
- package/dist/components/patterns/pages/PatternEditPage.d.ts.map +1 -1
- package/dist/components/patterns/pages/PatternEditPage.js +16 -5
- package/dist/components/patterns/pages/PatternsCreatePage.d.ts +2 -1
- package/dist/components/patterns/pages/PatternsCreatePage.d.ts.map +1 -1
- package/dist/components/patterns/pages/PatternsCreatePage.js +17 -5
- package/dist/components/ui/combobox.d.ts +8 -1
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/combobox.js +37 -44
- package/dist/components/ui/country-select.d.ts +3 -1
- package/dist/components/ui/country-select.d.ts.map +1 -1
- package/dist/components/ui/country-select.js +3 -1
- package/dist/components/ui/currency-select.d.ts +3 -1
- package/dist/components/ui/currency-select.d.ts.map +1 -1
- package/dist/components/ui/currency-select.js +3 -1
- package/dist/components/ui/sheet.d.ts +5 -0
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js +46 -26
- package/dist/components/ui/timezone-select.d.ts +3 -1
- package/dist/components/ui/timezone-select.d.ts.map +1 -1
- package/dist/components/ui/timezone-select.js +3 -1
- package/dist/contexts/TeamContext.d.ts +6 -0
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +8 -1
- package/dist/emails/otp-verification.d.ts.map +1 -1
- package/dist/emails/reset-password.d.ts.map +1 -1
- package/dist/emails/team-invitation.d.ts.map +1 -1
- package/dist/emails/team-invitation.js +3 -2
- package/dist/emails/verify-email.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAuth.d.ts +29 -0
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +24 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts +22 -0
- package/dist/hooks/useEnsureUserMetadata.d.ts.map +1 -1
- package/dist/hooks/useEnsureUserMetadata.js +18 -5
- package/dist/hooks/useSessionCookieRefresh.d.ts +35 -0
- package/dist/hooks/useSessionCookieRefresh.d.ts.map +1 -0
- package/dist/hooks/useSessionCookieRefresh.js +49 -0
- package/dist/lib/api/api-error.d.ts +3 -1
- package/dist/lib/api/api-error.d.ts.map +1 -1
- package/dist/lib/api/api-error.js +4 -2
- package/dist/lib/api/auth/dual-auth.d.ts +38 -3
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +42 -7
- package/dist/lib/api/auth/permissions.d.ts +7 -2
- package/dist/lib/api/auth/permissions.d.ts.map +1 -1
- package/dist/lib/api/auth/permissions.js +2 -2
- package/dist/lib/api/auth/scope-policy.d.ts +71 -0
- package/dist/lib/api/auth/scope-policy.d.ts.map +1 -0
- package/dist/lib/api/auth/scope-policy.js +53 -0
- package/dist/lib/api/auth.d.ts +15 -0
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +17 -4
- package/dist/lib/api/entities.js +2 -1
- package/dist/lib/api/entity/audit-log.d.ts +35 -0
- package/dist/lib/api/entity/audit-log.d.ts.map +1 -0
- package/dist/lib/api/entity/audit-log.js +35 -0
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +333 -138
- package/dist/lib/api/entity-handler.js +2 -2
- package/dist/lib/api/helpers.d.ts +29 -4
- package/dist/lib/api/helpers.d.ts.map +1 -1
- package/dist/lib/api/helpers.js +30 -6
- package/dist/lib/api/keys.d.ts +16 -0
- package/dist/lib/api/keys.d.ts.map +1 -1
- package/dist/lib/api/keys.js +19 -1
- package/dist/lib/api/routes/patterns-usages.d.ts.map +1 -1
- package/dist/lib/api/routes/patterns-usages.js +3 -11
- package/dist/lib/auth/auth-methods.d.ts +52 -0
- package/dist/lib/auth/auth-methods.d.ts.map +1 -0
- package/dist/lib/auth/auth-methods.js +63 -0
- package/dist/lib/auth/registration-guard-plugin.d.ts.map +1 -1
- package/dist/lib/auth/registration-helpers.d.ts +9 -0
- package/dist/lib/auth/registration-helpers.d.ts.map +1 -1
- package/dist/lib/auth/registration-helpers.js +8 -2
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts +52 -0
- package/dist/lib/auth/security-notifications/device-fingerprint.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/device-fingerprint.js +42 -0
- package/dist/lib/auth/security-notifications/dispatcher.d.ts +37 -0
- package/dist/lib/auth/security-notifications/dispatcher.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/dispatcher.js +157 -0
- package/dist/lib/auth/security-notifications/index.d.ts +20 -0
- package/dist/lib/auth/security-notifications/index.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/index.js +22 -0
- package/dist/lib/auth/security-notifications/registry.d.ts +36 -0
- package/dist/lib/auth/security-notifications/registry.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/registry.js +26 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.d.ts +12 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/email-changed.js +43 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts +7 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/new-device-login.js +46 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.d.ts +7 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/password-changed.js +35 -0
- package/dist/lib/auth/security-notifications/templates/shared.d.ts +36 -0
- package/dist/lib/auth/security-notifications/templates/shared.d.ts.map +1 -0
- package/dist/lib/auth/security-notifications/templates/shared.js +68 -0
- package/dist/lib/auth/security-notifications/types.d.ts +74 -0
- package/dist/lib/auth/security-notifications/types.d.ts.map +1 -0
- package/dist/lib/auth/session-config.d.ts +37 -0
- package/dist/lib/auth/session-config.d.ts.map +1 -0
- package/dist/lib/auth/session-config.js +59 -0
- package/dist/lib/auth/session-refresh.d.ts +46 -0
- package/dist/lib/auth/session-refresh.d.ts.map +1 -0
- package/dist/lib/auth/session-refresh.js +11 -0
- package/dist/lib/auth-client.d.ts +1639 -0
- package/dist/lib/auth-client.d.ts.map +1 -0
- package/dist/lib/auth-client.js +13 -1
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +45 -11
- package/dist/lib/billing/gateways/factory.d.ts +8 -3
- package/dist/lib/billing/gateways/factory.d.ts.map +1 -1
- package/dist/lib/billing/gateways/factory.js +37 -23
- package/dist/lib/billing/stripe-webhook.d.ts.map +1 -1
- package/dist/lib/billing/stripe-webhook.js +2 -1
- package/dist/lib/config/app.config.d.ts.map +1 -1
- package/dist/lib/config/app.config.js +71 -0
- package/dist/lib/config/config-sync.d.ts +1 -4
- package/dist/lib/config/config-sync.d.ts.map +1 -1
- package/dist/lib/config/config-sync.js +6 -2
- package/dist/lib/config/types.d.ts +130 -0
- package/dist/lib/config/types.d.ts.map +1 -1
- package/dist/lib/email/templates.d.ts +3 -3
- package/dist/lib/email/templates.d.ts.map +1 -1
- package/dist/lib/entities/api-generator.js +5 -5
- package/dist/lib/entities/entity-hooks.d.ts +9 -1
- package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
- package/dist/lib/entities/entity-hooks.js +77 -4
- package/dist/lib/entities/schema-generator.d.ts.map +1 -1
- package/dist/lib/entities/schema-generator.js +20 -4
- package/dist/lib/entities/serialization.d.ts +2 -2
- package/dist/lib/entities/serialization.d.ts.map +1 -1
- package/dist/lib/entities/serialization.js +3 -2
- package/dist/lib/entities/types.d.ts +20 -0
- package/dist/lib/entities/types.d.ts.map +1 -1
- package/dist/lib/locale.d.ts.map +1 -1
- package/dist/lib/locale.js +4 -1
- package/dist/lib/mcp/audit.d.ts +8 -4
- package/dist/lib/mcp/audit.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.d.ts +13 -0
- package/dist/lib/plugins/hook-system.d.ts.map +1 -1
- package/dist/lib/plugins/hook-system.js +32 -2
- package/dist/lib/scheduled-actions/handlers/index.d.ts +1 -0
- package/dist/lib/scheduled-actions/handlers/index.d.ts.map +1 -1
- package/dist/lib/scheduled-actions/handlers/index.js +3 -1
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts +26 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.d.ts.map +1 -0
- package/dist/lib/scheduled-actions/handlers/security-notification.js +54 -0
- package/dist/lib/selectors/core-selectors.d.ts +12 -0
- package/dist/lib/selectors/core-selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.d.ts +12 -0
- package/dist/lib/selectors/domains/auth.selectors.d.ts.map +1 -1
- package/dist/lib/selectors/domains/auth.selectors.js +14 -1
- package/dist/lib/selectors/selectors.d.ts +24 -0
- package/dist/lib/selectors/selectors.d.ts.map +1 -1
- package/dist/lib/services/api-docs.service.d.ts +5 -1
- package/dist/lib/services/api-docs.service.d.ts.map +1 -1
- package/dist/lib/services/api-presets.service.d.ts +6 -1
- package/dist/lib/services/api-presets.service.d.ts.map +1 -1
- package/dist/lib/services/feature.service.d.ts +58 -4
- package/dist/lib/services/feature.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
- package/dist/lib/services/generic-entity.service.js +16 -32
- package/dist/lib/services/membership.service.d.ts +10 -2
- package/dist/lib/services/membership.service.d.ts.map +1 -1
- package/dist/lib/services/membership.service.js +37 -16
- package/dist/lib/services/permission.service.d.ts +10 -4
- package/dist/lib/services/permission.service.d.ts.map +1 -1
- package/dist/lib/services/plugin.service.d.ts +1 -1
- package/dist/lib/services/plugin.service.d.ts.map +1 -1
- package/dist/lib/services/subscription.service.d.ts.map +1 -1
- package/dist/lib/services/subscription.service.js +2 -1
- package/dist/lib/services/team-member.service.d.ts +15 -3
- package/dist/lib/services/team-member.service.d.ts.map +1 -1
- package/dist/lib/services/team-member.service.js +27 -6
- package/dist/lib/services/team.service.d.ts.map +1 -1
- package/dist/lib/teams/permissions.d.ts +3 -1
- package/dist/lib/teams/permissions.d.ts.map +1 -1
- package/dist/lib/teams/permissions.js +6 -0
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +3 -0
- package/dist/lib/template-resolver.d.ts +3 -3
- package/dist/lib/theme/forced-theme.d.ts +34 -0
- package/dist/lib/theme/forced-theme.d.ts.map +1 -0
- package/dist/lib/theme/forced-theme.js +27 -0
- package/dist/lib/theme/get-default-theme-mode.d.ts +3 -0
- package/dist/lib/theme/get-default-theme-mode.d.ts.map +1 -1
- package/dist/lib/theme/get-default-theme-mode.js +5 -3
- package/dist/lib/utils/cors.d.ts +13 -0
- package/dist/lib/utils/cors.d.ts.map +1 -1
- package/dist/lib/utils/cors.js +20 -0
- package/dist/messages/de/auth.json +25 -2
- package/dist/messages/de/index.d.ts +83 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/index.js +2 -0
- package/dist/messages/de/index.ts +2 -0
- package/dist/messages/de/navigation.json +3 -1
- package/dist/messages/en/auth.json +25 -2
- package/dist/messages/en/index.d.ts +83 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/index.js +2 -0
- package/dist/messages/en/index.ts +2 -0
- package/dist/messages/en/navigation.json +3 -1
- package/dist/messages/es/auth.json +25 -2
- package/dist/messages/es/index.d.ts +83 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/index.js +2 -0
- package/dist/messages/es/index.ts +2 -0
- package/dist/messages/es/navigation.json +3 -1
- package/dist/messages/fr/auth.json +25 -2
- package/dist/messages/fr/index.d.ts +83 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/index.js +2 -0
- package/dist/messages/fr/index.ts +2 -0
- package/dist/messages/fr/navigation.json +3 -1
- package/dist/messages/it/auth.json +25 -2
- package/dist/messages/it/index.d.ts +83 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/index.js +2 -0
- package/dist/messages/it/index.ts +2 -0
- package/dist/messages/it/navigation.json +3 -1
- package/dist/messages/pt/auth.json +25 -2
- package/dist/messages/pt/index.d.ts +83 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/index.js +2 -0
- package/dist/messages/pt/index.ts +2 -0
- package/dist/messages/pt/navigation.json +3 -1
- package/dist/migrations/005_api_audit_log.sql +2 -1
- package/dist/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
- package/dist/migrations/026_login_events.sql +91 -0
- package/dist/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
- package/dist/nextspark-registries.d.ts +580 -236
- package/dist/providers/DashboardProviders.d.ts +12 -3
- package/dist/providers/DashboardProviders.d.ts.map +1 -1
- package/dist/providers/DashboardProviders.js +6 -2
- package/dist/providers/theme-provider.d.ts +19 -2
- package/dist/providers/theme-provider.d.ts.map +1 -1
- package/dist/providers/theme-provider.js +6 -2
- package/dist/templates/app/(auth)/signup/page.tsx +8 -0
- package/dist/templates/app/(public)/[...slug]/page.tsx +9 -14
- package/dist/templates/app/api/auth/[...all]/route.ts +23 -1
- package/dist/templates/app/api/health/route.ts +4 -2
- package/dist/templates/app/api/internal/user-metadata/route.ts +5 -3
- package/dist/templates/app/api/mcp/route.ts +4 -1
- package/dist/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/dist/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/dist/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/dist/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/dist/templates/app/api/v1/api-keys/route.ts +21 -43
- package/dist/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/dist/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/dist/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/dist/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/dist/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/dist/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/dist/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/dist/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/dist/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/dist/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/dist/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/dist/templates/app/api/v1/media/route.ts +5 -9
- package/dist/templates/app/api/v1/media/upload/route.ts +9 -21
- package/dist/templates/app/api/v1/media-tags/route.ts +9 -13
- package/dist/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/dist/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/dist/templates/app/api/v1/post-categories/route.ts +5 -4
- package/dist/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/dist/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/dist/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/dist/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/dist/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/dist/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/dist/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/dist/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/dist/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/dist/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/dist/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/dist/templates/app/api/v1/teams/route.ts +33 -27
- package/dist/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/dist/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/dist/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/dist/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/dist/templates/app/api/v1/users/route.ts +21 -25
- package/dist/templates/app/dashboard/layout.tsx +2 -6
- package/dist/templates/app/layout.ppr.tsx +15 -15
- package/dist/templates/app/layout.tsx +46 -20
- package/dist/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/dist/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/dist/templates/contents/themes/starter/styles/globals.css +6 -2
- package/dist/templates/proxy.ts +46 -10
- package/dist/templates/tsconfig.json +4 -0
- package/dist/types/theme.d.ts +9 -0
- package/dist/types/theme.d.ts.map +1 -1
- package/migrations/005_api_audit_log.sql +2 -1
- package/migrations/025_api_audit_log_nullable_api_key.sql +12 -0
- package/migrations/026_login_events.sql +91 -0
- package/migrations/027_rls_bypass_developer_and_system_admin_team.sql +197 -0
- package/package.json +14 -11
- package/scripts/build/registry/__tests__/scope-registry.test.mjs +18 -0
- package/scripts/build/registry/generators/scope-registry.mjs +9 -2
- package/templates/app/(auth)/signup/page.tsx +8 -0
- package/templates/app/(public)/[...slug]/page.tsx +9 -14
- package/templates/app/api/auth/[...all]/route.ts +23 -1
- package/templates/app/api/health/route.ts +4 -2
- package/templates/app/api/internal/user-metadata/route.ts +5 -3
- package/templates/app/api/mcp/route.ts +4 -1
- package/templates/app/api/superadmin/subscriptions/route.ts +2 -1
- package/templates/app/api/superadmin/teams/[teamId]/route.ts +4 -3
- package/templates/app/api/superadmin/users/[userId]/route.ts +10 -3
- package/templates/app/api/v1/api-keys/[id]/route.ts +50 -71
- package/templates/app/api/v1/api-keys/route.ts +21 -43
- package/templates/app/api/v1/auth/signup-with-invite/route.ts +14 -14
- package/templates/app/api/v1/billing/cancel/route.ts +7 -6
- package/templates/app/api/v1/billing/change-plan/route.ts +5 -4
- package/templates/app/api/v1/billing/check-action/route.ts +6 -4
- package/templates/app/api/v1/billing/checkout/route.ts +7 -5
- package/templates/app/api/v1/billing/plans/route.ts +8 -12
- package/templates/app/api/v1/billing/portal/route.ts +7 -5
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +1 -1
- package/templates/app/api/v1/devtools/blocks/route.ts +5 -4
- package/templates/app/api/v1/devtools/features/route.ts +5 -4
- package/templates/app/api/v1/devtools/flows/route.ts +5 -4
- package/templates/app/api/v1/devtools/scheduled-actions/route.ts +9 -7
- package/templates/app/api/v1/devtools/scheduled-actions/run/route.ts +5 -4
- package/templates/app/api/v1/devtools/testing/route.ts +5 -4
- package/templates/app/api/v1/media/[id]/route.ts +13 -25
- package/templates/app/api/v1/media/[id]/tags/route.ts +17 -25
- package/templates/app/api/v1/media/check-duplicates/route.ts +5 -7
- package/templates/app/api/v1/media/route.ts +5 -9
- package/templates/app/api/v1/media/upload/route.ts +9 -21
- package/templates/app/api/v1/media-tags/route.ts +9 -13
- package/templates/app/api/v1/patterns/[id]/usages/route.ts +11 -19
- package/templates/app/api/v1/post-categories/[id]/route.ts +9 -7
- package/templates/app/api/v1/post-categories/route.ts +5 -4
- package/templates/app/api/v1/team-invitations/[token]/accept/route.ts +16 -18
- package/templates/app/api/v1/team-invitations/[token]/decline/route.ts +15 -17
- package/templates/app/api/v1/team-invitations/[token]/route.ts +8 -8
- package/templates/app/api/v1/team-invitations/route.ts +9 -11
- package/templates/app/api/v1/teams/[teamId]/invitations/route.ts +41 -32
- package/templates/app/api/v1/teams/[teamId]/invoices/[invoiceNumber]/route.ts +15 -16
- package/templates/app/api/v1/teams/[teamId]/invoices/route.ts +14 -15
- package/templates/app/api/v1/teams/[teamId]/members/[memberId]/route.ts +32 -36
- package/templates/app/api/v1/teams/[teamId]/members/route.ts +33 -33
- package/templates/app/api/v1/teams/[teamId]/route.ts +35 -41
- package/templates/app/api/v1/teams/[teamId]/subscription/route.ts +5 -3
- package/templates/app/api/v1/teams/[teamId]/usage/[limitSlug]/route.ts +10 -6
- package/templates/app/api/v1/teams/route.ts +33 -27
- package/templates/app/api/v1/teams/switch/route.ts +11 -13
- package/templates/app/api/v1/theme/[...path]/route.ts +23 -2
- package/templates/app/api/v1/users/[id]/meta/[key]/route.ts +32 -77
- package/templates/app/api/v1/users/[id]/route.ts +35 -68
- package/templates/app/api/v1/users/route.ts +21 -25
- package/templates/app/dashboard/layout.tsx +2 -6
- package/templates/app/layout.ppr.tsx +15 -15
- package/templates/app/layout.tsx +46 -20
- package/templates/contents/themes/starter/config/app.config.ts +19 -0
- package/templates/contents/themes/starter/config/theme.config.ts +6 -0
- package/templates/contents/themes/starter/styles/globals.css +6 -2
- package/templates/proxy.ts +46 -10
- package/templates/tsconfig.json +4 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +23 -0
- package/tests/jest/__mocks__/next-server.js +24 -0
- package/dist/components/teams/TeamSwitchModal.d.js +0 -0
- package/dist/components/ui/accordion.d.js +0 -0
- package/dist/components/ui/avatar.d.js +0 -0
- package/dist/components/ui/badge.d.js +0 -0
- package/dist/components/ui/button.d.js +0 -0
- package/dist/components/ui/card.d.js +0 -0
- package/dist/components/ui/dialog.d.js +0 -0
- package/dist/components/ui/dropdown-menu.d.js +0 -0
- package/dist/components/ui/input.d.js +0 -0
- package/dist/components/ui/progress.d.js +0 -0
- package/dist/components/ui/scroll-area.d.js +0 -0
- package/dist/components/ui/select.d.js +0 -0
- package/dist/components/ui/separator.d.js +0 -0
- package/dist/components/ui/table.d.js +0 -0
- package/dist/components/ui/textarea.d.js +0 -0
- package/dist/contexts/TeamContext.d.js +0 -0
- package/dist/hooks/useAuth.d.js +0 -0
- package/dist/hooks/useLastAuthMethod.d.js +0 -0
- package/dist/hooks/useOrigin.d.js +0 -0
- package/dist/lib/api/auth/dual-auth.d.js +0 -0
- package/dist/lib/api/auth.d.js +0 -0
- package/dist/lib/api/cache.d.js +0 -0
- package/dist/lib/api/keys.d.js +0 -0
- package/dist/lib/auth-client.d.js +0 -0
- package/dist/lib/auth-context.d.js +0 -0
- package/dist/lib/auth.d.js +0 -0
- package/dist/lib/billing/config-types.d.js +0 -0
- package/dist/lib/billing/enforcement.d.js +0 -0
- package/dist/lib/billing/helpers.d.js +0 -0
- package/dist/lib/billing/jobs.d.js +0 -0
- package/dist/lib/billing/types.d.js +0 -0
- package/dist/lib/blocks/loader.d.js +0 -0
- package/dist/lib/config/app.config.d.js +0 -0
- package/dist/lib/config/config-loader.d.js +0 -0
- package/dist/lib/config/config-sync.d.js +0 -0
- package/dist/lib/config/config-types.d.js +0 -0
- package/dist/lib/config/config-utils.d.js +0 -0
- package/dist/lib/config/dashboard-types.d.js +0 -0
- package/dist/lib/config/dashboard.config.d.js +0 -0
- package/dist/lib/config/icon-map.d.js +0 -0
- package/dist/lib/config/index.d.js +0 -7
- package/dist/lib/config/nextspark-types.d.js +0 -0
- package/dist/lib/config/roles-merge.d.js +0 -0
- package/dist/lib/config/types.d.js +0 -0
- package/dist/lib/db.d.js +0 -0
- package/dist/lib/email/factory.d.js +0 -0
- package/dist/lib/email/index.d.js +0 -3
- package/dist/lib/email/providers/console.d.js +0 -0
- package/dist/lib/email/providers/resend.d.js +0 -0
- package/dist/lib/email/templates.d.js +0 -0
- package/dist/lib/email/types.d.js +0 -0
- package/dist/lib/entities/types.d.js +0 -0
- package/dist/lib/permissions/system.d.js +0 -0
- package/dist/lib/permissions/types.d.js +0 -0
- package/dist/lib/plugins/hook-system.d.js +0 -0
- package/dist/lib/scheduled-actions/cleanup.d.js +0 -0
- package/dist/lib/scheduled-actions/index.d.js +0 -19
- package/dist/lib/scheduled-actions/initializer.d.js +0 -1
- package/dist/lib/scheduled-actions/processor.d.js +0 -0
- package/dist/lib/scheduled-actions/registry.d.js +0 -0
- package/dist/lib/scheduled-actions/scheduler.d.js +0 -0
- package/dist/lib/scheduled-actions/types.d.js +0 -0
- package/dist/lib/services/api-routes.service.d.js +0 -0
- package/dist/lib/services/entity-type.service.d.js +0 -0
- package/dist/lib/services/index.d.js +0 -40
- package/dist/lib/services/invoice.service.d.js +0 -0
- package/dist/lib/services/membership.service.d.js +0 -0
- package/dist/lib/services/meta.service.d.js +0 -0
- package/dist/lib/services/middleware.service.d.js +0 -0
- package/dist/lib/services/namespace.service.d.js +0 -0
- package/dist/lib/services/permission.service.d.js +0 -0
- package/dist/lib/services/plan.service.d.js +0 -0
- package/dist/lib/services/plugin.service.d.js +0 -0
- package/dist/lib/services/route-handler.service.d.js +0 -0
- package/dist/lib/services/scope.service.d.js +0 -0
- package/dist/lib/services/subscription.service.d.js +0 -0
- package/dist/lib/services/team-member.service.d.js +0 -0
- package/dist/lib/services/team.service.d.js +0 -0
- package/dist/lib/services/theme.service.d.js +0 -0
- package/dist/lib/services/usage.service.d.js +0 -0
- package/dist/lib/services/user-flags.service.d.js +0 -0
- package/dist/lib/services/user.service.d.js +0 -0
- package/dist/lib/teams/helpers.d.js +0 -0
- package/dist/lib/teams/permissions.d.js +0 -0
- package/dist/lib/teams/types.d.js +0 -0
- package/dist/lib/utils/config-merge.d.js +0 -0
- package/dist/lib/utils/file-logger.d.js +0 -0
- package/dist/lib/utils.d.js +0 -0
- package/dist/styles/classes.json +0 -1093
- package/dist/styles/ui.css +0 -2
- package/dist/types/auth.d.js +0 -0
- package/dist/types/blocks.d.js +0 -0
- package/dist/types/meta.types.d.js +0 -0
- package/dist/types/plugin.d.js +0 -0
- package/dist/types/testing.types.d.js +0 -0
- package/dist/types/theme.d.js +0 -0
- package/dist/types/user.types.d.js +0 -0
- package/scripts/build/build-ui-css.mjs +0 -351
- /package/dist/{components/app/layouts/PublicFooter.d.js → lib/auth/security-notifications/types.js} +0 -0
- /package/dist/{components/app/layouts/PublicNavbar.d.js → nextspark-registries.d.js} +0 -0
- /package/dist/{components/app/misc/ThemeToggle.d.js → types/use-intl-react-shim.d.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getTypedSession } from "../auth.js";
|
|
2
2
|
import { entityRegistry, ensureInitialized } from "../entities/registry.js";
|
|
3
3
|
import {
|
|
4
4
|
createApiResponse,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { afterEntityCreate, afterEntityUpdate, afterEntityDelete } from "../entities/entity-hooks.js";
|
|
14
14
|
async function checkBasicAuth(request, entityName) {
|
|
15
15
|
try {
|
|
16
|
-
const session = await
|
|
16
|
+
const session = await getTypedSession(request.headers);
|
|
17
17
|
if (!session) {
|
|
18
18
|
return {
|
|
19
19
|
allowed: false,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { ApiKeyAuth } from './auth';
|
|
3
|
+
import { type AuthenticateOptions } from './auth/scope-policy';
|
|
3
4
|
interface SessionAuth {
|
|
4
5
|
userId: string;
|
|
5
6
|
scopes: string[];
|
|
@@ -10,14 +11,34 @@ type Auth = ApiKeyAuth | SessionAuth;
|
|
|
10
11
|
/**
|
|
11
12
|
* Validates authentication using session or API key
|
|
12
13
|
* Prefers session auth if available, falls back to API key
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
*
|
|
15
|
+
* Resolves to `{ auth: null }` when the request carries neither a valid
|
|
16
|
+
* session nor a valid API key — it never throws for an unauthenticated
|
|
17
|
+
* request (#112). Routes are expected to branch on `auth` and answer 401
|
|
18
|
+
* themselves; a throw here would be swallowed by the generic try/catch most
|
|
19
|
+
* routes wrap around their body and surface as a 500 instead.
|
|
20
|
+
*
|
|
21
|
+
* API-key scope enforcement fails closed here (#93), with the same rule as
|
|
22
|
+
* `authenticateRequest`: declare `{ requiredScope }` (or, explicitly,
|
|
23
|
+
* `{ allowAnyScope: true }`). A key rejected on scope resolves to
|
|
24
|
+
* `{ auth: null, errorResponse }` where `errorResponse` is the ready-made
|
|
25
|
+
* 403 — return it. Session auth keeps its role-derived scopes and is not
|
|
26
|
+
* gated here; use `checkScope` for explicit per-branch checks.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateAndAuthenticateRequest(request: NextRequest, options?: AuthenticateOptions): Promise<{
|
|
29
|
+
auth: Auth | null;
|
|
16
30
|
rateLimitResponse?: NextResponse;
|
|
31
|
+
/** Set (403) when a valid API key was rejected by scope enforcement. */
|
|
32
|
+
errorResponse?: NextResponse;
|
|
17
33
|
}>;
|
|
18
34
|
/**
|
|
19
35
|
* Valida la API key y aplica rate limiting
|
|
20
36
|
* Esta función debe ser llamada al inicio de cada endpoint API v1
|
|
37
|
+
*
|
|
38
|
+
* API-key-only and strict: throws when no valid key is present. Endpoints
|
|
39
|
+
* that also accept sessions (or want to treat anonymous callers as a normal
|
|
40
|
+
* 401 branch) should use `validateAndAuthenticateRequest`, which resolves to
|
|
41
|
+
* a null auth instead.
|
|
21
42
|
*/
|
|
22
43
|
export declare function validateAndAuthenticateApiRequest(request: NextRequest): Promise<{
|
|
23
44
|
auth: ApiKeyAuth;
|
|
@@ -31,8 +52,12 @@ export declare function getApiAuth(request: NextRequest): ApiKeyAuth;
|
|
|
31
52
|
/**
|
|
32
53
|
* Verifica que la autenticación tenga el scope requerido
|
|
33
54
|
* Retorna un NextResponse de error si no tiene permisos, null si está autorizado
|
|
55
|
+
*
|
|
56
|
+
* Applies to both session auth (role-derived scopes) and API-key auth, with
|
|
57
|
+
* the same matching rule as `hasRequiredScope`: the wildcard `*` satisfies
|
|
58
|
+
* any scope, and an array is satisfied by any one of its entries.
|
|
34
59
|
*/
|
|
35
|
-
export declare function checkScope(auth: Auth, requiredScope: string): NextResponse | null;
|
|
60
|
+
export declare function checkScope(auth: Auth, requiredScope: string | string[]): NextResponse | null;
|
|
36
61
|
/**
|
|
37
62
|
* Crea una respuesta exitosa estandarizada para la API
|
|
38
63
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAkB,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/api/helpers.js
CHANGED
|
@@ -8,7 +8,13 @@ import { MetaService } from "../services/meta.service.js";
|
|
|
8
8
|
import { ScopeService } from "../services/scope.service.js";
|
|
9
9
|
import { getChildEntities } from "../entities/queries.js";
|
|
10
10
|
import { getCorsOrigins, normalizeOrigin, isOriginAllowed } from "../utils/cors.js";
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
describeRoute,
|
|
13
|
+
normalizeRequiredScopes,
|
|
14
|
+
resolveApiKeyScopeFailure,
|
|
15
|
+
scopesSatisfy
|
|
16
|
+
} from "./auth/scope-policy.js";
|
|
17
|
+
async function validateAndAuthenticateRequest(request, options = {}) {
|
|
12
18
|
var _a;
|
|
13
19
|
const cookieHeader = request.headers.get("cookie");
|
|
14
20
|
if (cookieHeader) {
|
|
@@ -35,7 +41,21 @@ async function validateAndAuthenticateRequest(request) {
|
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
console.log(`[Auth] Falling back to API key authentication`);
|
|
38
|
-
|
|
44
|
+
const apiKeyAuth = await validateApiKey(request);
|
|
45
|
+
if (!apiKeyAuth) {
|
|
46
|
+
return { auth: null };
|
|
47
|
+
}
|
|
48
|
+
const scopeFailure = resolveApiKeyScopeFailure(apiKeyAuth.scopes, options, describeRoute(request));
|
|
49
|
+
if (scopeFailure) {
|
|
50
|
+
return {
|
|
51
|
+
auth: null,
|
|
52
|
+
errorResponse: NextResponse.json(
|
|
53
|
+
{ success: false, error: scopeFailure.message, code: scopeFailure.code },
|
|
54
|
+
{ status: scopeFailure.status }
|
|
55
|
+
)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return applyApiKeyRateLimit(apiKeyAuth);
|
|
39
59
|
}
|
|
40
60
|
function generateScopesForRole(role, flags = []) {
|
|
41
61
|
const baseScopes = getBaseScopesFromRegistry();
|
|
@@ -80,6 +100,9 @@ async function validateAndAuthenticateApiRequest(request) {
|
|
|
80
100
|
if (!auth2) {
|
|
81
101
|
throw new Error("Invalid API key");
|
|
82
102
|
}
|
|
103
|
+
return applyApiKeyRateLimit(auth2);
|
|
104
|
+
}
|
|
105
|
+
function applyApiKeyRateLimit(auth2) {
|
|
83
106
|
const rateLimitResult = checkRateLimit(auth2.keyId);
|
|
84
107
|
if (!rateLimitResult.allowed) {
|
|
85
108
|
const response = NextResponse.json(
|
|
@@ -120,14 +143,15 @@ function getApiAuth(request) {
|
|
|
120
143
|
};
|
|
121
144
|
}
|
|
122
145
|
function checkScope(auth2, requiredScope) {
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
if (!scopesSatisfy(auth2.scopes, requiredScope)) {
|
|
147
|
+
const required = normalizeRequiredScopes(requiredScope);
|
|
148
|
+
const label = required.join("' or '");
|
|
125
149
|
return NextResponse.json(
|
|
126
150
|
{
|
|
127
151
|
success: false,
|
|
128
152
|
error: "Insufficient permissions",
|
|
129
|
-
message: `This operation requires the '${
|
|
130
|
-
required_scope:
|
|
153
|
+
message: `This operation requires the '${label}' scope`,
|
|
154
|
+
required_scope: required.length === 1 ? required[0] : required,
|
|
131
155
|
your_scopes: auth2.scopes,
|
|
132
156
|
code: "INSUFFICIENT_PERMISSIONS"
|
|
133
157
|
},
|
package/dist/lib/api/keys.d.ts
CHANGED
|
@@ -48,8 +48,14 @@ export declare const API_SCOPES: {
|
|
|
48
48
|
readonly 'media:read': "Leer información de archivos y media";
|
|
49
49
|
readonly 'media:write': "Subir y actualizar archivos";
|
|
50
50
|
readonly 'media:delete': "Eliminar archivos";
|
|
51
|
+
readonly 'teams:read': "Leer equipos, miembros e invitaciones";
|
|
52
|
+
readonly 'teams:write': "Crear y actualizar equipos, miembros e invitaciones";
|
|
53
|
+
readonly 'teams:delete': "Eliminar equipos";
|
|
54
|
+
readonly 'billing:read': "Leer suscripciones, facturas y uso";
|
|
55
|
+
readonly 'billing:write': "Gestionar suscripciones, checkout, cambios de plan y uso";
|
|
51
56
|
readonly 'admin:api-keys': "Gestionar API keys";
|
|
52
57
|
readonly 'admin:users': "Administración completa de usuarios";
|
|
58
|
+
readonly 'admin:devtools': "Acceso a las APIs de DevTools (registries, testing, scheduled actions)";
|
|
53
59
|
readonly '*': "Acceso completo (solo superadmin)";
|
|
54
60
|
};
|
|
55
61
|
export type ApiScope = keyof typeof API_SCOPES;
|
|
@@ -82,6 +88,16 @@ export declare const SCOPE_CATEGORIES: {
|
|
|
82
88
|
readonly description: "Gestión de tareas y TODOs";
|
|
83
89
|
readonly scopes: ApiScope[];
|
|
84
90
|
};
|
|
91
|
+
readonly teams: {
|
|
92
|
+
readonly name: "Teams";
|
|
93
|
+
readonly description: "Equipos, miembros e invitaciones";
|
|
94
|
+
readonly scopes: ApiScope[];
|
|
95
|
+
};
|
|
96
|
+
readonly billing: {
|
|
97
|
+
readonly name: "Billing";
|
|
98
|
+
readonly description: "Suscripciones, facturas, uso y planes";
|
|
99
|
+
readonly scopes: ApiScope[];
|
|
100
|
+
};
|
|
85
101
|
readonly admin: {
|
|
86
102
|
readonly name: "Administración";
|
|
87
103
|
readonly description: "Funciones administrativas";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/lib/api/keys.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAa;IACxB;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBrF;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAM9C;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKzC;;OAEG;WACU,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE;IAUpF;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;CAchD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/lib/api/keys.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAa;IACxB;;;OAGG;WACU,cAAc,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBrF;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAM9C;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAKzC;;OAEG;WACU,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQlD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE;IAUpF;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;CAchD;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;CAgCb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGrD;AAED,oFAAoF;AACpF,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxD;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;yBAIgC,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKV,QAAQ,EAAE;;;;;yBAKtB,QAAQ,EAAE;;;;;yBAKQ,QAAQ,EAAE;;;;;yBAKxD,QAAQ,EAAE;;CAErB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CASd,CAAC;AAEX;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAmB9F"}
|
package/dist/lib/api/keys.js
CHANGED
|
@@ -81,9 +81,17 @@ const API_SCOPES = {
|
|
|
81
81
|
"media:read": "Leer informaci\xF3n de archivos y media",
|
|
82
82
|
"media:write": "Subir y actualizar archivos",
|
|
83
83
|
"media:delete": "Eliminar archivos",
|
|
84
|
+
// Teams (equipos, miembros e invitaciones)
|
|
85
|
+
"teams:read": "Leer equipos, miembros e invitaciones",
|
|
86
|
+
"teams:write": "Crear y actualizar equipos, miembros e invitaciones",
|
|
87
|
+
"teams:delete": "Eliminar equipos",
|
|
88
|
+
// Billing (suscripciones, facturas, uso y planes)
|
|
89
|
+
"billing:read": "Leer suscripciones, facturas y uso",
|
|
90
|
+
"billing:write": "Gestionar suscripciones, checkout, cambios de plan y uso",
|
|
84
91
|
// Administración
|
|
85
92
|
"admin:api-keys": "Gestionar API keys",
|
|
86
93
|
"admin:users": "Administraci\xF3n completa de usuarios",
|
|
94
|
+
"admin:devtools": "Acceso a las APIs de DevTools (registries, testing, scheduled actions)",
|
|
87
95
|
// Comodín (solo para superadmins)
|
|
88
96
|
"*": "Acceso completo (solo superadmin)"
|
|
89
97
|
};
|
|
@@ -109,10 +117,20 @@ const SCOPE_CATEGORIES = {
|
|
|
109
117
|
description: "Gesti\xF3n de tareas y TODOs",
|
|
110
118
|
scopes: ["tasks:read", "tasks:write", "tasks:delete"]
|
|
111
119
|
},
|
|
120
|
+
teams: {
|
|
121
|
+
name: "Teams",
|
|
122
|
+
description: "Equipos, miembros e invitaciones",
|
|
123
|
+
scopes: ["teams:read", "teams:write", "teams:delete"]
|
|
124
|
+
},
|
|
125
|
+
billing: {
|
|
126
|
+
name: "Billing",
|
|
127
|
+
description: "Suscripciones, facturas, uso y planes",
|
|
128
|
+
scopes: ["billing:read", "billing:write"]
|
|
129
|
+
},
|
|
112
130
|
admin: {
|
|
113
131
|
name: "Administraci\xF3n",
|
|
114
132
|
description: "Funciones administrativas",
|
|
115
|
-
scopes: ["admin:api-keys", "admin:users"]
|
|
133
|
+
scopes: ["admin:api-keys", "admin:users", "admin:devtools"]
|
|
116
134
|
},
|
|
117
135
|
system: {
|
|
118
136
|
name: "Sistema",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns-usages.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/routes/patterns-usages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAavD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B;;gBAQQ,OAAO,CAAC;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;;;
|
|
1
|
+
{"version":3,"file":"patterns-usages.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/routes/patterns-usages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAavD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B;;gBAQQ,OAAO,CAAC;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;;;EA0EzE"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { NextResponse } from "next/server";
|
|
2
1
|
import {
|
|
3
2
|
createApiResponse,
|
|
4
3
|
createApiError,
|
|
@@ -7,7 +6,7 @@ import {
|
|
|
7
6
|
handleCorsPreflightRequest,
|
|
8
7
|
addCorsHeaders
|
|
9
8
|
} from "../helpers.js";
|
|
10
|
-
import { authenticateRequest,
|
|
9
|
+
import { authenticateRequest, createAuthFailureResponse } from "../auth/dual-auth.js";
|
|
11
10
|
import { PatternUsageService } from "../../services/index.js";
|
|
12
11
|
import { queryOneWithRLS } from "../../db.js";
|
|
13
12
|
function createPatternUsagesHandlers() {
|
|
@@ -17,20 +16,13 @@ function createPatternUsagesHandlers() {
|
|
|
17
16
|
const GET = withApiLogging(
|
|
18
17
|
async (req, { params }) => {
|
|
19
18
|
try {
|
|
20
|
-
const authResult = await authenticateRequest(req);
|
|
19
|
+
const authResult = await authenticateRequest(req, { requiredScope: "patterns:read" });
|
|
21
20
|
if (!authResult.success) {
|
|
22
|
-
return
|
|
23
|
-
{ success: false, error: "Authentication required", code: "AUTHENTICATION_FAILED" },
|
|
24
|
-
{ status: 401 }
|
|
25
|
-
);
|
|
21
|
+
return createAuthFailureResponse(authResult);
|
|
26
22
|
}
|
|
27
23
|
if (authResult.rateLimitResponse) {
|
|
28
24
|
return authResult.rateLimitResponse;
|
|
29
25
|
}
|
|
30
|
-
if (!hasRequiredScope(authResult, "patterns:read")) {
|
|
31
|
-
const response2 = createApiError("Insufficient permissions", 403);
|
|
32
|
-
return addCorsHeaders(response2);
|
|
33
|
-
}
|
|
34
26
|
const { id: patternId } = await params;
|
|
35
27
|
const userId = authResult.user.id;
|
|
36
28
|
if (!patternId || patternId.trim() === "") {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Login Methods & Presets
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers around `AuthConfig.methods` — the list of login methods an app
|
|
5
|
+
* offers (`'email-otp'`, `'google'`, `'email-password'`), in priority order.
|
|
6
|
+
* Safe to import from server and client code: no side effects, types only.
|
|
7
|
+
*
|
|
8
|
+
* Portfolio policy (#126): the DEFAULT is the passwordless preset — a one-time
|
|
9
|
+
* code by email + Google OAuth, no password field. Themes opt into the classic
|
|
10
|
+
* preset (or any combination) from their app.config.ts.
|
|
11
|
+
*/
|
|
12
|
+
import type { AuthConfig, AuthLoginMethod, AuthPreset } from '../config/types';
|
|
13
|
+
/** Every method the core knows how to render/serve. */
|
|
14
|
+
export declare const AUTH_LOGIN_METHODS: readonly AuthLoginMethod[];
|
|
15
|
+
/** Named bundles of login methods. Arrays are in UI priority order. */
|
|
16
|
+
export declare const AUTH_PRESETS: Readonly<Record<AuthPreset, readonly AuthLoginMethod[]>>;
|
|
17
|
+
/** The preset applied when a theme does not set `auth.methods`. */
|
|
18
|
+
export declare const DEFAULT_AUTH_METHODS: readonly AuthLoginMethod[];
|
|
19
|
+
export declare function isAuthLoginMethod(value: unknown): value is AuthLoginMethod;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the login methods an app offers.
|
|
22
|
+
*
|
|
23
|
+
* - Unknown values are dropped (with a warning), duplicates removed, order kept.
|
|
24
|
+
* - No `methods` configured, or nothing valid left → the default (passwordless)
|
|
25
|
+
* preset.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveAuthMethods(authConfig?: Pick<AuthConfig, 'methods'> | {
|
|
28
|
+
methods?: readonly string[];
|
|
29
|
+
} | null): AuthLoginMethod[];
|
|
30
|
+
export declare function isAuthMethodEnabled(methods: readonly AuthLoginMethod[], method: AuthLoginMethod): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* True when the login has NO password path: a one-time code is offered and
|
|
33
|
+
* `'email-password'` is not. Google may or may not be present.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isPasswordlessPreset(methods: readonly AuthLoginMethod[]): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* The email-based method the login form should open with: the first of
|
|
38
|
+
* `'email-otp'` / `'email-password'` in the configured order, or null when the
|
|
39
|
+
* app is Google-only.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getPrimaryEmailMethod(methods: readonly AuthLoginMethod[]): Extract<AuthLoginMethod, 'email-otp' | 'email-password'> | null;
|
|
42
|
+
/**
|
|
43
|
+
* Which named preset the resolved methods correspond to (order-insensitive),
|
|
44
|
+
* or null for a custom combination.
|
|
45
|
+
*/
|
|
46
|
+
export declare function matchAuthPreset(methods: readonly AuthLoginMethod[]): AuthPreset | null;
|
|
47
|
+
/**
|
|
48
|
+
* Server-side: should Better Auth's email + password endpoints be enabled?
|
|
49
|
+
* Defaults to true regardless of the UI preset (see `AuthConfig.emailAndPassword`).
|
|
50
|
+
*/
|
|
51
|
+
export declare function isPasswordLoginEnabled(authConfig?: Pick<AuthConfig, 'emailAndPassword'> | null): boolean;
|
|
52
|
+
//# sourceMappingURL=auth-methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE9E,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA8C,CAAA;AAEvG,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,CAKjF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,SAAS,eAAe,EAA8B,CAAA;AAEzF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAChF,eAAe,EAAE,CAkBnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,OAAO,CAAC,eAAe,EAAE,WAAW,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,UAAU,GAAG,IAAI,CAMtF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,GAAG,IAAI,GACvD,OAAO,CAET"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const AUTH_LOGIN_METHODS = ["email-otp", "google", "email-password"];
|
|
2
|
+
const AUTH_PRESETS = {
|
|
3
|
+
/** One-time code by email + Google. No password anywhere. DEFAULT. */
|
|
4
|
+
passwordless: ["email-otp", "google"],
|
|
5
|
+
/** Email + password (with signup / reset flows) + Google. */
|
|
6
|
+
classic: ["email-password", "google"]
|
|
7
|
+
};
|
|
8
|
+
const DEFAULT_AUTH_METHODS = AUTH_PRESETS.passwordless;
|
|
9
|
+
function isAuthLoginMethod(value) {
|
|
10
|
+
return typeof value === "string" && AUTH_LOGIN_METHODS.includes(value);
|
|
11
|
+
}
|
|
12
|
+
function resolveAuthMethods(authConfig) {
|
|
13
|
+
const configured = authConfig == null ? void 0 : authConfig.methods;
|
|
14
|
+
if (!Array.isArray(configured)) return [...DEFAULT_AUTH_METHODS];
|
|
15
|
+
const valid = [];
|
|
16
|
+
for (const method of configured) {
|
|
17
|
+
if (!isAuthLoginMethod(method)) {
|
|
18
|
+
console.warn(`[Auth] Ignoring unknown auth.methods entry: ${String(method)}`);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (!valid.includes(method)) valid.push(method);
|
|
22
|
+
}
|
|
23
|
+
if (valid.length === 0) {
|
|
24
|
+
console.warn("[Auth] auth.methods resolved to an empty list; falling back to the passwordless preset");
|
|
25
|
+
return [...DEFAULT_AUTH_METHODS];
|
|
26
|
+
}
|
|
27
|
+
return valid;
|
|
28
|
+
}
|
|
29
|
+
function isAuthMethodEnabled(methods, method) {
|
|
30
|
+
return methods.includes(method);
|
|
31
|
+
}
|
|
32
|
+
function isPasswordlessPreset(methods) {
|
|
33
|
+
return methods.includes("email-otp") && !methods.includes("email-password");
|
|
34
|
+
}
|
|
35
|
+
function getPrimaryEmailMethod(methods) {
|
|
36
|
+
for (const method of methods) {
|
|
37
|
+
if (method === "email-otp" || method === "email-password") return method;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function matchAuthPreset(methods) {
|
|
42
|
+
const sorted = [...methods].sort().join(",");
|
|
43
|
+
for (const [preset, presetMethods] of Object.entries(AUTH_PRESETS)) {
|
|
44
|
+
if ([...presetMethods].sort().join(",") === sorted) return preset;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function isPasswordLoginEnabled(authConfig) {
|
|
49
|
+
var _a;
|
|
50
|
+
return ((_a = authConfig == null ? void 0 : authConfig.emailAndPassword) == null ? void 0 : _a.enabled) !== false;
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
AUTH_LOGIN_METHODS,
|
|
54
|
+
AUTH_PRESETS,
|
|
55
|
+
DEFAULT_AUTH_METHODS,
|
|
56
|
+
getPrimaryEmailMethod,
|
|
57
|
+
isAuthLoginMethod,
|
|
58
|
+
isAuthMethodEnabled,
|
|
59
|
+
isPasswordLoginEnabled,
|
|
60
|
+
isPasswordlessPreset,
|
|
61
|
+
matchAuthPreset,
|
|
62
|
+
resolveAuthMethods
|
|
63
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration-guard-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/registration-guard-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,uBAAuB,QAAO,
|
|
1
|
+
{"version":3,"file":"registration-guard-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/registration-guard-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,uBAAuB,QAAO,gBA6C1C,CAAC"}
|
|
@@ -29,9 +29,18 @@ export declare function isDomainAllowed(email: string, allowedDomains: string[])
|
|
|
29
29
|
*
|
|
30
30
|
* Google is disabled when:
|
|
31
31
|
* - providers.google.enabled is explicitly false
|
|
32
|
+
* - 'google' is not one of the configured login `methods`
|
|
32
33
|
* - GOOGLE_CLIENT_ID env var is not set (runtime check, not done here)
|
|
33
34
|
*/
|
|
34
35
|
export declare function isGoogleAuthEnabled(authConfig: AuthConfig): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check if the email+password SIGNUP flow (the /signup page and its form) is
|
|
38
|
+
* available: the registration mode must allow self-signup AND
|
|
39
|
+
* 'email-password' must be one of the configured login methods. Under the
|
|
40
|
+
* passwordless preset the first OTP sign-in creates the account, so there is
|
|
41
|
+
* no separate signup page.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isPasswordSignupAvailable(authConfig: AuthConfig): boolean;
|
|
35
44
|
/**
|
|
36
45
|
* Check if the signup page should be accessible.
|
|
37
46
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/registration-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"registration-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/registration-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAGrF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAShF;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAKnE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAKzE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAEnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,OAAO,GACf,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAEnE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,gBAAgB,CAY5E"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveAuthMethods } from "./auth-methods.js";
|
|
1
2
|
function isRegistrationOpen(mode) {
|
|
2
3
|
return mode === "open" || mode === "domain-open";
|
|
3
4
|
}
|
|
@@ -12,7 +13,10 @@ function isDomainAllowed(email, allowedDomains) {
|
|
|
12
13
|
}
|
|
13
14
|
function isGoogleAuthEnabled(authConfig) {
|
|
14
15
|
var _a, _b;
|
|
15
|
-
return ((_b = (_a = authConfig.providers) == null ? void 0 : _a.google) == null ? void 0 : _b.enabled) !== false;
|
|
16
|
+
return ((_b = (_a = authConfig.providers) == null ? void 0 : _a.google) == null ? void 0 : _b.enabled) !== false && resolveAuthMethods(authConfig).includes("google");
|
|
17
|
+
}
|
|
18
|
+
function isPasswordSignupAvailable(authConfig) {
|
|
19
|
+
return isEmailSignupEnabled(authConfig.registration.mode) && resolveAuthMethods(authConfig).includes("email-password");
|
|
16
20
|
}
|
|
17
21
|
function isSignupPageVisible(mode) {
|
|
18
22
|
return mode === "open" || mode === "domain-open";
|
|
@@ -36,7 +40,8 @@ function getPublicAuthConfig(authConfig) {
|
|
|
36
40
|
google: {
|
|
37
41
|
enabled: isGoogleAuthEnabled(authConfig)
|
|
38
42
|
}
|
|
39
|
-
}
|
|
43
|
+
},
|
|
44
|
+
methods: resolveAuthMethods(authConfig)
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
47
|
export {
|
|
@@ -45,6 +50,7 @@ export {
|
|
|
45
50
|
isEmailLoginVisible,
|
|
46
51
|
isEmailSignupEnabled,
|
|
47
52
|
isGoogleAuthEnabled,
|
|
53
|
+
isPasswordSignupAvailable,
|
|
48
54
|
isRegistrationOpen,
|
|
49
55
|
isSignupPageVisible,
|
|
50
56
|
shouldBlockSignup
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device fingerprinting for new-device login detection.
|
|
3
|
+
*
|
|
4
|
+
* The fingerprint is deliberately COARSE:
|
|
5
|
+
* fingerprint = sha256( normalizedUA + "|" + ip/24 )
|
|
6
|
+
*
|
|
7
|
+
* - UA is normalized so browser auto-updates (patch/build version churn) do NOT
|
|
8
|
+
* look like a brand-new device every week. We keep major.minor of each version
|
|
9
|
+
* token and drop the rest (`Chrome/120.0.6099.109` → `Chrome/120.0`).
|
|
10
|
+
* - IP is truncated to its /24 network so a phone hopping between addresses in
|
|
11
|
+
* the same carrier/NAT block is still "the same place".
|
|
12
|
+
*
|
|
13
|
+
* This is an anti-annoyance heuristic for security emails, NOT an
|
|
14
|
+
* authentication signal — a coarse fingerprint means fewer false "new device"
|
|
15
|
+
* alerts, at the cost of not distinguishing devices that share a /24 and a
|
|
16
|
+
* near-identical UA. That trade-off is intentional.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a User-Agent string:
|
|
20
|
+
* - lower-cased and whitespace-collapsed
|
|
21
|
+
* - every version token with 3+ dotted numeric components is reduced to its
|
|
22
|
+
* first two (major.minor), stripping patch/build churn
|
|
23
|
+
*
|
|
24
|
+
* A missing UA normalizes to the empty string (all UA-less clients from the
|
|
25
|
+
* same /24 then share a fingerprint, which is the desired conservative bucket).
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizeUserAgent(ua: string | null | undefined): string;
|
|
28
|
+
/**
|
|
29
|
+
* Reduce an IP address to its /24 network key.
|
|
30
|
+
* - IPv4 `a.b.c.d` → `a.b.c.0/24`
|
|
31
|
+
* - IPv6 `x:y:z:...` → first three hextets + `::/48` (coarse, best-effort)
|
|
32
|
+
* - anything else → returned unchanged (e.g. the literal 'unknown')
|
|
33
|
+
*/
|
|
34
|
+
export declare function ipToNetworkKey(ip: string | null | undefined): string;
|
|
35
|
+
/**
|
|
36
|
+
* Compute the stable device fingerprint for a login.
|
|
37
|
+
*
|
|
38
|
+
* @param ua - raw User-Agent header (or null)
|
|
39
|
+
* @param ip - client IP (or null); truncated to /24 before hashing
|
|
40
|
+
* @returns hex sha256 of `normalizedUA|ipNetworkKey`
|
|
41
|
+
*/
|
|
42
|
+
export declare function computeDeviceFingerprint(ua: string | null, ip: string | null): string;
|
|
43
|
+
/**
|
|
44
|
+
* Extract the best-effort client IP from request headers.
|
|
45
|
+
*
|
|
46
|
+
* Strategy mirrors the rate-limiter (see lib/api/rate-limit.ts):
|
|
47
|
+
* Cloudflare > rightmost x-forwarded-for > x-real-ip > true-client-ip > 'unknown'
|
|
48
|
+
* The rightmost x-forwarded-for entry is the one appended by the last (trusted)
|
|
49
|
+
* proxy, so it is the hardest for a client to spoof.
|
|
50
|
+
*/
|
|
51
|
+
export declare function extractClientIp(headers: Headers): string;
|
|
52
|
+
//# sourceMappingURL=device-fingerprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-fingerprint.d.ts","sourceRoot":"","sources":["../../../../src/lib/auth/security-notifications/device-fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAQxE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBpE;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,MAAM,GAAG,IAAI,EACjB,EAAE,EAAE,MAAM,GAAG,IAAI,GAChB,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAoBxD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
function normalizeUserAgent(ua) {
|
|
3
|
+
if (!ua) return "";
|
|
4
|
+
return ua.toLowerCase().trim().replace(/\s+/g, " ").replace(/(\d+)\.(\d+)(?:\.\d+)+/g, "$1.$2");
|
|
5
|
+
}
|
|
6
|
+
function ipToNetworkKey(ip) {
|
|
7
|
+
if (!ip) return "";
|
|
8
|
+
const trimmed = ip.trim();
|
|
9
|
+
const v4 = trimmed.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
|
|
10
|
+
if (v4) {
|
|
11
|
+
return `${v4[1]}.${v4[2]}.${v4[3]}.0/24`;
|
|
12
|
+
}
|
|
13
|
+
if (trimmed.includes(":")) {
|
|
14
|
+
const hextets = trimmed.split(":").filter(Boolean).slice(0, 3);
|
|
15
|
+
if (hextets.length > 0) return `${hextets.join(":")}::/48`;
|
|
16
|
+
}
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
function computeDeviceFingerprint(ua, ip) {
|
|
20
|
+
const material = `${normalizeUserAgent(ua)}|${ipToNetworkKey(ip)}`;
|
|
21
|
+
return createHash("sha256").update(material).digest("hex");
|
|
22
|
+
}
|
|
23
|
+
function extractClientIp(headers) {
|
|
24
|
+
const cfIp = headers.get("cf-connecting-ip");
|
|
25
|
+
if (cfIp) return cfIp;
|
|
26
|
+
const forwardedFor = headers.get("x-forwarded-for");
|
|
27
|
+
if (forwardedFor) {
|
|
28
|
+
const ips = forwardedFor.split(",").map((v) => v.trim()).filter(Boolean);
|
|
29
|
+
if (ips.length > 0) return ips[ips.length - 1];
|
|
30
|
+
}
|
|
31
|
+
const realIp = headers.get("x-real-ip");
|
|
32
|
+
if (realIp) return realIp;
|
|
33
|
+
const trueClientIp = headers.get("true-client-ip");
|
|
34
|
+
if (trueClientIp) return trueClientIp;
|
|
35
|
+
return "unknown";
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
computeDeviceFingerprint,
|
|
39
|
+
extractClientIp,
|
|
40
|
+
ipToNetworkKey,
|
|
41
|
+
normalizeUserAgent
|
|
42
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security-notifications dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Called from the Better Auth route handler AFTER the auth response has been
|
|
5
|
+
* produced. It inspects the request path + the outgoing response, and — when a
|
|
6
|
+
* security-relevant event succeeded — records login history and queues the
|
|
7
|
+
* appropriate email via the `auth:security-notification` scheduled action.
|
|
8
|
+
*
|
|
9
|
+
* ────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
* ⚠️ CRITICAL FOOTGUN — read the user from the RESPONSE BODY, not the session.
|
|
11
|
+
* ────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
* For a sign-in, the session does NOT exist on the INCOMING request: the session
|
|
13
|
+
* cookie is only now being written, sitting in the OUTGOING `Set-Cookie` header.
|
|
14
|
+
* Calling `auth.api.getSession({ headers: req.headers })` here would read the
|
|
15
|
+
* incoming cookies, find none, and silently return null — so new-device emails
|
|
16
|
+
* would NEVER fire. We therefore read the just-authenticated user from
|
|
17
|
+
* `response.clone().json()` (Better Auth's sign-in/change-password responses
|
|
18
|
+
* carry `{ user, token, ... }`).
|
|
19
|
+
*
|
|
20
|
+
* The ONE exception is `change-email`: that request is made by an ALREADY
|
|
21
|
+
* authenticated user (its incoming cookie is valid) and its response body is
|
|
22
|
+
* only `{ status: true }` — no user. There we DO read the session, to recover
|
|
23
|
+
* the *old* address the notification must go to. That asymmetry is deliberate;
|
|
24
|
+
* do not "simplify" it into a single getSession call.
|
|
25
|
+
*
|
|
26
|
+
* Everything here is best-effort: any failure is swallowed so a security email
|
|
27
|
+
* problem can NEVER turn a successful auth response into a 500.
|
|
28
|
+
*/
|
|
29
|
+
import type { NextRequest, NextResponse } from 'next/server';
|
|
30
|
+
/**
|
|
31
|
+
* Entry point. Inspect (req, response) and fire the matching security email.
|
|
32
|
+
*
|
|
33
|
+
* @param req the auth request (a clone is fine; headers + body are read here)
|
|
34
|
+
* @param response the outgoing auth response (cloned before reading its body)
|
|
35
|
+
*/
|
|
36
|
+
export declare function dispatchSecurityNotificationsForRequest(req: NextRequest, response: NextResponse | Response): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../../../src/lib/auth/security-notifications/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAwM5D;;;;;GAKG;AACH,wBAAsB,uCAAuC,CAC3D,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,YAAY,GAAG,QAAQ,GAChC,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|