@starci/hfs 5.2.0 → 5.4.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/CHANGELOG.md +43 -0
- package/README.md +2 -1
- package/cache/hfs-family.d.mts +2 -3
- package/cache/hfs-family.mjs +4 -9
- package/cache/secret-records.d.mts +7 -0
- package/cache/secret-records.mjs +12 -0
- package/cache/session.mjs +4 -2
- package/codemod/account-doors.d.mts +36 -0
- package/codemod/account-doors.mjs +134 -0
- package/codemod/app-prettier.d.mts +5 -0
- package/codemod/app-prettier.mjs +27 -0
- package/codemod/explicit-surface.mjs +1 -25
- package/codemod/main.mjs +35 -1
- package/codemod/module-definition.d.mts +59 -0
- package/codemod/module-definition.mjs +257 -0
- package/codemod/quality-binding.d.mts +33 -0
- package/codemod/quality-binding.mjs +38 -0
- package/codemod/secrets-cache.d.mts +35 -0
- package/codemod/secrets-cache.mjs +70 -0
- package/codemod/sops-rules.d.mts +34 -0
- package/codemod/sops-rules.mjs +69 -0
- package/codemod/theme-tokens.mjs +48 -2
- package/lint/run.d.mts +6 -0
- package/lint/run.mjs +63 -10
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +43 -10
- package/runtime/knowledge/hfs/fe-stack.yaml +3 -3
- package/runtime/knowledge/hfs/rules.yaml +13 -9
- package/runtime/knowledge/hfs/slots.yaml +105 -12
- package/runtime/modules/kernel/failure-codes.yaml +5 -4
- package/runtime/scripts/hfs/architecture/config.d.mts +4 -0
- package/runtime/scripts/hfs/architecture/config.mjs +25 -1
- package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +1 -1
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +6 -1
- package/runtime/scripts/hfs/architecture/root-packages.d.mts +26 -0
- package/runtime/scripts/hfs/architecture/root-packages.mjs +78 -0
- package/runtime/scripts/hfs/architecture/type-context.d.mts +4 -1
- package/runtime/scripts/hfs/architecture/type-context.mjs +30 -0
- package/runtime/scripts/hfs/architecture/typescript.d.mts +7 -1
- package/runtime/scripts/hfs/architecture/typescript.mjs +21 -1
- package/runtime/scripts/hfs/explicit-surface-rule.mjs +1 -1
- package/runtime/scripts/hfs/rules/database-access.mjs +39 -1
- package/runtime/scripts/hfs/rules/monorepo.d.mts +4 -0
- package/runtime/scripts/hfs/rules/monorepo.mjs +10 -5
- package/runtime/scripts/hfs/rules/secrets.mjs +11 -0
- package/runtime/scripts/hfs/secret-errors.d.mts +3 -0
- package/runtime/scripts/hfs/secret-errors.mjs +8 -0
- package/runtime/scripts/hfs/secret-init.d.mts +6 -0
- package/runtime/scripts/hfs/secret-init.mjs +23 -0
- package/runtime/scripts/hfs/secret-sops.mjs +9 -1
- package/runtime/scripts/hfs/secret-standard.d.mts +20 -0
- package/runtime/scripts/hfs/secret-standard.mjs +106 -0
- package/runtime/scripts/hfs/secret.mjs +5 -1
- package/runtime/scripts/hfs/view.d.mts +23 -0
- package/runtime/scripts/hfs/view.mjs +68 -0
- package/runtime/scripts/lib/declaration-surface.d.mts +4 -0
- package/runtime/scripts/lib/declaration-surface.mjs +55 -1
- package/runtime/scripts/lib/secret-records.d.mts +13 -0
- package/runtime/scripts/lib/secret-records.mjs +83 -0
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-queue-lite.mjs +1 -3
- package/scaffold/app.d.mts +2 -2
- package/scaffold/app.mjs +10 -12
- package/scaffold/lite-exports.mjs +1 -15
- package/scripts/sync-bundles.d.mts +8 -0
- package/stack/images/openclaw-runtime/Dockerfile +18 -0
- package/stack/images/openclaw-runtime/entrypoint.sh +23 -0
- package/stack/images/openclaw-runtime/openclaw-init.mjs +101 -0
- package/stack/images/openclaw-runtime/openclaw-relay.mjs +25 -0
- package/stack/local-up.mjs +3 -0
- package/stack/runtime-layers.d.mts +21 -0
- package/stack/runtime-layers.mjs +67 -0
- package/sync/compile-guard.d.mts +36 -0
- package/sync/compile-guard.mjs +189 -0
- package/sync/hygiene.mjs +3 -1
- package/sync/index.d.mts +11 -1
- package/sync/index.mjs +61 -10
- package/templates/app/auth-doors.sql.partial +115 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
- package/templates/app/package-scripts/core.json +1 -1
- package/templates/app/package-scripts/package.json +1 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +9 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +3 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/compose/compose.yaml +19 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +3 -0
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +5 -0
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +4 -0
- package/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql +27 -5
- package/templates/be/auth-lite/src/features/api/auth/application/auth.contracts.ts +33 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/confirm.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/forgot.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/resend.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/reset.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.command.ts +11 -0
- package/templates/be/auth-lite/src/features/api/auth/application/signup.handler.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/auth.module-definition.ts +3 -10
- package/templates/be/auth-lite/src/features/api/auth/auth.module.ts +6 -1
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts +3 -10
- package/templates/be/auth-lite/src/features/api/auth/transport/http/auth.controller.ts +56 -1
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/forgot.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/resend.request.ts +16 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/reset.request.ts +18 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/signup.request.ts +22 -0
- package/templates/be/auth-lite/src/features/api/auth/transport/http/dto/token.request.ts +11 -0
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.spec.ts +30 -30
- package/templates/be/auth-lite/src/modules/domain/identity/account-directory.service.ts +7 -16
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.spec.ts +113 -49
- package/templates/be/auth-lite/src/modules/domain/identity/authenticator.service.ts +42 -53
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.spec.ts +233 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-email.service.ts +176 -0
- package/templates/be/auth-lite/src/modules/domain/identity/identity-sign-in.log-events.ts +6 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/account-doors.port.ts +98 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-email-token.entity.ts +34 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/identity-refresh-token.entity.ts +4 -0
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/index.ts +3 -10
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.spec.ts +6 -3
- package/templates/be/auth-lite/src/modules/domain/identity/session-store.service.ts +6 -1
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.spec.ts +17 -0
- package/templates/be/auth-lite/src/modules/domain/identity/sign-in.service.ts +6 -0
- package/templates/be/auth-lite/src/tests/fixtures/builders/identity.builder.ts +29 -33
- package/templates/be/inbox/module-definition.ts.tpl +2 -9
- package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +2 -11
- package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/cli/group.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +3 -10
- package/templates/be/patterns/queues/queue.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/reactors/message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.sse.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +3 -9
- package/templates/be/patterns/saga/saga.module-definition.ts.tpl +2 -9
- package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +3 -9
- package/templates/be/platform/graphql/graphql.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +2 -9
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +3 -9
- package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +3 -13
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +4 -9
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +5 -11
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +4 -11
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +2 -9
- package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +2 -9
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +4 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +3 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +3 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/errors/identity.error.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +3 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity-mail.messages.ts +65 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/messages/identity.messages.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.config.ts +33 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.decorators.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module-definition.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.module.ts +27 -0
- package/templates/be/skeleton-lite/src/modules/integrations/mail/mail.options.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.module-definition.ts +2 -9
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/rate-limit.guard.spec.ts +11 -0
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +11 -0
- package/templates/be/table/api/feature.module-definition.ts.tpl +3 -9
- package/templates/be/table/api/graphql.module-definition.ts.tpl +3 -9
- package/templates/be/table/module-definition.ts.tpl +2 -9
- package/templates/fe/auth-lite/apps/web/src/modules/auth/email-doors.ts +16 -0
- package/templates/fe/auth-lite/apps/web/src/modules/auth/index.ts +5 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +30 -67
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +6 -21
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +10 -16
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/confirm/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/forgot/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/resend/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/reset/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/auth/signup/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +5 -9
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/component.tsx +51 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/EmailDoorForm/index.tsx +84 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppConfirmPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppForgotPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +11 -3
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +4 -1
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResendPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppResetPage/index.tsx +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppSignupPage/index.tsx +21 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/emailAuth.shared.ts +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/hooks/emailAuth/useEmailDoor.ts +32 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +50 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +7 -30
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +13 -14
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +4 -17
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-identity.entity.ts +0 -38
- package/templates/be/auth-lite/src/modules/domain/identity/persistence/entities/auth-user.entity.ts +0 -54
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# The dev environment's dependencies, run by `starci app stack up --env dev --execute` (PROJECT-STACKS-3, PROJECT-STACKS-6).
|
|
2
2
|
# Host ports come from environments.dev.local.ports through STACK_PORT_<NAME>; the superuser password is read from a file through the least
|
|
3
|
-
# grant STACK_GRANT_DB (a directory holding only POSTGRES_PASSWORD), never from the environment. Every port publishes on loopback only.
|
|
3
|
+
# grant STACK_GRANT_DB (a directory holding only POSTGRES_PASSWORD), never from the environment. Every port publishes on loopback only.
|
|
4
|
+
# `mail` is the dev mail sink (Mailpit, an unmodified upstream image pinned by digest in contexts/stack/pins.yaml): the api sends the
|
|
5
|
+
# confirmation and reset mails to its SMTP port, over a plain channel that exists on loopback only, and a person reads them in its web UI. The api and web run natively against these containers.
|
|
4
6
|
name: {{project}}-dev
|
|
5
7
|
services:
|
|
6
8
|
db:
|
|
@@ -21,5 +23,21 @@ services:
|
|
|
21
23
|
retries: 30
|
|
22
24
|
start_period: 10s
|
|
23
25
|
restart: unless-stopped
|
|
26
|
+
mail:
|
|
27
|
+
image: axllent/mailpit:v1.31.6@sha256:23fe39e81bde596a918000b3135c0f85dc8bb4c9ea6be274b051143f3e300387
|
|
28
|
+
environment:
|
|
29
|
+
MP_SMTP_BIND_ADDR: 0.0.0.0:1025
|
|
30
|
+
MP_UI_BIND_ADDR: 0.0.0.0:8025
|
|
31
|
+
MP_MAX_MESSAGES: "500"
|
|
32
|
+
ports:
|
|
33
|
+
- "127.0.0.1:${STACK_PORT_MAIL}:1025"
|
|
34
|
+
- "127.0.0.1:${STACK_PORT_MAILUI}:8025"
|
|
35
|
+
healthcheck:
|
|
36
|
+
test: ["CMD", "/mailpit", "readyz"]
|
|
37
|
+
interval: 5s
|
|
38
|
+
timeout: 5s
|
|
39
|
+
retries: 20
|
|
40
|
+
start_period: 5s
|
|
41
|
+
restart: unless-stopped
|
|
24
42
|
volumes:
|
|
25
43
|
db-data: {}
|
|
@@ -18,5 +18,8 @@ Names only. Dev values are generated once by the owner-armed `starci app secret
|
|
|
18
18
|
| `AUTH_JWT_PRIVATE_KEY` | API Ed25519 (EdDSA) signing key (member `auth`) |
|
|
19
19
|
| `STORAGE_ENDPOINT`, `STORAGE_REGION`, `STORAGE_BUCKET` | API storage address |
|
|
20
20
|
| `STORAGE_ACCESS_KEY_ID`, `STORAGE_SECRET_ACCESS_KEY` | API storage credentials (member `storage`) |
|
|
21
|
+
| `MAIL_HOST`, `MAIL_PORT`, `MAIL_SECURITY`, `MAIL_FROM`, `MAIL_FROM_NAME` | API outgoing mail server (the dev `mail` service, Mailpit, on its SMTP port: a plain channel on loopback only) and the sender of the confirmation and reset mails |
|
|
22
|
+
| `MAIL_DEFAULT_LANGUAGE`, `MAIL_CONFIRM_TTL_SECONDS`, `MAIL_RESET_TTL_SECONDS` | API mail language (`vi` or `en`) and link lifetimes; optional |
|
|
23
|
+
| `MAIL_USER`, `MAIL_PASSWORD` | API mail server login; optional, the password reads `MAIL_PASSWORD_FILE` |
|
|
21
24
|
| `NEXT_PUBLIC_SITE_URL`, `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_GRAPHQL_URL` | Front-end site, auth doors and GraphQL transport |
|
|
22
25
|
| `DATABASE_URL` | `starci app db` verbs; the stack verb composes it in memory for the migration |
|
|
@@ -17,6 +17,11 @@ services:
|
|
|
17
17
|
AUTH_JWT_KEY_ID: production-1
|
|
18
18
|
AUTH_COOKIE_DOMAIN: {{project}}.example.invalid
|
|
19
19
|
AUTH_JWT_PRIVATE_KEY_FILE: /run/secrets/auth-jwt-private-key
|
|
20
|
+
MAIL_HOST: smtp.{{project}}.example.invalid
|
|
21
|
+
MAIL_PORT: "587"
|
|
22
|
+
MAIL_SECURITY: starttls
|
|
23
|
+
MAIL_FROM: no-reply@{{project}}.example.invalid
|
|
24
|
+
MAIL_FROM_NAME: {{project}}
|
|
20
25
|
STORAGE_ENDPOINT: https://storage.{{project}}.example.invalid
|
|
21
26
|
STORAGE_REGION: us-east-1
|
|
22
27
|
STORAGE_BUCKET: {{project}}-production
|
|
@@ -23,6 +23,10 @@ This inventory declares names, consumers and mounted file paths. Scaffolding gen
|
|
|
23
23
|
| `STORAGE_BUCKET` | API storage bucket |
|
|
24
24
|
| `STORAGE_ACCESS_KEY_ID` | API storage credential; reads `STORAGE_ACCESS_KEY_ID_FILE` at `/run/secrets/storage-access-key-id` |
|
|
25
25
|
| `STORAGE_SECRET_ACCESS_KEY` | API storage credential; reads `STORAGE_SECRET_ACCESS_KEY_FILE` at `/run/secrets/storage-secret-access-key` |
|
|
26
|
+
| `MAIL_HOST`, `MAIL_PORT`, `MAIL_SECURITY` | API outgoing mail server and how its channel is secured (`starttls` is required to succeed, `tls` is implicit TLS) |
|
|
27
|
+
| `MAIL_FROM`, `MAIL_FROM_NAME` | API sender address and name of the confirmation and reset mails |
|
|
28
|
+
| `MAIL_DEFAULT_LANGUAGE`, `MAIL_CONFIRM_TTL_SECONDS`, `MAIL_RESET_TTL_SECONDS` | API mail language (`vi` or `en`) and link lifetimes; optional |
|
|
29
|
+
| `MAIL_USER`, `MAIL_PASSWORD` | API mail server login; optional, the password reads `MAIL_PASSWORD_FILE` at `/run/secrets/mail-password` |
|
|
26
30
|
| `DATABASE_URL` | `starci app db` verbs (migrate, dump) and `contract:emit`, from the operator's environment |
|
|
27
31
|
| `NEXT_PUBLIC_SITE_URL` | Front-end site URL |
|
|
28
32
|
| `NEXT_PUBLIC_API_URL` | Front-end auth doors of the API |
|
|
@@ -24,12 +24,9 @@ begin
|
|
|
24
24
|
end;
|
|
25
25
|
$$;
|
|
26
26
|
|
|
27
|
-
grant usage on schema auth to app_be;
|
|
28
|
-
grant select, insert, update on table auth.users to app_be;
|
|
29
|
-
grant select, insert on table auth.identities to app_be;
|
|
30
|
-
|
|
31
27
|
-- The device sessions and rotating refresh tokens of @starci/auth. A token is stored by its sha256 only; a rotated token stays,
|
|
32
|
-
-- marked used, so a replay is detected
|
|
28
|
+
-- marked used, so a replay is detected: a replay inside the grace window gets a successor of its own (raced_count bounds how many),
|
|
29
|
+
-- an older one ends the session and every token of it.
|
|
33
30
|
-- identity_attempts counts the sign-in attempts of one address in a window every replica shares (the budget of failures).
|
|
34
31
|
-- The app owns auth.users (db/bootstrap.sql) and the role app_be.
|
|
35
32
|
|
|
@@ -52,6 +49,7 @@ create table public.identity_refresh_tokens (
|
|
|
52
49
|
created_at timestamptz not null default now(),
|
|
53
50
|
expires_at timestamptz not null,
|
|
54
51
|
used_at timestamptz,
|
|
52
|
+
raced_count integer not null default 0,
|
|
55
53
|
constraint identity_refresh_tokens_hash_key unique (token_hash)
|
|
56
54
|
);
|
|
57
55
|
|
|
@@ -87,6 +85,30 @@ create policy identity_refresh_tokens_app_be_all on public.identity_refresh_toke
|
|
|
87
85
|
create policy identity_attempts_app_be_all on public.identity_attempts
|
|
88
86
|
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
89
87
|
|
|
88
|
+
{{> app/auth-doors.sql.partial}}
|
|
89
|
+
|
|
90
|
+
-- The single-use links that confirm a mailbox or reset a password (the email doors of sign-in): only the sha256 of the token is
|
|
91
|
+
-- stored, a spent link stays marked used, and a new link of the same purpose ends the older ones.
|
|
92
|
+
create table public.identity_email_tokens (
|
|
93
|
+
id uuid primary key,
|
|
94
|
+
user_id uuid not null references auth.users (id) on delete cascade,
|
|
95
|
+
purpose varchar(16) not null check (purpose in ('confirm', 'reset')),
|
|
96
|
+
token_hash varchar(64) not null,
|
|
97
|
+
expires_at timestamptz not null,
|
|
98
|
+
used_at timestamptz,
|
|
99
|
+
created_at timestamptz not null,
|
|
100
|
+
constraint identity_email_tokens_hash_key unique (token_hash)
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
create index identity_email_tokens_user_idx on public.identity_email_tokens (user_id, purpose);
|
|
104
|
+
|
|
105
|
+
alter table public.identity_email_tokens enable row level security;
|
|
106
|
+
alter table public.identity_email_tokens force row level security;
|
|
107
|
+
revoke all on table public.identity_email_tokens from public;
|
|
108
|
+
grant select, insert, update, delete on table public.identity_email_tokens to app_be;
|
|
109
|
+
create policy identity_email_tokens_app_be_all on public.identity_email_tokens
|
|
110
|
+
for all to app_be using (current_user = 'app_be') with check (current_user = 'app_be');
|
|
111
|
+
|
|
90
112
|
create table public.profiles (
|
|
91
113
|
id uuid primary key references auth.users (id) on delete cascade,
|
|
92
114
|
display_name text not null check (char_length(display_name) between 1 and 120),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import type { MailLanguage } from "@modules/integrations/mail"
|
|
2
3
|
import type { HttpExchange } from "@modules/integrations/passport"
|
|
3
4
|
|
|
4
5
|
/** One cookie door of sign-in: the request and response the strategy or the refresh cookie work on. */
|
|
@@ -9,3 +10,35 @@ export interface ExchangeRequest {
|
|
|
9
10
|
|
|
10
11
|
/** A request with nothing in it. */
|
|
11
12
|
export type EmptyRequest = Record<string, never>
|
|
13
|
+
|
|
14
|
+
/** The posted data of the signup door. */
|
|
15
|
+
export interface SignupData {
|
|
16
|
+
/** The sign-in address. */
|
|
17
|
+
readonly email: string
|
|
18
|
+
/** The chosen password. */
|
|
19
|
+
readonly password: string
|
|
20
|
+
/** The language of the confirmation mail, when the request names one. */
|
|
21
|
+
readonly language: MailLanguage | undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The posted data of the resend and forgot doors. */
|
|
25
|
+
export interface AddressData {
|
|
26
|
+
/** The address the mail goes to. */
|
|
27
|
+
readonly email: string
|
|
28
|
+
/** The language of the mail, when the request names one. */
|
|
29
|
+
readonly language: MailLanguage | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The link token of the confirm door. */
|
|
33
|
+
export interface TokenData {
|
|
34
|
+
/** The token the link carries. */
|
|
35
|
+
readonly token: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The posted data of the reset door. */
|
|
39
|
+
export interface ResetData {
|
|
40
|
+
/** The token the link carries. */
|
|
41
|
+
readonly token: string
|
|
42
|
+
/** The new password. */
|
|
43
|
+
readonly password: string
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { TokenData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Spends a confirmation link and confirms the mailbox. */
|
|
7
|
+
export class ConfirmCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<TokenData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ConfirmCommand } from "./confirm.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ConfirmCommand)
|
|
10
|
+
/** Spends a confirmation link and confirms the mailbox. */
|
|
11
|
+
export class ConfirmHandler extends ICQRSHandler<ConfirmCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ConfirmCommand): Promise<void> {
|
|
20
|
+
return this.email.confirm(command.params.request.token)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { AddressData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Mails a password reset link when the address has an account; the answer never says whether it does. */
|
|
7
|
+
export class ForgotCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<AddressData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ForgotCommand } from "./forgot.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ForgotCommand)
|
|
10
|
+
/** Mails a password reset link when the address has an account; the answer never says whether it does. */
|
|
11
|
+
export class ForgotHandler extends ICQRSHandler<ForgotCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ForgotCommand): Promise<void> {
|
|
20
|
+
return this.email.forgot(command.params.request.email, command.params.request.language)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { AddressData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Mails a new confirmation link to an unconfirmed account; the answer never says whether the address has one. */
|
|
7
|
+
export class ResendCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<AddressData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ResendCommand } from "./resend.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ResendCommand)
|
|
10
|
+
/** Mails a new confirmation link to an unconfirmed account; the answer never says whether the address has one. */
|
|
11
|
+
export class ResendHandler extends ICQRSHandler<ResendCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ResendCommand): Promise<void> {
|
|
20
|
+
return this.email.resend(command.params.request.email, command.params.request.language)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { ResetData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Spends a reset link: sets the new password and ends every session of the person. */
|
|
7
|
+
export class ResetCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<ResetData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { ResetCommand } from "./reset.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(ResetCommand)
|
|
10
|
+
/** Spends a reset link: sets the new password and ends every session of the person. */
|
|
11
|
+
export class ResetHandler extends ICQRSHandler<ResetCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: ResetCommand): Promise<void> {
|
|
20
|
+
return this.email.reset(command.params.request.token, command.params.request.password)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { Command } from "@nestjs/cqrs"
|
|
3
|
+
import type { PublicExecuteParams } from "@modules/platform/cqrs"
|
|
4
|
+
import type { SignupData } from "./auth.contracts"
|
|
5
|
+
|
|
6
|
+
/** Creates an unconfirmed password account and mails the confirmation link; the answer never says whether the address already had an account. */
|
|
7
|
+
export class SignupCommand extends Command<void> {
|
|
8
|
+
constructor(readonly params: PublicExecuteParams<SignupData>) {
|
|
9
|
+
super()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { CommandHandler } from "@nestjs/cqrs"
|
|
3
|
+
import { IdentityEmailService } from "@modules/domain/identity"
|
|
4
|
+
import { ICQRSHandler } from "@modules/platform/cqrs"
|
|
5
|
+
import { InjectLogger } from "@modules/platform/logging"
|
|
6
|
+
import type { Logger } from "@modules/platform/logging"
|
|
7
|
+
import { SignupCommand } from "./signup.command"
|
|
8
|
+
|
|
9
|
+
@CommandHandler(SignupCommand)
|
|
10
|
+
/** Creates an unconfirmed password account and mails the confirmation link; the answer never says whether the address already had an account. */
|
|
11
|
+
export class SignupHandler extends ICQRSHandler<SignupCommand, void> {
|
|
12
|
+
constructor(
|
|
13
|
+
@InjectLogger() logger: Logger,
|
|
14
|
+
private readonly email: IdentityEmailService,
|
|
15
|
+
) {
|
|
16
|
+
super(logger)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected override process(command: SignupCommand): Promise<void> {
|
|
20
|
+
return this.email.signup(command.params.request.email, command.params.request.password, command.params.request.language)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
1
|
+
import { defineConfigurableModule } from "@starci/nest-kit"
|
|
3
2
|
import type { AuthApiOptions } from "./auth.options"
|
|
4
3
|
|
|
5
|
-
const built = new ConfigurableModuleBuilder<AuthApiOptions>().setClassMethodName("register").build()
|
|
6
|
-
type Host = ConfigurableModuleHost<AuthApiOptions, "register", "create", {}>
|
|
7
|
-
|
|
8
4
|
/** The configurable-module base of the auth feature module. */
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
12
|
-
/** The options of a synchronous registration, for the register signature. */
|
|
13
|
-
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
5
|
+
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } =
|
|
6
|
+
defineConfigurableModule<AuthApiOptions>()
|
|
@@ -7,10 +7,15 @@ import { LogoutHandler } from "./application/logout.handler"
|
|
|
7
7
|
import { GoogleBeginHandler } from "./application/google-begin.handler"
|
|
8
8
|
import { GoogleCompleteHandler } from "./application/google-complete.handler"
|
|
9
9
|
import { GetJwksHandler } from "./application/get-jwks.handler"
|
|
10
|
+
import { SignupHandler } from "./application/signup.handler"
|
|
11
|
+
import { ResendHandler } from "./application/resend.handler"
|
|
12
|
+
import { ConfirmHandler } from "./application/confirm.handler"
|
|
13
|
+
import { ForgotHandler } from "./application/forgot.handler"
|
|
14
|
+
import { ResetHandler } from "./application/reset.handler"
|
|
10
15
|
import { ConfigurableModuleClass } from "./auth.module-definition"
|
|
11
16
|
|
|
12
17
|
@Module({
|
|
13
|
-
providers: [LoginHandler, RefreshHandler, SessionHandler, LogoutHandler, GoogleBeginHandler, GoogleCompleteHandler, GetJwksHandler],
|
|
18
|
+
providers: [LoginHandler, RefreshHandler, SessionHandler, LogoutHandler, GoogleBeginHandler, GoogleCompleteHandler, GetJwksHandler, SignupHandler, ResendHandler, ConfirmHandler, ForgotHandler, ResetHandler],
|
|
14
19
|
})
|
|
15
20
|
/** The auth feature: the handlers behind the sign-in doors. */
|
|
16
21
|
export class AuthApiModule extends ConfigurableModuleClass {}
|
package/templates/be/auth-lite/src/features/api/auth/transport/http/auth-http.module-definition.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { ConfigurableModuleBuilder, type ConfigurableModuleHost } from "@nestjs/common"
|
|
1
|
+
import { defineConfigurableModule } from "@starci/nest-kit"
|
|
3
2
|
import type { AuthHttpOptions } from "./auth-http.options"
|
|
4
3
|
|
|
5
|
-
const built = new ConfigurableModuleBuilder<AuthHttpOptions>().setClassMethodName("register").build()
|
|
6
|
-
type Host = ConfigurableModuleHost<AuthHttpOptions, "register", "create", {}>
|
|
7
|
-
|
|
8
4
|
/** The configurable-module base of the auth-http module. */
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
export const MODULE_OPTIONS_TOKEN: Host["MODULE_OPTIONS_TOKEN"] = built.MODULE_OPTIONS_TOKEN
|
|
12
|
-
/** The options of a synchronous registration, for the register signature. */
|
|
13
|
-
export const OPTIONS_TYPE: Host["OPTIONS_TYPE"] = built.OPTIONS_TYPE
|
|
5
|
+
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } =
|
|
6
|
+
defineConfigurableModule<AuthHttpOptions>()
|
|
@@ -6,6 +6,11 @@ import { Public, PublicReason } from "@modules/domain/identity"
|
|
|
6
6
|
import type { AccessGrant, GoogleRedirect, JwksDocument } from "@modules/integrations/passport"
|
|
7
7
|
import { InjectCommandBus, InjectQueryBus } from "@modules/platform/cqrs"
|
|
8
8
|
import { RateLimit, RateTier } from "@modules/platform/http-security"
|
|
9
|
+
import { SignupCommand } from "../../application/signup.command"
|
|
10
|
+
import { ResendCommand } from "../../application/resend.command"
|
|
11
|
+
import { ConfirmCommand } from "../../application/confirm.command"
|
|
12
|
+
import { ForgotCommand } from "../../application/forgot.command"
|
|
13
|
+
import { ResetCommand } from "../../application/reset.command"
|
|
9
14
|
import { GetJwksQuery } from "../../application/get-jwks.query"
|
|
10
15
|
import { GoogleBeginCommand } from "../../application/google-begin.command"
|
|
11
16
|
import { GoogleCompleteCommand } from "../../application/google-complete.command"
|
|
@@ -13,11 +18,16 @@ import { LoginCommand } from "../../application/login.command"
|
|
|
13
18
|
import { LogoutCommand } from "../../application/logout.command"
|
|
14
19
|
import { RefreshCommand } from "../../application/refresh.command"
|
|
15
20
|
import { SessionCommand } from "../../application/session.command"
|
|
21
|
+
import { ForgotRequest } from "./dto/forgot.request"
|
|
16
22
|
import { GoogleStartRequest } from "./dto/google-start.request"
|
|
17
23
|
import { LoginRequest } from "./dto/login.request"
|
|
24
|
+
import { ResendRequest } from "./dto/resend.request"
|
|
25
|
+
import { ResetRequest } from "./dto/reset.request"
|
|
26
|
+
import { SignupRequest } from "./dto/signup.request"
|
|
27
|
+
import { TokenRequest } from "./dto/token.request"
|
|
18
28
|
|
|
19
29
|
@Controller("auth")
|
|
20
|
-
/** The cookie doors of sign-in: email and password, Google, refresh, session, sign-out and the public keys. */
|
|
30
|
+
/** The cookie doors of sign-in: email and password, the email doors (sign up, confirm, resend, forgot, reset), Google, refresh, session, sign-out and the public keys. */
|
|
21
31
|
export class AuthController {
|
|
22
32
|
constructor(
|
|
23
33
|
@InjectCommandBus() private readonly commandBus: CommandBus,
|
|
@@ -80,6 +90,51 @@ export class AuthController {
|
|
|
80
90
|
return this.commandBus.execute(new GoogleCompleteCommand({ request: { exchange: { req, res } } }))
|
|
81
91
|
}
|
|
82
92
|
|
|
93
|
+
/** Creates an unconfirmed account and mails the confirmation link; always answers 204. */
|
|
94
|
+
@Post("signup")
|
|
95
|
+
@HttpCode(204)
|
|
96
|
+
@Public({ reason: PublicReason.AuthHandshake })
|
|
97
|
+
@RateLimit(RateTier.Strict)
|
|
98
|
+
signup(@Body() body: SignupRequest): Promise<void> {
|
|
99
|
+
return this.commandBus.execute(new SignupCommand({ request: { email: body.email, password: body.password, language: body.language } }))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Mails a new confirmation link; always answers 204. */
|
|
103
|
+
@Post("resend")
|
|
104
|
+
@HttpCode(204)
|
|
105
|
+
@Public({ reason: PublicReason.AuthHandshake })
|
|
106
|
+
@RateLimit(RateTier.Strict)
|
|
107
|
+
resend(@Body() body: ResendRequest): Promise<void> {
|
|
108
|
+
return this.commandBus.execute(new ResendCommand({ request: { email: body.email, language: body.language } }))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Spends a confirmation link; a spent, expired or unknown link is refused with IDENTITY_RESET_TOKEN_INVALID. */
|
|
112
|
+
@Post("confirm")
|
|
113
|
+
@HttpCode(204)
|
|
114
|
+
@Public({ reason: PublicReason.AuthHandshake })
|
|
115
|
+
@RateLimit(RateTier.Strict)
|
|
116
|
+
confirm(@Body() body: TokenRequest): Promise<void> {
|
|
117
|
+
return this.commandBus.execute(new ConfirmCommand({ request: { token: body.token } }))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Mails a password reset link when the address has an account; always answers 204. */
|
|
121
|
+
@Post("forgot")
|
|
122
|
+
@HttpCode(204)
|
|
123
|
+
@Public({ reason: PublicReason.AuthHandshake })
|
|
124
|
+
@RateLimit(RateTier.Strict)
|
|
125
|
+
forgot(@Body() body: ForgotRequest): Promise<void> {
|
|
126
|
+
return this.commandBus.execute(new ForgotCommand({ request: { email: body.email, language: body.language } }))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Spends a reset link and sets the new password; every session of the person ends. */
|
|
130
|
+
@Post("reset")
|
|
131
|
+
@HttpCode(204)
|
|
132
|
+
@Public({ reason: PublicReason.AuthHandshake })
|
|
133
|
+
@RateLimit(RateTier.Strict)
|
|
134
|
+
reset(@Body() body: ResetRequest): Promise<void> {
|
|
135
|
+
return this.commandBus.execute(new ResetCommand({ request: { token: body.token, password: body.password } }))
|
|
136
|
+
}
|
|
137
|
+
|
|
83
138
|
/** The public keys access tokens verify against. */
|
|
84
139
|
@Get("jwks")
|
|
85
140
|
@Public({ reason: PublicReason.AuthHandshake })
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { IsEmail, IsIn, IsOptional, MaxLength } from "class-validator"
|
|
3
|
+
import type { MailLanguage } from "@modules/integrations/mail"
|
|
4
|
+
|
|
5
|
+
/** The posted address of a password reset request. */
|
|
6
|
+
export class ForgotRequest {
|
|
7
|
+
/** The sign-in address. */
|
|
8
|
+
@IsEmail()
|
|
9
|
+
@MaxLength(254)
|
|
10
|
+
email!: string
|
|
11
|
+
|
|
12
|
+
/** The language of the mail; the app's default when left out. */
|
|
13
|
+
@IsOptional()
|
|
14
|
+
@IsIn(["vi", "en"])
|
|
15
|
+
language?: MailLanguage
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { IsEmail, IsIn, IsOptional, MaxLength } from "class-validator"
|
|
3
|
+
import type { MailLanguage } from "@modules/integrations/mail"
|
|
4
|
+
|
|
5
|
+
/** The posted address of a resend. */
|
|
6
|
+
export class ResendRequest {
|
|
7
|
+
/** The sign-in address. */
|
|
8
|
+
@IsEmail()
|
|
9
|
+
@MaxLength(254)
|
|
10
|
+
email!: string
|
|
11
|
+
|
|
12
|
+
/** The language of the mail; the app's default when left out. */
|
|
13
|
+
@IsOptional()
|
|
14
|
+
@IsIn(["vi", "en"])
|
|
15
|
+
language?: MailLanguage
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { IsString, MaxLength, MinLength } from "class-validator"
|
|
3
|
+
|
|
4
|
+
/** The posted link token and new password. */
|
|
5
|
+
export class ResetRequest {
|
|
6
|
+
/** The token the link carries. */
|
|
7
|
+
@IsString()
|
|
8
|
+
@MinLength(16)
|
|
9
|
+
@MaxLength(256)
|
|
10
|
+
token!: string
|
|
11
|
+
|
|
12
|
+
/** The new password. */
|
|
13
|
+
@IsString()
|
|
14
|
+
@MinLength(1)
|
|
15
|
+
@MaxLength(128)
|
|
16
|
+
password!: string
|
|
17
|
+
|
|
18
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { IsEmail, IsIn, IsOptional, IsString, MaxLength, MinLength } from "class-validator"
|
|
3
|
+
import type { MailLanguage } from "@modules/integrations/mail"
|
|
4
|
+
|
|
5
|
+
/** The posted sign-up data. */
|
|
6
|
+
export class SignupRequest {
|
|
7
|
+
/** The sign-in address. */
|
|
8
|
+
@IsEmail()
|
|
9
|
+
@MaxLength(254)
|
|
10
|
+
email!: string
|
|
11
|
+
|
|
12
|
+
/** The password. */
|
|
13
|
+
@IsString()
|
|
14
|
+
@MinLength(1)
|
|
15
|
+
@MaxLength(128)
|
|
16
|
+
password!: string
|
|
17
|
+
|
|
18
|
+
/** The language of the mail; the app's default when left out. */
|
|
19
|
+
@IsOptional()
|
|
20
|
+
@IsIn(["vi", "en"])
|
|
21
|
+
language?: MailLanguage
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Managed by starci app sync (slot be.feature.auth); do not edit: run "npx starci app sync --execute".
|
|
2
|
+
import { IsString, MaxLength, MinLength } from "class-validator"
|
|
3
|
+
|
|
4
|
+
/** The posted link token. */
|
|
5
|
+
export class TokenRequest {
|
|
6
|
+
/** The token the link carries. */
|
|
7
|
+
@IsString()
|
|
8
|
+
@MinLength(16)
|
|
9
|
+
@MaxLength(256)
|
|
10
|
+
token!: string
|
|
11
|
+
}
|