@cosmicdrift/kumiko-bundled-features 1.0.0 → 2.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/package.json +41 -7
- package/src/__tests__/access-invalidation-consumer.integration.test.ts +279 -0
- package/src/__tests__/access-invalidation-stream.integration.test.ts +217 -0
- package/src/admin-shell/__tests__/admin-shell-security.integration.test.ts +200 -0
- package/src/admin-shell/__tests__/admin-shell.boot.test.ts +147 -0
- package/src/admin-shell/__tests__/overview-allowlist.test.ts +43 -0
- package/src/admin-shell/__tests__/overview-screens.boot.test.ts +35 -0
- package/src/admin-shell/constants.ts +11 -0
- package/src/admin-shell/feature.ts +135 -0
- package/src/admin-shell/i18n.ts +36 -0
- package/src/admin-shell/index.ts +17 -0
- package/src/admin-shell/overview-allowlist.ts +37 -0
- package/src/admin-shell/web/client-plugin.tsx +26 -0
- package/src/admin-shell/web/i18n.ts +16 -0
- package/src/admin-shell/web/index.ts +2 -0
- package/src/admin-shell/web/overview-layout.tsx +69 -0
- package/src/admin-shell/web/overview-query.ts +15 -0
- package/src/admin-shell/web/platform-overview-screen.tsx +90 -0
- package/src/admin-shell/web/tenant-overview-screen.tsx +90 -0
- package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
- package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
- package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
- package/src/agent-tools/index.ts +9 -0
- package/src/agent-tools/tool-catalog.ts +130 -0
- package/src/agent-tools/tool-dispatch.ts +82 -0
- package/src/agent-tools/types.ts +38 -0
- package/src/audit/__tests__/audit-screens.boot.test.ts +51 -0
- package/src/audit/__tests__/audit-security.integration.test.ts +201 -0
- package/src/audit/__tests__/audit.integration.test.ts +50 -25
- package/src/audit/constants.ts +9 -2
- package/src/audit/feature.ts +33 -1
- package/src/audit/handlers/details.query.ts +47 -0
- package/src/audit/handlers/list.query.ts +3 -2
- package/src/audit/i18n.ts +31 -0
- package/src/audit/web/audit-log-detail-screen.tsx +131 -0
- package/src/audit/web/audit-log-screen.tsx +250 -0
- package/src/audit/web/client-plugin.tsx +22 -0
- package/src/audit/web/i18n.ts +12 -0
- package/src/audit/web/index.ts +5 -0
- package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-lockout.integration.test.ts +4 -4
- package/src/auth-email-password/__tests__/account-unlock.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/auth-claims.integration.test.ts +10 -11
- package/src/auth-email-password/__tests__/auth-user-row.test.ts +36 -0
- package/src/auth-email-password/__tests__/auth.integration.test.ts +33 -6
- package/src/auth-email-password/__tests__/email-verification.integration.test.ts +9 -6
- package/src/auth-email-password/__tests__/feature-options.test.ts +33 -0
- package/src/auth-email-password/__tests__/identity-v3-login.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/invite-flow-kms.integration.test.ts +283 -0
- package/src/auth-email-password/__tests__/invite-flow.integration.test.ts +35 -1
- package/src/auth-email-password/__tests__/login-mfa-setup-required.integration.test.ts +129 -0
- package/src/auth-email-password/__tests__/multi-roles.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/password-policy.test.ts +15 -0
- package/src/auth-email-password/__tests__/password-reset.integration.test.ts +19 -6
- package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.test.ts +3 -3
- package/src/auth-email-password/__tests__/seed-admin.integration.test.ts +1 -1
- package/src/auth-email-password/__tests__/self-registration-toggle.integration.test.ts +264 -0
- package/src/auth-email-password/__tests__/session-callbacks.integration.test.ts +20 -4
- package/src/auth-email-password/__tests__/session-strict-mode.integration.test.ts +7 -14
- package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +91 -0
- package/src/auth-email-password/constants.ts +25 -0
- package/src/auth-email-password/email-templates.ts +32 -0
- package/src/auth-email-password/errors.ts +9 -0
- package/src/auth-email-password/feature.ts +59 -11
- package/src/auth-email-password/handlers/change-password.write.ts +14 -13
- package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +59 -0
- package/src/auth-email-password/handlers/confirm-token-flow.ts +34 -105
- package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +12 -5
- package/src/auth-email-password/handlers/invite-accept.write.ts +9 -2
- package/src/auth-email-password/handlers/invite-create.write.ts +2 -2
- package/src/auth-email-password/handlers/invite-signup-complete.write.ts +12 -4
- package/src/auth-email-password/handlers/login.write.ts +86 -16
- package/src/auth-email-password/handlers/logout.write.ts +4 -2
- package/src/auth-email-password/handlers/request-account-unlock.write.ts +38 -0
- package/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/src/auth-email-password/handlers/self-registration-status.query.ts +23 -0
- package/src/auth-email-password/handlers/signup-confirm.write.ts +19 -11
- package/src/auth-email-password/handlers/signup-request.write.ts +22 -0
- package/src/auth-email-password/handlers/token-request-handler.ts +4 -2
- package/src/auth-email-password/i18n.ts +52 -0
- package/src/auth-email-password/index.ts +13 -2
- package/src/auth-email-password/lockout-store.ts +10 -5
- package/src/auth-email-password/password-policy.ts +2108 -0
- package/src/auth-email-password/seeding.ts +55 -27
- package/src/auth-email-password/self-registration-toggle.ts +25 -0
- package/src/auth-email-password/signed-token.ts +1 -0
- package/src/auth-email-password/unlock-token.ts +28 -0
- package/src/auth-email-password/web/__tests__/auth-client.test.ts +542 -0
- package/src/auth-email-password/web/__tests__/auth-form-logic.test.ts +44 -0
- package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +194 -6
- package/src/auth-email-password/web/__tests__/client-plugin.test.tsx +34 -0
- package/src/auth-email-password/web/__tests__/confirm-account-unlock-screen.test.tsx +64 -0
- package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/invite-accept-screen.test.tsx +129 -2
- package/src/auth-email-password/web/__tests__/login-screen.test.tsx +149 -5
- package/src/auth-email-password/web/__tests__/request-account-unlock-screen.test.tsx +137 -0
- package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +5 -1
- package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +134 -0
- package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +93 -0
- package/src/auth-email-password/web/__tests__/test-utils.tsx +12 -2
- package/src/auth-email-password/web/__tests__/use-url-token.test.tsx +29 -0
- package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +5 -1
- package/src/auth-email-password/web/auth-client.ts +96 -18
- package/src/auth-email-password/web/auth-form-logic.ts +30 -0
- package/src/auth-email-password/web/auth-form-primitives.tsx +22 -20
- package/src/auth-email-password/web/auth-gate.tsx +140 -19
- package/src/auth-email-password/web/client-plugin.ts +24 -2
- package/src/auth-email-password/web/confirm-account-unlock-screen.tsx +95 -0
- package/src/auth-email-password/web/forgot-password-screen.tsx +6 -6
- package/src/auth-email-password/web/index.ts +16 -4
- package/src/auth-email-password/web/invite-accept-screen.tsx +5 -5
- package/src/auth-email-password/web/login-screen.tsx +78 -22
- package/src/auth-email-password/web/request-account-unlock-screen.tsx +112 -0
- package/src/auth-email-password/web/reset-password-screen.tsx +15 -18
- package/src/auth-email-password/web/session.tsx +11 -6
- package/src/auth-email-password/web/signup-complete-screen.tsx +13 -15
- package/src/auth-email-password/web/signup-screen.tsx +8 -10
- package/src/auth-email-password/web/use-shell-user.ts +5 -2
- package/src/auth-email-password/web/user-menu.tsx +3 -1
- package/src/auth-email-password/web/verify-email-screen.tsx +10 -14
- package/src/auth-foundation/__tests__/feature.test.ts +46 -0
- package/src/auth-foundation/__tests__/session-store.test.ts +77 -0
- package/src/auth-foundation/__tests__/tenant-providers.test.ts +160 -0
- package/src/auth-foundation/__tests__/token-verifier.test.ts +150 -0
- package/src/auth-foundation/boot-checks.ts +150 -0
- package/src/auth-foundation/feature.ts +157 -0
- package/src/auth-foundation/index.ts +37 -0
- package/src/auth-foundation/resolve-anonymous-access.ts +62 -0
- package/src/auth-foundation/types.ts +170 -0
- package/src/auth-mfa/__tests__/base32.test.ts +28 -0
- package/src/auth-mfa/__tests__/disable-and-regenerate.integration.test.ts +265 -0
- package/src/auth-mfa/__tests__/enable-confirm-preauth.integration.test.ts +321 -0
- package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +322 -0
- package/src/auth-mfa/__tests__/enable-start-preauth.integration.test.ts +237 -0
- package/src/auth-mfa/__tests__/errors-i18n-parity.test.ts +39 -0
- package/src/auth-mfa/__tests__/mfa-preauth-setup-token.test.ts +113 -0
- package/src/auth-mfa/__tests__/no-redis.integration.test.ts +209 -0
- package/src/auth-mfa/__tests__/pii-subject-encryption.integration.test.ts +139 -0
- package/src/auth-mfa/__tests__/reencrypt-job.integration.test.ts +247 -0
- package/src/auth-mfa/__tests__/session-auto-revoke.integration.test.ts +169 -0
- package/src/auth-mfa/__tests__/totp.test.ts +131 -0
- package/src/auth-mfa/__tests__/verify.integration.test.ts +319 -0
- package/src/auth-mfa/base32.ts +51 -0
- package/src/auth-mfa/config.ts +26 -0
- package/src/auth-mfa/constants.ts +45 -0
- package/src/auth-mfa/db/queries.ts +45 -0
- package/src/auth-mfa/errors.ts +66 -0
- package/src/auth-mfa/feature.ts +170 -0
- package/src/auth-mfa/handlers/disable.write.ts +67 -0
- package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +200 -0
- package/src/auth-mfa/handlers/enable-confirm.write.ts +106 -0
- package/src/auth-mfa/handlers/enable-start-preauth.write.ts +87 -0
- package/src/auth-mfa/handlers/enable-start.write.ts +66 -0
- package/src/auth-mfa/handlers/reencrypt.job.ts +234 -0
- package/src/auth-mfa/handlers/regenerate-recovery.write.ts +77 -0
- package/src/auth-mfa/handlers/status.query.ts +23 -0
- package/src/auth-mfa/handlers/verify.write.ts +179 -0
- package/src/auth-mfa/i18n.ts +12 -0
- package/src/auth-mfa/index.ts +19 -0
- package/src/auth-mfa/mfa-challenge-token.ts +90 -0
- package/src/auth-mfa/mfa-preauth-setup-token.ts +86 -0
- package/src/auth-mfa/mfa-setup-token.ts +96 -0
- package/src/auth-mfa/mfa-status-checker.ts +84 -0
- package/src/auth-mfa/mfa-verify-attempts.ts +90 -0
- package/src/auth-mfa/otpauth-uri.ts +21 -0
- package/src/auth-mfa/recovery-codes.ts +54 -0
- package/src/auth-mfa/schema/user-mfa.ts +60 -0
- package/src/auth-mfa/testing.ts +5 -0
- package/src/auth-mfa/totp.ts +67 -0
- package/src/auth-mfa/verify-factor.ts +58 -0
- package/src/auth-mfa/web/__tests__/mfa-client.test.ts +265 -0
- package/src/auth-mfa/web/__tests__/mfa-enable-screen.test.tsx +182 -0
- package/src/auth-mfa/web/__tests__/mfa-setup-preauth-screen.test.tsx +379 -0
- package/src/auth-mfa/web/__tests__/mfa-verify-screen.test.tsx +104 -0
- package/src/auth-mfa/web/client-plugin.ts +37 -0
- package/src/auth-mfa/web/i18n.ts +136 -0
- package/src/auth-mfa/web/index.ts +27 -0
- package/src/auth-mfa/web/mfa-client.ts +184 -0
- package/src/auth-mfa/web/mfa-disable-dialog.tsx +69 -0
- package/src/auth-mfa/web/mfa-enable-screen.tsx +228 -0
- package/src/auth-mfa/web/mfa-recovery-codes-reveal.tsx +53 -0
- package/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +68 -0
- package/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +267 -0
- package/src/auth-mfa/web/mfa-verify-screen.tsx +117 -0
- package/src/auth-mfa/web/qrcode-browser.d.ts +12 -0
- package/src/auth-mfa-user-data/__tests__/hooks.integration.test.ts +134 -0
- package/src/auth-mfa-user-data/hooks.ts +53 -0
- package/src/auth-mfa-user-data/index.ts +21 -0
- package/src/billing-foundation/__tests__/billing-foundation.integration.test.ts +206 -2
- package/src/billing-foundation/__tests__/subscription-provider-contract.ts +93 -0
- package/src/billing-foundation/entities.ts +28 -4
- package/src/billing-foundation/events.ts +6 -2
- package/src/billing-foundation/feature.ts +9 -1
- package/src/billing-foundation/get-subscription-for-tenant.ts +16 -5
- package/src/billing-foundation/handlers/create-portal-session.write.ts +17 -1
- package/src/billing-foundation/handlers/list-subscriptions.query.ts +15 -1
- package/src/billing-foundation/handlers/process-event.write.ts +39 -3
- package/src/billing-foundation/tenant-destroy-hook.ts +46 -0
- package/src/cap-counter/__tests__/cap-list.boot.test.ts +27 -0
- package/src/cap-counter/__tests__/cap-list.security.integration.test.ts +53 -0
- package/src/cap-counter/__tests__/entity-ddl.test.ts +15 -0
- package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +8 -5
- package/src/cap-counter/constants.ts +2 -0
- package/src/cap-counter/enforce-cap.ts +15 -18
- package/src/cap-counter/entity.ts +3 -3
- package/src/cap-counter/feature.ts +28 -1
- package/src/cap-counter/i18n.ts +12 -0
- package/src/cap-counter/with-cap-enforcement.ts +2 -2
- package/src/channel-email/__tests__/pii-guard.test.ts +56 -0
- package/src/channel-email/email-channel.ts +2 -1
- package/src/channel-email/index.ts +1 -0
- package/src/channel-email/pii-guard.ts +39 -0
- package/src/compliance-profiles/__tests__/profile-picker.boot.test.ts +28 -0
- package/src/compliance-profiles/__tests__/profile-picker.security.integration.test.ts +91 -0
- package/src/compliance-profiles/constants.ts +15 -0
- package/src/compliance-profiles/feature.ts +22 -1
- package/src/compliance-profiles/handlers/set-profile.write.ts +2 -2
- package/src/compliance-profiles/i18n.ts +6 -0
- package/src/compliance-profiles/index.ts +6 -0
- package/src/compliance-profiles/web/client-plugin.tsx +22 -0
- package/src/compliance-profiles/web/compliance-profile-screen.tsx +152 -0
- package/src/compliance-profiles/web/i18n.ts +25 -0
- package/src/compliance-profiles/web/index.ts +6 -0
- package/src/config/__tests__/app-overrides.test.ts +11 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +22 -0
- package/src/config/__tests__/cascade.integration.test.ts +35 -0
- package/src/config/__tests__/config.integration.test.ts +71 -10
- package/src/config/__tests__/deserialize-value.test.ts +5 -8
- package/src/config/__tests__/inherited-redaction.integration.test.ts +4 -3
- package/src/config/__tests__/pii-encrypted.integration.test.ts +179 -0
- package/src/config/__tests__/reencrypt-job-concurrent-write-race.integration.test.ts +234 -0
- package/src/config/__tests__/reencrypt-job-kek-rotation.integration.test.ts +243 -0
- package/src/config/__tests__/write-helpers.test.ts +23 -0
- package/src/config/db/queries/resolver.ts +3 -3
- package/src/config/feature.ts +15 -8
- package/src/config/handlers/cascade.query.ts +2 -1
- package/src/config/handlers/reencrypt.job.ts +209 -0
- package/src/config/handlers/set.write.ts +55 -3
- package/src/config/handlers/values.query.ts +2 -1
- package/src/config/i18n.ts +9 -0
- package/src/config/index.ts +5 -4
- package/src/config/resolver.ts +75 -61
- package/src/config/table.ts +1 -1
- package/src/config/write-helpers.ts +22 -0
- package/src/crypto-shredding/__tests__/forget-subject.integration.test.ts +148 -0
- package/src/crypto-shredding/constants.ts +5 -0
- package/src/crypto-shredding/feature.ts +18 -0
- package/src/crypto-shredding/handlers/forget-subject.write.ts +91 -0
- package/src/crypto-shredding/index.ts +11 -0
- package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
- package/src/custom-fields/__tests__/custom-fields.integration.test.ts +92 -53
- package/src/custom-fields/__tests__/field-access.integration.test.ts +25 -0
- package/src/custom-fields/__tests__/parse-serialized-field.test.ts +11 -3
- package/src/custom-fields/__tests__/retention.integration.test.ts +40 -0
- package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +26 -79
- package/src/custom-fields/constants.ts +1 -0
- package/src/custom-fields/db/queries/user-data-rights.ts +0 -20
- package/src/custom-fields/entity.ts +1 -1
- package/src/custom-fields/events.ts +4 -3
- package/src/custom-fields/feature.ts +8 -0
- package/src/custom-fields/handlers/delete-system-field.write.ts +2 -1
- package/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -1
- package/src/custom-fields/handlers/set-custom-field.write.ts +3 -44
- package/src/custom-fields/handlers/update-tenant-field.write.ts +0 -17
- package/src/custom-fields/lib/field-access.ts +8 -1
- package/src/custom-fields/lib/parse-serialized-field.ts +12 -4
- package/src/custom-fields/lib/value-schema.ts +1 -1
- package/src/custom-fields/run-retention.ts +9 -1
- package/src/custom-fields/schemas.ts +9 -9
- package/src/custom-fields/wire-for-entity.ts +110 -78
- package/src/custom-fields/wire-user-data-rights.ts +5 -54
- package/src/data-retention/__tests__/parse-override.test.ts +14 -13
- package/src/data-retention/__tests__/retention-cleanup.integration.test.ts +145 -8
- package/src/data-retention/feature.ts +3 -3
- package/src/data-retention/run-retention-cleanup.ts +102 -19
- package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +171 -0
- package/src/delivery/__tests__/delivery-screens.boot.test.ts +27 -0
- package/src/delivery/__tests__/delivery-security.integration.test.ts +106 -0
- package/src/delivery/constants.ts +13 -51
- package/src/delivery/feature.ts +29 -5
- package/src/delivery/handlers/log.query.ts +28 -7
- package/src/delivery/i18n.ts +6 -0
- package/src/delivery/index.ts +6 -1
- package/src/delivery/public-names.ts +36 -0
- package/src/delivery/tables.ts +3 -3
- package/src/delivery/web/client-plugin.tsx +20 -0
- package/src/delivery/web/delivery-log-screen.tsx +103 -0
- package/src/delivery/web/i18n.ts +25 -0
- package/src/delivery/web/index.ts +3 -0
- package/src/document-ingest-foundation/__tests__/feature.integration.test.ts +147 -0
- package/src/document-ingest-foundation/__tests__/feature.test.ts +105 -0
- package/src/document-ingest-foundation/__tests__/pages.test.ts +37 -0
- package/src/document-ingest-foundation/entity.ts +43 -0
- package/src/document-ingest-foundation/events.ts +25 -0
- package/src/document-ingest-foundation/feature.ts +126 -0
- package/src/document-ingest-foundation/index.ts +12 -0
- package/src/document-ingest-foundation/pages.ts +34 -0
- package/src/feature-toggles/__tests__/compose-tier-resolver.test.ts +304 -0
- package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +7 -6
- package/src/feature-toggles/__tests__/toggle-admin.boot.test.ts +31 -0
- package/src/feature-toggles/__tests__/toggle-admin.security.integration.test.ts +69 -0
- package/src/feature-toggles/compose-tier-resolver.ts +134 -0
- package/src/feature-toggles/constants.ts +14 -0
- package/src/feature-toggles/feature.ts +23 -3
- package/src/feature-toggles/global-feature-state-table.ts +20 -1
- package/src/feature-toggles/handlers/list.query.ts +1 -1
- package/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/src/feature-toggles/i18n.ts +6 -0
- package/src/feature-toggles/index.ts +8 -0
- package/src/feature-toggles/toggle-runtime.ts +14 -0
- package/src/feature-toggles/web/client-plugin.tsx +22 -0
- package/src/feature-toggles/web/i18n.ts +29 -0
- package/src/feature-toggles/web/index.ts +6 -0
- package/src/feature-toggles/web/toggle-admin-screen.tsx +136 -0
- package/src/file-foundation/__tests__/feature.test.ts +5 -5
- package/src/file-foundation/__tests__/file-foundation.integration.test.ts +4 -3
- package/src/file-foundation/feature.ts +10 -11
- package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +27 -2
- package/src/files-provider-s3/s3-provider.ts +12 -1
- package/src/folders/__tests__/folders.integration.test.ts +3 -0
- package/src/folders/web/__tests__/folder-manager.test.tsx +43 -2
- package/src/folders/web/__tests__/tree.test.ts +8 -0
- package/src/folders/web/folder-manager.tsx +2 -3
- package/src/folders/web/i18n.ts +0 -2
- package/src/folders/web/tree.ts +13 -2
- package/src/folders-user-data/__tests__/hooks.integration.test.ts +1 -0
- package/src/folders-user-data/index.ts +4 -4
- package/src/inbound-mail-foundation/__tests__/account-state.test.ts +30 -0
- package/src/inbound-mail-foundation/__tests__/connect-routes.integration.test.ts +307 -0
- package/src/inbound-mail-foundation/__tests__/feature.test.ts +169 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-foundation.integration.test.ts +644 -0
- package/src/inbound-mail-foundation/__tests__/inbound-mail-provider-contract.ts +111 -0
- package/src/inbound-mail-foundation/__tests__/retention.integration.test.ts +237 -0
- package/src/inbound-mail-foundation/__tests__/watch-supervisor.integration.test.ts +433 -0
- package/src/inbound-mail-foundation/aggregate-id.ts +40 -0
- package/src/inbound-mail-foundation/connect-routes.ts +230 -0
- package/src/inbound-mail-foundation/constants.ts +80 -0
- package/src/inbound-mail-foundation/db/queries/inbound-projections.ts +38 -0
- package/src/inbound-mail-foundation/entities.ts +172 -0
- package/src/inbound-mail-foundation/events.ts +147 -0
- package/src/inbound-mail-foundation/feature.ts +243 -0
- package/src/inbound-mail-foundation/handlers/account-state.ts +23 -0
- package/src/inbound-mail-foundation/handlers/connect-account.write.ts +112 -0
- package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +69 -0
- package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +333 -0
- package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +44 -0
- package/src/inbound-mail-foundation/handlers/list-messages.query.ts +58 -0
- package/src/inbound-mail-foundation/handlers/scope-visibility.ts +14 -0
- package/src/inbound-mail-foundation/handlers/update-account.write.ts +78 -0
- package/src/inbound-mail-foundation/index.ts +97 -0
- package/src/inbound-mail-foundation/oauth-state.ts +103 -0
- package/src/inbound-mail-foundation/projection.ts +152 -0
- package/src/inbound-mail-foundation/provider-factory.ts +53 -0
- package/src/inbound-mail-foundation/retention-sweep.ts +168 -0
- package/src/inbound-mail-foundation/tenant-destroy-hook.ts +93 -0
- package/src/inbound-mail-foundation/types.ts +250 -0
- package/src/inbound-mail-foundation/watch-supervisor.ts +482 -0
- package/src/inbound-provider-imap/__tests__/feature.test.ts +115 -0
- package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +302 -0
- package/src/inbound-provider-imap/__tests__/imap-live.integration.test.ts +206 -0
- package/src/inbound-provider-imap/__tests__/plugin-mocked.test.ts +358 -0
- package/src/inbound-provider-imap/credential-document.ts +51 -0
- package/src/inbound-provider-imap/feature.ts +283 -0
- package/src/inbound-provider-imap/imap-client.ts +158 -0
- package/src/inbound-provider-imap/index.ts +17 -0
- package/src/inbound-provider-inmemory/__tests__/feature.test.ts +52 -0
- package/src/inbound-provider-inmemory/feature.ts +154 -0
- package/src/inbound-provider-inmemory/index.ts +12 -0
- package/src/jobs/__tests__/entity-ddl.test.ts +15 -0
- package/src/jobs/__tests__/job-instant.test.ts +15 -0
- package/src/jobs/__tests__/job-system-user.integration.test.ts +4 -8
- package/src/jobs/__tests__/jobs-events.integration.test.ts +37 -1
- package/src/jobs/__tests__/jobs-pii-kms.integration.test.ts +120 -0
- package/src/jobs/__tests__/jobs-screens.boot.test.ts +36 -0
- package/src/jobs/__tests__/jobs-security.integration.test.ts +108 -0
- package/src/jobs/__tests__/reindex-entity-job.integration.test.ts +169 -0
- package/src/jobs/constants.ts +8 -1
- package/src/jobs/feature.ts +52 -8
- package/src/jobs/handlers/detail.query.ts +6 -0
- package/src/jobs/handlers/list.query.ts +8 -1
- package/src/jobs/handlers/projection-rebuild.job.ts +14 -4
- package/src/jobs/handlers/reindex-entity.job.ts +49 -0
- package/src/jobs/i18n.ts +37 -0
- package/src/jobs/job-instant.ts +9 -0
- package/src/jobs/job-run-table.ts +4 -4
- package/src/jobs/web/client-plugin.tsx +22 -0
- package/src/jobs/web/i18n.ts +12 -0
- package/src/jobs/web/index.ts +4 -0
- package/src/jobs/web/job-run-detail-screen.tsx +180 -0
- package/src/jobs/web/job-runs-screen.tsx +146 -0
- package/src/ledger/__tests__/ledger.integration.test.ts +8 -3
- package/src/ledger/handlers/confirm-schedule-period.write.ts +17 -4
- package/src/ledger/recurring.ts +17 -8
- package/src/ledger/web/index.ts +3 -2
- package/src/legal-pages/__tests__/legal-pages.integration.test.ts +55 -0
- package/src/legal-pages/feature.ts +24 -31
- package/src/legal-pages/index.ts +1 -0
- package/src/mail-foundation/__tests__/feature.test.ts +5 -6
- package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +4 -3
- package/src/mail-foundation/__tests__/mail-transport-contract.ts +58 -0
- package/src/mail-foundation/feature.ts +26 -24
- package/src/mail-transport-inmemory/__tests__/feature.test.ts +23 -0
- package/src/mail-transport-smtp/__tests__/feature.test.ts +65 -0
- package/src/managed-pages/__tests__/branding-coerce.test.ts +2 -6
- package/src/managed-pages/__tests__/i18n-surface.boot.test.ts +30 -0
- package/src/managed-pages/__tests__/managed-pages.integration.test.ts +137 -0
- package/src/managed-pages/feature.ts +45 -47
- package/src/managed-pages/handlers/branding.query.ts +5 -1
- package/src/managed-pages/handlers/by-tenant-published.query.ts +52 -0
- package/src/managed-pages/i18n.ts +66 -0
- package/src/managed-pages/screens/page-screens.ts +5 -1
- package/src/managed-pages/web/client-plugin.tsx +22 -0
- package/src/managed-pages/web/i18n.ts +24 -0
- package/src/managed-pages/web/index.ts +3 -0
- package/src/page-render/__tests__/layout.test.ts +63 -0
- package/src/page-render/__tests__/markdown.test.ts +29 -0
- package/src/page-render/branding.ts +4 -3
- package/src/page-render/index.ts +1 -1
- package/src/page-render/layout.ts +18 -3
- package/src/page-render/markdown.ts +1 -9
- package/src/page-render/web.ts +13 -0
- package/src/personal-access-tokens/__tests__/feature.test.ts +42 -0
- package/src/personal-access-tokens/__tests__/pat.integration.test.ts +239 -0
- package/src/personal-access-tokens/__tests__/scopes.test.ts +42 -0
- package/src/personal-access-tokens/constants.ts +33 -0
- package/src/personal-access-tokens/feature.ts +105 -0
- package/src/personal-access-tokens/handlers/available-scopes.query.ts +21 -0
- package/src/personal-access-tokens/handlers/create.write.ts +47 -0
- package/src/personal-access-tokens/handlers/list.query.ts +50 -0
- package/src/personal-access-tokens/handlers/revoke.write.ts +37 -0
- package/src/personal-access-tokens/hash.ts +17 -0
- package/src/personal-access-tokens/i18n.ts +5 -0
- package/src/personal-access-tokens/index.ts +31 -0
- package/src/personal-access-tokens/resolver.ts +58 -0
- package/src/personal-access-tokens/roles.ts +29 -0
- package/src/personal-access-tokens/schema/api-token.ts +63 -0
- package/src/personal-access-tokens/scopes.ts +43 -0
- package/src/personal-access-tokens/web/client-plugin.tsx +29 -0
- package/src/personal-access-tokens/web/i18n.ts +69 -0
- package/src/personal-access-tokens/web/index.ts +7 -0
- package/src/personal-access-tokens/web/pat-tokens-screen.tsx +265 -0
- package/src/readiness/__tests__/readiness.integration.test.ts +4 -3
- package/src/renderer-simple/__tests__/resolve-variables.test.ts +37 -0
- package/src/renderer-simple/resolve-variables.ts +3 -1
- package/src/secrets/__tests__/rotate.integration.test.ts +115 -18
- package/src/secrets/handlers/rotate.job.ts +77 -98
- package/src/secrets/secrets-context.ts +7 -38
- package/src/secrets/table.ts +7 -8
- package/src/seo/__tests__/llms-txt.test.ts +38 -0
- package/src/seo/__tests__/robots-txt.test.ts +18 -0
- package/src/seo/__tests__/schema-builders.test.ts +57 -0
- package/src/seo/__tests__/seo.integration.test.ts +350 -0
- package/src/seo/__tests__/sitemap.test.ts +38 -0
- package/src/seo/constants.ts +60 -0
- package/src/seo/feature.ts +316 -0
- package/src/seo/handlers/seo-config.query.ts +35 -0
- package/src/seo/index.ts +19 -0
- package/src/seo/llms-txt.ts +33 -0
- package/src/seo/robots-txt.ts +13 -0
- package/src/seo/schema-builders.ts +57 -0
- package/src/seo/sitemap.ts +33 -0
- package/src/sessions/__tests__/auto-revoke-binding.test.ts +71 -0
- package/src/sessions/__tests__/cleanup.integration.test.ts +2 -1
- package/src/sessions/__tests__/password-auto-revoke.integration.test.ts +11 -7
- package/src/sessions/__tests__/rebuild-survival.integration.test.ts +18 -7
- package/src/sessions/__tests__/session-store-registration.test.ts +21 -0
- package/src/sessions/__tests__/sessions-screens.boot.test.ts +55 -0
- package/src/sessions/__tests__/sessions.integration.test.ts +272 -20
- package/src/sessions/__tests__/test-helpers.ts +16 -3
- package/src/sessions/constants.ts +5 -0
- package/src/sessions/db/queries/cleanup.ts +2 -2
- package/src/sessions/feature.ts +175 -26
- package/src/sessions/handlers/detail.query.ts +36 -0
- package/src/sessions/handlers/list.query.ts +14 -9
- package/src/sessions/handlers/mine.query.ts +13 -8
- package/src/sessions/handlers/revoke-all-for-user.write.ts +51 -12
- package/src/sessions/handlers/revoke-all-others.write.ts +19 -1
- package/src/sessions/handlers/revoke.write.ts +13 -0
- package/src/sessions/i18n.ts +23 -0
- package/src/sessions/index.ts +9 -2
- package/src/sessions/schema/user-session.ts +8 -2
- package/src/sessions/session-callbacks.ts +99 -14
- package/src/sessions/session-checker-fail-open.test.ts +77 -0
- package/src/sessions/session-revoked-event.ts +29 -0
- package/src/sessions/testing.ts +14 -1
- package/src/shared/__tests__/chunked-entity-migration.test.ts +145 -0
- package/src/shared/chunked-entity-migration.ts +89 -0
- package/src/shared/decrypt-stored-pii.ts +26 -0
- package/src/shared/encrypt-for-direct-write.ts +23 -0
- package/src/shared/entities-of.ts +17 -0
- package/src/shared/grace-period.test.ts +26 -0
- package/src/shared/grace-period.ts +20 -0
- package/src/{auth-email-password/__tests__ → shared}/identity-v3-hash.test.ts +2 -2
- package/src/shared/index.ts +16 -0
- package/src/shared/map-with-concurrency.test.ts +104 -0
- package/src/shared/map-with-concurrency.ts +38 -0
- package/src/shared/password-hashing.test.ts +35 -0
- package/src/{auth-email-password → shared}/password-hashing.ts +11 -0
- package/src/shared/system-query.ts +5 -0
- package/src/{auth-email-password → shared}/token-burn-store.ts +1 -1
- package/src/subscription-mollie/__tests__/feature.test.ts +97 -0
- package/src/subscription-mollie/__tests__/mollie-foundation.integration.test.ts +12 -1
- package/src/subscription-mollie/__tests__/verify-webhook.test.ts +21 -0
- package/src/subscription-mollie/verify-webhook.ts +1 -0
- package/src/subscription-stripe/__tests__/feature.test.ts +127 -1
- package/src/subscription-stripe/__tests__/runtime.test.ts +14 -24
- package/src/subscription-stripe/__tests__/stripe-foundation.integration.test.ts +15 -5
- package/src/subscription-stripe/__tests__/verify-webhook.test.ts +20 -0
- package/src/subscription-stripe/feature.ts +2 -0
- package/src/subscription-stripe/verify-webhook.ts +3 -2
- package/src/tags/__tests__/schemas.test.ts +34 -0
- package/src/tags/__tests__/tags.integration.test.ts +12 -1
- package/src/tags/entity.ts +6 -1
- package/src/tags/feature.ts +2 -0
- package/src/tags/i18n.ts +5 -0
- package/src/tags/schemas.ts +9 -2
- package/src/tags/web/__tests__/tag-manager.test.tsx +333 -0
- package/src/tags/web/__tests__/tag-section.test.tsx +47 -1
- package/src/tags/web/tag-manager.tsx +1 -1
- package/src/tags/web/tag-section.tsx +6 -3
- package/src/template-resolver/testing.ts +2 -1
- package/src/tenant/__tests__/members-query-kms.integration.test.ts +98 -0
- package/src/tenant/__tests__/members-screens.boot.test.ts +51 -0
- package/src/tenant/__tests__/multi-tenant.integration.test.ts +1 -1
- package/src/tenant/__tests__/tenant-security.integration.test.ts +310 -0
- package/src/tenant/__tests__/tenant.integration.test.ts +8 -4
- package/src/tenant/constants.ts +5 -0
- package/src/tenant/feature.ts +19 -0
- package/src/tenant/handlers/active-tenant-ids.query.ts +8 -2
- package/src/tenant/handlers/cancel-invitation.write.ts +2 -2
- package/src/tenant/handlers/invitations.query.ts +21 -4
- package/src/tenant/handlers/members.query.ts +41 -6
- package/src/tenant/i18n.ts +28 -0
- package/src/tenant/index.ts +7 -2
- package/src/tenant/invitation-table.ts +3 -2
- package/src/tenant/schema/tenant.ts +27 -2
- package/src/tenant/screens.ts +3 -2
- package/src/tenant/seeding.ts +136 -3
- package/src/tenant/web/client-plugin.tsx +20 -0
- package/src/tenant/web/i18n.ts +43 -0
- package/src/tenant/web/index.ts +4 -0
- package/src/tenant/web/members-screen.tsx +270 -0
- package/src/tenant-lifecycle/__tests__/stages.test.ts +22 -0
- package/src/tenant-lifecycle/__tests__/tenant-lifecycle.integration.test.ts +471 -0
- package/src/tenant-lifecycle/boot-checks.ts +45 -0
- package/src/tenant-lifecycle/constants.ts +56 -0
- package/src/tenant-lifecycle/events.ts +46 -0
- package/src/tenant-lifecycle/feature.ts +112 -0
- package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +79 -0
- package/src/tenant-lifecycle/handlers/request-destruction.write.ts +83 -0
- package/src/tenant-lifecycle/index.ts +7 -0
- package/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +16 -0
- package/src/tenant-lifecycle/lifecycle-gate.ts +65 -0
- package/src/tenant-lifecycle/run-tenant-destroy.ts +368 -0
- package/src/tenant-lifecycle/stages.ts +206 -0
- package/src/tenant-settings/__tests__/tenant-settings.integration.test.ts +115 -0
- package/src/tenant-settings/config.ts +44 -0
- package/src/tenant-settings/constants.ts +16 -0
- package/src/tenant-settings/feature.ts +49 -0
- package/src/tenant-settings/index.ts +8 -0
- package/src/tenant-settings/tenant-defaults.ts +67 -0
- package/src/text-content/web/__tests__/editor-read-only.test.tsx +33 -6
- package/src/text-content/web/__tests__/group-blocks.test.ts +28 -3
- package/src/text-content/web/client-plugin.tsx +126 -104
- package/src/tier-engine/__tests__/auto-default-tier.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/resolver.integration.test.ts +1 -1
- package/src/tier-engine/__tests__/tier-engine.integration.test.ts +7 -4
- package/src/tier-engine/__tests__/trial-config-validation.test.ts +30 -0
- package/src/tier-engine/feature.ts +13 -6
- package/src/tier-engine/i18n.ts +8 -0
- package/src/tier-engine/web/tier-admin-screen.tsx +26 -19
- package/src/user/__tests__/stream-tenant-backfill.integration.test.ts +186 -0
- package/src/user/__tests__/user.integration.test.ts +26 -0
- package/src/user/constants.ts +1 -0
- package/src/user/db/queries/stream-tenant-backfill.ts +112 -0
- package/src/user/feature.ts +3 -0
- package/src/user/handlers/find-for-auth.query.ts +8 -6
- package/src/user/i18n.ts +20 -0
- package/src/user/index.ts +4 -0
- package/src/user/schema/user.ts +6 -2
- package/src/user/screens.ts +3 -15
- package/src/user/seeding.ts +8 -0
- package/src/user-data-rights/COMPLIANCE.md +21 -1
- package/src/user-data-rights/__tests__/anonymous-deletion-kms.integration.test.ts +145 -0
- package/src/user-data-rights/__tests__/anonymous-deletion.integration.test.ts +3 -0
- package/src/user-data-rights/__tests__/audit-log.integration.test.ts +67 -0
- package/src/user-data-rights/__tests__/boot-checks.test.ts +170 -0
- package/src/user-data-rights/__tests__/cross-data-matrix.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/download-by-token-resolver-trust.integration.test.ts +169 -0
- package/src/user-data-rights/__tests__/download.integration.test.ts +101 -33
- package/src/user-data-rights/__tests__/email-templates.test.ts +15 -0
- package/src/user-data-rights/__tests__/export-encrypted-fields.integration.test.ts +154 -0
- package/src/user-data-rights/__tests__/export-job-idempotency.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/export-job-list-cross-tenant.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/file-binary-forget-cleanup.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-binary-forget-failure.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-retention.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/file-storage-unification.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/forget-cleanup-hook-ordering.integration.test.ts +41 -62
- package/src/user-data-rights/__tests__/forget-hook-registry.integration.test.ts +110 -0
- package/src/user-data-rights/__tests__/inspector-screens.boot.test.ts +14 -0
- package/src/user-data-rights/__tests__/mail-default-bridge.integration.test.ts +19 -2
- package/src/user-data-rights/__tests__/read-users-rebuild-survives-lifecycle.integration.test.ts +25 -14
- package/src/user-data-rights/__tests__/request-cancel-deletion.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/request-deletion-callback.integration.test.ts +4 -1
- package/src/user-data-rights/__tests__/request-export.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/restriction-flow.integration.test.ts +172 -19
- package/src/user-data-rights/__tests__/run-export-jobs-cron-context.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/run-export-jobs.integration.test.ts +257 -26
- package/src/user-data-rights/__tests__/run-forget-cleanup.integration.test.ts +339 -5
- package/src/user-data-rights/__tests__/run-user-export.integration.test.ts +77 -1
- package/src/user-data-rights/__tests__/tenant-model-erasure.integration.test.ts +2 -0
- package/src/user-data-rights/__tests__/user-data-rights.integration.test.ts +9 -1
- package/src/user-data-rights/boot-checks.ts +70 -0
- package/src/user-data-rights/db/queries/__tests__/forget-cleanup.integration.test.ts +69 -0
- package/src/user-data-rights/db/queries/forget-cleanup.ts +9 -2
- package/src/user-data-rights/email-templates.ts +6 -6
- package/src/user-data-rights/feature.ts +162 -43
- package/src/user-data-rights/handlers/cancel-deletion.write.ts +8 -11
- package/src/user-data-rights/handlers/deletion-grace-period.ts +4 -1
- package/src/user-data-rights/handlers/download-by-job.query.ts +26 -18
- package/src/user-data-rights/handlers/download-by-token.query.ts +23 -15
- package/src/user-data-rights/handlers/lift-restriction.write.ts +44 -19
- package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -0
- package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -0
- package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +3 -1
- package/src/user-data-rights/handlers/restrict-account.write.ts +58 -7
- package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +2 -0
- package/src/user-data-rights/i18n.ts +35 -32
- package/src/user-data-rights/lib/is-admin-actor.ts +19 -0
- package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
- package/src/user-data-rights/lib/update-user-lifecycle.ts +8 -24
- package/src/user-data-rights/run-export-jobs.ts +55 -19
- package/src/user-data-rights/run-forget-cleanup.ts +112 -7
- package/src/user-data-rights/run-user-export.ts +70 -4
- package/src/user-data-rights/schema/download-token.ts +5 -3
- package/src/user-data-rights/schema/export-job.ts +2 -0
- package/src/user-data-rights/screens.ts +2 -1
- package/src/user-data-rights/web/__tests__/privacy-center-screen.test.tsx +1 -0
- package/src/user-data-rights/web/__tests__/public-deletion-gate.test.tsx +7 -1
- package/src/user-data-rights/web/client-plugin.tsx +6 -1
- package/src/user-data-rights/web/i18n.ts +30 -10
- package/src/user-data-rights/web/privacy-center-screen.tsx +5 -3
- package/src/user-data-rights-defaults/__tests__/bundled-entities.userdata-hooks.integration.test.ts +455 -0
- package/src/user-data-rights-defaults/__tests__/feature-mounted.test.ts +24 -0
- package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.test.ts +33 -9
- package/src/user-data-rights-defaults/feature.ts +78 -1
- package/src/user-data-rights-defaults/hooks/api-token.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/config-value.userdata-hook.ts +51 -0
- package/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +37 -0
- package/src/user-data-rights-defaults/hooks/feature-mounted.ts +10 -0
- package/src/user-data-rights-defaults/hooks/in-app-message.userdata-hook.ts +38 -0
- package/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +35 -0
- package/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +54 -0
- package/src/user-data-rights-defaults/hooks/tenant-invitation.userdata-hook.ts +112 -0
- package/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +42 -0
- package/src/user-data-rights-defaults/index.ts +26 -0
- package/src/user-profile/__tests__/change-email.integration.test.ts +7 -1
- package/src/user-profile/__tests__/profile-screen.test.tsx +5 -0
- package/src/user-profile/feature.ts +4 -1
- package/src/user-profile/handlers/change-email.write.ts +15 -10
- package/src/user-profile/web/profile-screen.tsx +15 -3
- package/src/auth-email-password/stream-tenant.ts +0 -31
- package/src/feature-toggles/db/queries/toggle-state.ts +0 -25
- package/src/legal-pages/security-headers.ts +0 -1
- package/src/sessions/schema/index.ts +0 -5
- package/src/tenant/schema/index.ts +0 -5
- package/src/user/schema/index.ts +0 -5
- /package/src/{auth-email-password → shared}/identity-v3-hash.ts +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
1
2
|
import {
|
|
3
|
+
buildSessionRoles,
|
|
2
4
|
createSystemUser,
|
|
3
5
|
defineWriteHandler,
|
|
4
6
|
type SessionUser,
|
|
5
|
-
stripForbiddenMembershipRoles,
|
|
6
7
|
type TenantId,
|
|
8
|
+
type WriteResult,
|
|
7
9
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
10
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
9
11
|
import { z } from "zod";
|
|
12
|
+
import { verifyDummyPassword, verifyPassword } from "../../shared";
|
|
10
13
|
import { USER_STATUS, UserQueries } from "../../user";
|
|
11
14
|
import { parseAuthUserRow } from "../auth-user-row";
|
|
12
15
|
import {
|
|
@@ -21,7 +24,6 @@ import {
|
|
|
21
24
|
noMembership,
|
|
22
25
|
} from "../errors";
|
|
23
26
|
import { clearLockoutState, getLockoutState, recordFailedAttempt } from "../lockout-store";
|
|
24
|
-
import { verifyPassword } from "../password-hashing";
|
|
25
27
|
|
|
26
28
|
export type LoginHandlerOptions = {
|
|
27
29
|
// When true, a valid (email + password) login fails with email_not_verified
|
|
@@ -40,10 +42,36 @@ export type LoginHandlerOptions = {
|
|
|
40
42
|
readonly maxFailedAttempts?: number;
|
|
41
43
|
readonly lockoutDurationMinutes?: number;
|
|
42
44
|
};
|
|
45
|
+
// Optional second-factor gate. auth-mfa (if mounted) wires this in at
|
|
46
|
+
// app-composition time. Deliberately generic here — auth-email-password
|
|
47
|
+
// must not import auth-mfa's config, only this shape. Called AFTER a
|
|
48
|
+
// successful password verification (never before — the point is to add
|
|
49
|
+
// a second factor to a real credential match, not to gate on identity
|
|
50
|
+
// alone).
|
|
51
|
+
readonly mfaStatusChecker?: (
|
|
52
|
+
ctx: HandlerContext,
|
|
53
|
+
userId: string,
|
|
54
|
+
tenantId: TenantId,
|
|
55
|
+
// Merged global+tenant roles, computed by this handler BEFORE the
|
|
56
|
+
// gate runs — needed for policy values like "admins" that key off role.
|
|
57
|
+
roles: readonly string[],
|
|
58
|
+
) => Promise<
|
|
59
|
+
| { readonly required: false }
|
|
60
|
+
| { readonly required: true; readonly challengeToken: string }
|
|
61
|
+
| { readonly setupRequired: true; readonly preauthSetupToken: string }
|
|
62
|
+
>;
|
|
43
63
|
};
|
|
44
64
|
|
|
45
65
|
const SYSTEM_USER_ID = "00000000-0000-4000-8000-000000000000";
|
|
46
66
|
|
|
67
|
+
// Two possible success shapes: a straight login mints a session; a login
|
|
68
|
+
// gated by MFA hands back a challenge instead — auth-routes.ts branches on
|
|
69
|
+
// `kind` to decide whether to mint a JWT now or wait for /auth/mfa/verify.
|
|
70
|
+
type LoginResult =
|
|
71
|
+
| { readonly kind: "auth-session"; readonly session: SessionUser }
|
|
72
|
+
| { readonly kind: "mfa-challenge"; readonly challengeToken: string }
|
|
73
|
+
| { readonly kind: "mfa-setup-required"; readonly preauthSetupToken: string };
|
|
74
|
+
|
|
47
75
|
// Login — unauthenticated entry point. The route is wired public (no JWT
|
|
48
76
|
// middleware), synthesising a guest SessionUser for the handler's access
|
|
49
77
|
// check. Everything inside the handler goes through ctx.queryAs(system, ...)
|
|
@@ -62,7 +90,7 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
62
90
|
password: z.string().min(1),
|
|
63
91
|
}),
|
|
64
92
|
access: { roles: ["all"] },
|
|
65
|
-
handler: async (event, ctx) => {
|
|
93
|
+
handler: async (event, ctx): Promise<WriteResult<LoginResult>> => {
|
|
66
94
|
const systemUser = createSystemUser(SYSTEM_USER_ID);
|
|
67
95
|
|
|
68
96
|
const found = parseAuthUserRow(
|
|
@@ -74,6 +102,9 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
74
102
|
// Uniform response on any credential mismatch (no user, wrong password,
|
|
75
103
|
// soft-deleted user) — prevents email enumeration.
|
|
76
104
|
if (!found?.passwordHash || found.isDeleted) {
|
|
105
|
+
// Burn the same argon2 verify cost as the hit path so response
|
|
106
|
+
// latency doesn't reveal whether the email is registered (#774).
|
|
107
|
+
await verifyDummyPassword(event.payload.password);
|
|
77
108
|
return invalidCredentials();
|
|
78
109
|
}
|
|
79
110
|
|
|
@@ -82,6 +113,14 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
82
113
|
// for a timing-oracle on the bcrypt verify). If Redis isn't wired,
|
|
83
114
|
// lockout is silently skipped — login still works, brute-force
|
|
84
115
|
// protection just degrades to the IP-rate-limiter at the edge.
|
|
116
|
+
//
|
|
117
|
+
// Deliberately fail-open here, unlike auth-mfa's enable-confirm-preauth
|
|
118
|
+
// (which fails closed without Redis): the secret guarded by THIS gate
|
|
119
|
+
// is a full password, not a 6-digit code — locking out every login
|
|
120
|
+
// app-wide because Redis is briefly unavailable is a self-inflicted
|
|
121
|
+
// outage across every tenant, for a backstop that the IP-rate-limiter
|
|
122
|
+
// still partially covers. auth-mfa's blast radius is one user's MFA
|
|
123
|
+
// enrollment, not global login availability — different tradeoff.
|
|
85
124
|
if (ctx.redis) {
|
|
86
125
|
const state = await getLockoutState(ctx.redis, found.id);
|
|
87
126
|
if (state?.lockedUntil !== null && state?.lockedUntil !== undefined) {
|
|
@@ -107,6 +146,17 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
107
146
|
return invalidCredentials();
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
// Clear the lockout state as soon as the password is proven — the
|
|
150
|
+
// password itself is the thing this counter guards, and MFA (if
|
|
151
|
+
// gated below) has its own separate attempt-cap. Doing this before
|
|
152
|
+
// the MFA gate matters: without it, an MFA user who occasionally
|
|
153
|
+
// mistypes their password accumulates failures across otherwise-
|
|
154
|
+
// successful logins and eventually gets password-locked out even
|
|
155
|
+
// though every login they completed was legitimate.
|
|
156
|
+
if (ctx.redis) {
|
|
157
|
+
await clearLockoutState(ctx.redis, found.id);
|
|
158
|
+
}
|
|
159
|
+
|
|
110
160
|
// Strict verification gate — runs AFTER password check so an attacker
|
|
111
161
|
// probing "email_not_verified" needs valid credentials first. The
|
|
112
162
|
// remaining enumeration surface is "valid-cred + unverified" → accepted
|
|
@@ -152,25 +202,45 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
152
202
|
return noMembership();
|
|
153
203
|
}
|
|
154
204
|
|
|
155
|
-
// Clear the lockout state on success. DEL is idempotent, so no need
|
|
156
|
-
// to gate on "was there a counter?" — skipping the Redis round-trip
|
|
157
|
-
// entirely for users who never failed a login would optimise the hot
|
|
158
|
-
// path, but the call is microseconds and the branch isn't free either.
|
|
159
|
-
if (ctx.redis) {
|
|
160
|
-
await clearLockoutState(ctx.redis, found.id);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
205
|
// Globale Rollen aus user.roles + tenant-membership-roles mergen.
|
|
164
206
|
// Globale Rollen (SystemAdmin etc.) bleiben so über alle tenants
|
|
165
207
|
// gleich; tenant-spezifische Rollen (Admin, User) kommen aus der
|
|
166
208
|
// membership. Dedupe via Set damit eine Rolle die in beiden Quellen
|
|
167
209
|
// steht nicht doppelt im Session-Roles landet.
|
|
210
|
+
//
|
|
211
|
+
// Computed BEFORE the MFA gate (moved up from its original spot below
|
|
212
|
+
// baseSession) because the gate's "admins" enforcement policy needs
|
|
213
|
+
// the MERGED set — a SystemAdmin whose admin-ness lives only in
|
|
214
|
+
// globalRoles would be missed if only chosen.roles were passed.
|
|
168
215
|
const globalRoles = parseRoles(found.roles ?? null);
|
|
169
|
-
//
|
|
170
|
-
// SystemAdmin) — read-time backstop against a
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
216
|
+
// buildSessionRoles calls stripForbiddenMembershipRoles to strip reserved
|
|
217
|
+
// only (globalRoles keeps SystemAdmin) — read-time backstop against a
|
|
218
|
+
// rebuild-resurrected role.
|
|
219
|
+
const mergedRoles = buildSessionRoles(globalRoles, chosen.roles);
|
|
220
|
+
|
|
221
|
+
// Second-factor gate. Runs AFTER password verification (correct
|
|
222
|
+
// credentials proven) and AFTER tenant resolution (need chosen.tenantId
|
|
223
|
+
// to scope the MFA-enabled check). Three outcomes: enrolled → mint a
|
|
224
|
+
// challenge instead of a session (/auth/mfa/verify completes the
|
|
225
|
+
// login); policy demands MFA but the user never enrolled → block with
|
|
226
|
+
// mfa-setup-required (no session, no challenge — see auth-mfa's
|
|
227
|
+
// config.ts for why this deliberately hard-blocks); neither → proceed.
|
|
228
|
+
if (opts.mfaStatusChecker) {
|
|
229
|
+
const mfaStatus = await opts.mfaStatusChecker(ctx, found.id, chosen.tenantId, mergedRoles);
|
|
230
|
+
if ("challengeToken" in mfaStatus) {
|
|
231
|
+
return {
|
|
232
|
+
isSuccess: true,
|
|
233
|
+
data: { kind: "mfa-challenge", challengeToken: mfaStatus.challengeToken },
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if ("setupRequired" in mfaStatus) {
|
|
237
|
+
return {
|
|
238
|
+
isSuccess: true,
|
|
239
|
+
data: { kind: "mfa-setup-required", preauthSetupToken: mfaStatus.preauthSetupToken },
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
174
244
|
const baseSession: SessionUser = {
|
|
175
245
|
id: found.id,
|
|
176
246
|
tenantId: chosen.tenantId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, defineWriteHandler,
|
|
1
|
+
import { access, defineWriteHandler, stepsPipeline } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
// Logout — JWT is stateless, so server-side we only return OK. A future
|
|
@@ -8,5 +8,7 @@ export const logoutWrite = defineWriteHandler({
|
|
|
8
8
|
name: "logout",
|
|
9
9
|
schema: z.object({}),
|
|
10
10
|
access: { roles: access.authenticated },
|
|
11
|
-
perform:
|
|
11
|
+
perform: stepsPipeline(({ r }) => [
|
|
12
|
+
r.step.return({ isSuccess: true, data: { kind: "logged-out" } }),
|
|
13
|
+
]),
|
|
12
14
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AUTH_UNLOCK_DEFAULT_TTL_MINUTES, AuthErrors } from "../constants";
|
|
2
|
+
import { renderUnlockAccountEmail } from "../email-templates";
|
|
3
|
+
import { signUnlockToken } from "../unlock-token";
|
|
4
|
+
import {
|
|
5
|
+
createTokenRequestHandler,
|
|
6
|
+
type TokenRequestData,
|
|
7
|
+
type TokenRequestOptions,
|
|
8
|
+
} from "./token-request-handler";
|
|
9
|
+
|
|
10
|
+
const UNLOCK_NOTIFICATION_TYPE = "auth-email-password:account-unlock";
|
|
11
|
+
|
|
12
|
+
export type RequestAccountUnlockOptions = TokenRequestOptions;
|
|
13
|
+
|
|
14
|
+
// Public shape re-exported for callers that build custom routes on top of
|
|
15
|
+
// the dispatcher (bypassing the framework's auth-routes).
|
|
16
|
+
export type RequestUnlockData = TokenRequestData<"unlock-requested">;
|
|
17
|
+
|
|
18
|
+
// @wrapper-known semantic-alias
|
|
19
|
+
export function createRequestAccountUnlockHandler(opts: RequestAccountUnlockOptions) {
|
|
20
|
+
return createTokenRequestHandler(
|
|
21
|
+
{
|
|
22
|
+
handlerName: "request-account-unlock",
|
|
23
|
+
successKind: "unlock-requested",
|
|
24
|
+
defaultTtlMinutes: AUTH_UNLOCK_DEFAULT_TTL_MINUTES,
|
|
25
|
+
sign: signUnlockToken,
|
|
26
|
+
notConfiguredError: AuthErrors.unlockNotConfigured,
|
|
27
|
+
notConfiguredI18nKey: "auth.errors.unlockNotConfigured",
|
|
28
|
+
// No extra skip condition — any existing, non-deleted user can
|
|
29
|
+
// request an unlock link regardless of whether they're actually
|
|
30
|
+
// locked right now. Confirming just clears the (possibly already
|
|
31
|
+
// empty) lockout state — harmless no-op if they weren't locked.
|
|
32
|
+
extraSilentSkip: () => false,
|
|
33
|
+
notificationType: UNLOCK_NOTIFICATION_TYPE,
|
|
34
|
+
renderContent: renderUnlockAccountEmail,
|
|
35
|
+
},
|
|
36
|
+
opts,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { hashPassword } from "../../shared";
|
|
4
5
|
import { AuthErrors } from "../constants";
|
|
5
6
|
import { invalidResetToken } from "../errors";
|
|
6
|
-
import {
|
|
7
|
+
import { passwordSchema } from "../password-policy";
|
|
7
8
|
import { verifyResetToken } from "../reset-token";
|
|
8
9
|
import { runConfirmTokenFlow } from "./confirm-token-flow";
|
|
9
10
|
|
|
@@ -22,7 +23,7 @@ export function createResetPasswordHandler(opts: ResetPasswordOptions) {
|
|
|
22
23
|
name: "reset-password",
|
|
23
24
|
schema: z.object({
|
|
24
25
|
token: z.string().min(1),
|
|
25
|
-
newPassword:
|
|
26
|
+
newPassword: passwordSchema,
|
|
26
27
|
}),
|
|
27
28
|
access: { roles: ["all"] },
|
|
28
29
|
handler: async (event, ctx) => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { AUTH_SELF_REGISTRATION_FEATURE } from "../self-registration-toggle";
|
|
4
|
+
|
|
5
|
+
// Anonymous-readable status for the (unauthenticated) signup page: lets it
|
|
6
|
+
// hide its own link/form when an operator has flipped self-registration
|
|
7
|
+
// off, instead of collecting an email that signup-request will silently
|
|
8
|
+
// no-op on. Lives in auth-email-password itself (never toggleable) rather
|
|
9
|
+
// than on the auth-self-registration companion feature — the dispatcher's
|
|
10
|
+
// per-feature gate would otherwise make this query unreachable exactly when
|
|
11
|
+
// the toggle is off (same split as managed-pages' branding query vs.
|
|
12
|
+
// css-gate.ts).
|
|
13
|
+
export const selfRegistrationStatusQuery = defineQueryHandler({
|
|
14
|
+
name: "signup-registration-status",
|
|
15
|
+
schema: z.object({}),
|
|
16
|
+
access: { roles: ["anonymous", "User", "TenantAdmin", "SystemAdmin"] },
|
|
17
|
+
handler: async (_query, ctx) => {
|
|
18
|
+
// Same fail-open reasoning as signup-request.write.ts (#1468): only
|
|
19
|
+
// report disabled when the toggle feature is actually composed.
|
|
20
|
+
const gated = ctx.registry.getFeature(AUTH_SELF_REGISTRATION_FEATURE) !== undefined;
|
|
21
|
+
return { enabled: gated ? await ctx.hasFeature(AUTH_SELF_REGISTRATION_FEATURE) : true };
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -33,6 +33,7 @@ import { z } from "zod";
|
|
|
33
33
|
// kumiko-lint-ignore cross-feature-import signup-confirm reads tenants.key for slug-uniqueness check (TOCTOU + DB-unique-index zusammen)
|
|
34
34
|
import { tenantTable } from "../../tenant/schema/tenant";
|
|
35
35
|
import { invalidSignupToken, signupEmailAlreadyRegistered } from "../errors";
|
|
36
|
+
import { passwordSchema } from "../password-policy";
|
|
36
37
|
// kumiko-lint-ignore cross-feature-import provisioning needs cross-feature seeding helpers
|
|
37
38
|
import { INITIAL_SIGNUP_ROLES, provisionSignupAccount } from "../seeding";
|
|
38
39
|
import {
|
|
@@ -44,7 +45,7 @@ import {
|
|
|
44
45
|
|
|
45
46
|
const SignupConfirmSchema = z.object({
|
|
46
47
|
token: z.string().min(8),
|
|
47
|
-
password:
|
|
48
|
+
password: passwordSchema,
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
// Mirror der login-handler-Shape (kind: "auth-session", session: SessionUser)
|
|
@@ -108,16 +109,23 @@ export function createSignupConfirmHandler() {
|
|
|
108
109
|
|
|
109
110
|
let provisioned: { readonly userId: string; readonly tenantId: TenantId };
|
|
110
111
|
try {
|
|
111
|
-
provisioned = await provisionSignupAccount(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
provisioned = await provisionSignupAccount(
|
|
113
|
+
dbConn,
|
|
114
|
+
{
|
|
115
|
+
email,
|
|
116
|
+
password: event.payload.password,
|
|
117
|
+
displayName,
|
|
118
|
+
tenantId,
|
|
119
|
+
tenantKey,
|
|
120
|
+
// Tenant-Display-Name als Default = Email. User wechselt das im
|
|
121
|
+
// Settings-Screen. Konzept "Tenant" leakt nicht in die Signup-UI.
|
|
122
|
+
tenantName: email,
|
|
123
|
+
},
|
|
124
|
+
// #1463: seedTenant's postSave hooks (tier-engine's auto-default-
|
|
125
|
+
// tier, an app's auto-default-compliance) must fire on self-signup
|
|
126
|
+
// exactly like they do on the regular TenantHandlers.create path.
|
|
127
|
+
{ registry: ctx.registry, context: ctx },
|
|
128
|
+
);
|
|
121
129
|
} catch (err) {
|
|
122
130
|
// Email hat bereits ein Konto — provisionSignupAccount ist create-only
|
|
123
131
|
// (#365): sauberer User-Fehler, KEINE Session für den fremden Account.
|
|
@@ -31,6 +31,7 @@ import { AUTH_SIGNUP_DEFAULT_TTL_MINUTES } from "../constants";
|
|
|
31
31
|
import type { AuthMailLocale } from "../email-templates";
|
|
32
32
|
import { renderActivationEmail } from "../email-templates";
|
|
33
33
|
import { dispatchMagicLinkMail } from "../magic-link-mail";
|
|
34
|
+
import { AUTH_SELF_REGISTRATION_FEATURE } from "../self-registration-toggle";
|
|
34
35
|
import { getTokenForSignupEmail, normalizeEmail, storeSignupToken } from "../signup-token-store";
|
|
35
36
|
|
|
36
37
|
const SIGNUP_NOTIFICATION_TYPE = "auth-email-password:signup-activation";
|
|
@@ -68,6 +69,27 @@ export function createSignupRequestHandler(opts: SignupRequestOptions) {
|
|
|
68
69
|
schema: SignupRequestSchema,
|
|
69
70
|
access: { roles: ["all"] },
|
|
70
71
|
handler: async (event, ctx) => {
|
|
72
|
+
// Silent no-op when off, matching the route's own always-200
|
|
73
|
+
// anti-enumeration contract (registerTokenRequestRoute swallows every
|
|
74
|
+
// handler failure into `{isSuccess:true}` regardless) — no mail goes
|
|
75
|
+
// out, but the caller can't distinguish "disabled" from "unknown
|
|
76
|
+
// email" either way. The client-visible signal is the `status` query
|
|
77
|
+
// on auth-self-registration, which the signup page uses to hide its
|
|
78
|
+
// own link/form instead of collecting input that silently no-ops.
|
|
79
|
+
// Fail-open when the companion toggle feature isn't composed at all —
|
|
80
|
+
// ctx.hasFeature() is fail-closed for *unregistered* names the moment
|
|
81
|
+
// feature-toggles/tier-engine is wired, which silently kills
|
|
82
|
+
// self-signup for every existing app that mounts auth-email-password
|
|
83
|
+
// without also composing createAuthSelfRegistrationToggleFeature()
|
|
84
|
+
// (#1468). Only gate when the toggle actually exists.
|
|
85
|
+
const gated = ctx.registry.getFeature(AUTH_SELF_REGISTRATION_FEATURE) !== undefined;
|
|
86
|
+
if (gated && !(await ctx.hasFeature(AUTH_SELF_REGISTRATION_FEATURE))) {
|
|
87
|
+
// No email address here — this fires on every no-op request, PII
|
|
88
|
+
// must not accumulate in logs just for the "was this toggled off"
|
|
89
|
+
// signal a support ticket needs.
|
|
90
|
+
ctx.log?.info("signup-request skipped: self-registration disabled");
|
|
91
|
+
return { isSuccess: true, data: { kind: "no-op" } };
|
|
92
|
+
}
|
|
71
93
|
if (!ctx.redis) {
|
|
72
94
|
return writeFailure(
|
|
73
95
|
new InternalError({
|
|
@@ -125,7 +125,9 @@ export function createTokenRequestHandler<TName extends string, TSuccessKind ext
|
|
|
125
125
|
renderContent: spec.renderContent,
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
|
-
|
|
128
|
+
// Row value is ciphertext with an active KMS; the byte-exact
|
|
129
|
+
// lookup above guarantees the payload IS the stored plaintext.
|
|
130
|
+
email: event.payload.email,
|
|
129
131
|
appUrl: opts.appUrl,
|
|
130
132
|
token,
|
|
131
133
|
expiresAt: expiresAt.toString(),
|
|
@@ -136,7 +138,7 @@ export function createTokenRequestHandler<TName extends string, TSuccessKind ext
|
|
|
136
138
|
|
|
137
139
|
const data: TokenRequestData<TSuccessKind> = {
|
|
138
140
|
kind: spec.successKind,
|
|
139
|
-
email:
|
|
141
|
+
email: event.payload.email,
|
|
140
142
|
token,
|
|
141
143
|
expiresAt: expiresAt.toString(),
|
|
142
144
|
};
|
|
@@ -18,6 +18,7 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
18
18
|
"auth.login.submit": "Einloggen",
|
|
19
19
|
"auth.login.submitting": "…",
|
|
20
20
|
"auth.login.forgotPassword": "Passwort vergessen?",
|
|
21
|
+
"auth.login.unlockAccount": "Konto entsperren?",
|
|
21
22
|
"auth.login.resendVerification": "Bestätigungs-Mail erneut senden",
|
|
22
23
|
"auth.login.resendSuccess": "Wir haben dir eine neue Bestätigungs-Mail geschickt.",
|
|
23
24
|
"auth.login.resendRateLimited": "Bitte warte kurz und versuche es erneut.",
|
|
@@ -32,9 +33,15 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
32
33
|
"auth.errors.rateLimited": "Zu viele Login-Versuche. Bitte kurz warten.",
|
|
33
34
|
"auth.errors.invalidBody": "Ungültige Eingabe.",
|
|
34
35
|
"auth.errors.loginFailed": "Login fehlgeschlagen.",
|
|
36
|
+
"auth.errors.mfaNotSupported":
|
|
37
|
+
"Diese App unterstützt keine Zwei-Faktor-Bestätigung. Bitte an den Support wenden.",
|
|
38
|
+
"auth.errors.mfaSetupRequired":
|
|
39
|
+
"Zwei-Faktor-Authentifizierung erforderlich. Bitte an den Administrator wenden.",
|
|
35
40
|
"auth.errors.invalidResetToken":
|
|
36
41
|
"Der Link ist ungültig oder abgelaufen. Bitte fordere einen neuen an.",
|
|
37
42
|
"auth.errors.invalidVerificationToken": "Der Bestätigungs-Link ist ungültig oder abgelaufen.",
|
|
43
|
+
"auth.errors.invalidUnlockToken":
|
|
44
|
+
"Der Entsperren-Link ist ungültig oder abgelaufen. Bitte fordere einen neuen an.",
|
|
38
45
|
"auth.errors.invalidSignupToken":
|
|
39
46
|
"Der Aktivierungs-Link ist ungültig oder abgelaufen. Bitte fordere einen neuen an.",
|
|
40
47
|
"auth.errors.signupEmailAlreadyRegistered":
|
|
@@ -72,6 +79,26 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
72
79
|
"Der Link ist ungültig oder abgelaufen. Bitte fordere eine neue Bestätigungs-Mail an.",
|
|
73
80
|
"auth.verifyEmail.goToLogin": "Zum Login",
|
|
74
81
|
"auth.verifyEmail.missingToken": "Der Bestätigungs-Link enthält keinen Token.",
|
|
82
|
+
"auth.requestUnlock.title": "Konto entsperren",
|
|
83
|
+
"auth.requestUnlock.intro":
|
|
84
|
+
"Gib deine E-Mail-Adresse ein. Falls dein Konto gesperrt ist, schicken wir dir einen Entsperren-Link.",
|
|
85
|
+
"auth.requestUnlock.email": "E-Mail",
|
|
86
|
+
"auth.requestUnlock.submit": "Link anfordern",
|
|
87
|
+
"auth.requestUnlock.submitting": "…",
|
|
88
|
+
"auth.requestUnlock.successTitle": "Mail gesendet",
|
|
89
|
+
"auth.requestUnlock.successBody":
|
|
90
|
+
"Falls die E-Mail in unserem System existiert und gesperrt ist, ist eine Nachricht mit einem Entsperren-Link unterwegs. Bitte schau in deinen Posteingang.",
|
|
91
|
+
"auth.requestUnlock.backToLogin": "Zurück zum Login",
|
|
92
|
+
"auth.requestUnlock.rateLimited": "Zu viele Anfragen. Neuer Versuch in {minutes} Minuten.",
|
|
93
|
+
"auth.unlockAccount.verifying": "Konto wird entsperrt …",
|
|
94
|
+
"auth.unlockAccount.successTitle": "Konto entsperrt",
|
|
95
|
+
"auth.unlockAccount.successBody":
|
|
96
|
+
"Dein Konto ist wieder entsperrt. Du kannst dich jetzt anmelden.",
|
|
97
|
+
"auth.unlockAccount.errorTitle": "Entsperren fehlgeschlagen",
|
|
98
|
+
"auth.unlockAccount.errorBody":
|
|
99
|
+
"Der Link ist ungültig oder abgelaufen. Bitte fordere einen neuen Entsperren-Link an.",
|
|
100
|
+
"auth.unlockAccount.goToLogin": "Zum Login",
|
|
101
|
+
"auth.unlockAccount.missingToken": "Der Entsperren-Link enthält keinen Token.",
|
|
75
102
|
"auth.signup.title": "Account erstellen",
|
|
76
103
|
"auth.signup.intro":
|
|
77
104
|
"Gib deine E-Mail-Adresse ein. Wir schicken dir einen Aktivierungs-Link, mit dem du dein Passwort setzt.",
|
|
@@ -120,6 +147,7 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
120
147
|
"auth.login.submit": "Sign in",
|
|
121
148
|
"auth.login.submitting": "…",
|
|
122
149
|
"auth.login.forgotPassword": "Forgot password?",
|
|
150
|
+
"auth.login.unlockAccount": "Unlock account?",
|
|
123
151
|
"auth.login.resendVerification": "Send verification email again",
|
|
124
152
|
"auth.login.resendSuccess": "We've sent you a new verification email.",
|
|
125
153
|
"auth.login.resendRateLimited": "Please wait a moment and try again.",
|
|
@@ -134,8 +162,14 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
134
162
|
"auth.errors.rateLimited": "Too many login attempts. Please wait briefly.",
|
|
135
163
|
"auth.errors.invalidBody": "Invalid input.",
|
|
136
164
|
"auth.errors.loginFailed": "Login failed.",
|
|
165
|
+
"auth.errors.mfaNotSupported":
|
|
166
|
+
"This app doesn't support two-factor verification. Please contact support.",
|
|
167
|
+
"auth.errors.mfaSetupRequired":
|
|
168
|
+
"Two-factor authentication required. Please contact your administrator.",
|
|
137
169
|
"auth.errors.invalidResetToken": "Link is invalid or expired. Please request a new one.",
|
|
138
170
|
"auth.errors.invalidVerificationToken": "Verification link is invalid or expired.",
|
|
171
|
+
"auth.errors.invalidUnlockToken":
|
|
172
|
+
"Unlock link is invalid or expired. Please request a new one.",
|
|
139
173
|
"auth.errors.invalidSignupToken":
|
|
140
174
|
"Activation link is invalid or expired. Please request a new one.",
|
|
141
175
|
"auth.errors.signupEmailAlreadyRegistered":
|
|
@@ -172,6 +206,24 @@ export const defaultTranslations: TranslationsByLocale = {
|
|
|
172
206
|
"Link is invalid or expired. Please request a new verification email.",
|
|
173
207
|
"auth.verifyEmail.goToLogin": "Go to sign in",
|
|
174
208
|
"auth.verifyEmail.missingToken": "Verification link is missing a token.",
|
|
209
|
+
"auth.requestUnlock.title": "Unlock account",
|
|
210
|
+
"auth.requestUnlock.intro":
|
|
211
|
+
"Enter your email address. If your account is locked, we'll send you an unlock link.",
|
|
212
|
+
"auth.requestUnlock.email": "Email",
|
|
213
|
+
"auth.requestUnlock.submit": "Request link",
|
|
214
|
+
"auth.requestUnlock.submitting": "…",
|
|
215
|
+
"auth.requestUnlock.successTitle": "Email sent",
|
|
216
|
+
"auth.requestUnlock.successBody":
|
|
217
|
+
"If that email exists in our system and is locked, a message with an unlock link is on its way. Please check your inbox.",
|
|
218
|
+
"auth.requestUnlock.backToLogin": "Back to sign in",
|
|
219
|
+
"auth.requestUnlock.rateLimited": "Too many requests. Try again in {minutes} minutes.",
|
|
220
|
+
"auth.unlockAccount.verifying": "Unlocking account …",
|
|
221
|
+
"auth.unlockAccount.successTitle": "Account unlocked",
|
|
222
|
+
"auth.unlockAccount.successBody": "Your account is unlocked again. You can sign in now.",
|
|
223
|
+
"auth.unlockAccount.errorTitle": "Unlock failed",
|
|
224
|
+
"auth.unlockAccount.errorBody": "Link is invalid or expired. Please request a new unlock link.",
|
|
225
|
+
"auth.unlockAccount.goToLogin": "Go to sign in",
|
|
226
|
+
"auth.unlockAccount.missingToken": "Unlock link is missing a token.",
|
|
175
227
|
"auth.signup.title": "Create account",
|
|
176
228
|
"auth.signup.intro":
|
|
177
229
|
"Enter your email. We'll send you an activation link to set your password.",
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// Convention paths for the auth pages (reset/verify/signup/invite); apps build
|
|
2
2
|
// their magic-link appUrls from these. All mail now goes through delivery.
|
|
3
|
+
|
|
4
|
+
// Kept as a public re-export (not just internal call sites) — this feature's
|
|
5
|
+
// barrel is where consumers of the published package have always found
|
|
6
|
+
// hashPassword/verifyPassword; removing it is a breaking change that needs
|
|
7
|
+
// its own changeset + deliberate deprecation window, not a silent drop.
|
|
8
|
+
export { hashPassword, verifyPassword } from "../shared/password-hashing";
|
|
3
9
|
export { type AuthPaths, DEFAULT_AUTH_PATHS, makeAuthPaths } from "./auth-paths";
|
|
4
|
-
export { AUTH_EMAIL_PASSWORD_FEATURE, AuthErrors, AuthHandlers } from "./constants";
|
|
10
|
+
export { AUTH_EMAIL_PASSWORD_FEATURE, AuthErrors, AuthHandlers, AuthQueries } from "./constants";
|
|
5
11
|
// Renderers for the auth mails. All four magic-link flows (reset, verify,
|
|
6
12
|
// signup-activation, invite) emit structured AuthMailContent through delivery
|
|
7
13
|
// (ctx.notify).
|
|
@@ -16,10 +22,12 @@ export {
|
|
|
16
22
|
renderActivationEmail,
|
|
17
23
|
renderInviteEmail,
|
|
18
24
|
renderResetPasswordEmail,
|
|
25
|
+
renderUnlockAccountEmail,
|
|
19
26
|
renderVerifyEmail,
|
|
20
27
|
} from "./email-templates";
|
|
21
28
|
export type {
|
|
22
29
|
AccountLockoutOptions,
|
|
30
|
+
AccountUnlockOptions,
|
|
23
31
|
AuthEmailPasswordOptions,
|
|
24
32
|
EmailVerificationOptions,
|
|
25
33
|
InviteOptions,
|
|
@@ -27,7 +35,10 @@ export type {
|
|
|
27
35
|
SignupOptions,
|
|
28
36
|
} from "./feature";
|
|
29
37
|
export { authEmailPasswordEnvSchema, createAuthEmailPasswordFeature } from "./feature";
|
|
30
|
-
export {
|
|
38
|
+
export {
|
|
39
|
+
AUTH_SELF_REGISTRATION_FEATURE,
|
|
40
|
+
createAuthSelfRegistrationToggleFeature,
|
|
41
|
+
} from "./self-registration-toggle";
|
|
31
42
|
// Generic HMAC-signed single-purpose token helpers. Re-exported damit
|
|
32
43
|
// app-spezifische out-of-band-Flows (subscriber-confirm, magic-links,
|
|
33
44
|
// invite-tokens) denselben battle-tested signer/verifier nutzen können
|
|
@@ -31,9 +31,13 @@ export type LockoutState = {
|
|
|
31
31
|
// Consequence of the monotonic counter: once a user has been locked, the
|
|
32
32
|
// NEXT wrong password after the lock expires re-locks immediately — the
|
|
33
33
|
// INCR still returns a value ≥ threshold, so the SET NX re-arms the lock.
|
|
34
|
-
// A successful login is
|
|
35
|
-
//
|
|
36
|
-
//
|
|
34
|
+
// A successful login is one way to reset the streak; the other is the
|
|
35
|
+
// account-unlock magic-link flow (#1266, see
|
|
36
|
+
// handlers/confirm-account-unlock.write.ts), a deliberate escape hatch for
|
|
37
|
+
// a legitimate user who can't currently produce the right password (e.g.
|
|
38
|
+
// they forgot it too) but can prove mailbox ownership. Intentional:
|
|
39
|
+
// brute-force resistance favours strictness over UX for anonymous login
|
|
40
|
+
// attempts, while the unlock flow keeps the DoS from being permanent.
|
|
37
41
|
const COUNT_KEY_PREFIX = "kumiko:auth:lockout:count:";
|
|
38
42
|
const UNTIL_KEY_PREFIX = "kumiko:auth:lockout:until:";
|
|
39
43
|
|
|
@@ -111,8 +115,9 @@ export async function recordFailedAttempt(
|
|
|
111
115
|
return { failureCount: count, lockedUntil };
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
// Called on successful login
|
|
115
|
-
//
|
|
118
|
+
// Called on successful login, and on a confirmed account-unlock token
|
|
119
|
+
// (#1266). Idempotent — deleting missing keys is a no-op, so a replayed
|
|
120
|
+
// unlock link just re-clears harmlessly.
|
|
116
121
|
export async function clearLockoutState(redis: Redis, userId: string): Promise<void> {
|
|
117
122
|
await redis.del(countKey(userId), untilKey(userId));
|
|
118
123
|
}
|