alepha 0.14.2 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/audits/index.browser.js +5 -5
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +784 -784
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +13 -13
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +5 -5
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +57 -57
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +71 -71
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +5 -5
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +165 -165
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -10
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +10 -10
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +583 -171
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +12 -12
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +163 -10
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +281 -276
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +196 -91
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +19 -19
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1137 -1123
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +827 -596
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js +6 -6
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +13 -13
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +6 -6
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/bin/index.d.ts +1 -2
- package/dist/bin/index.js +0 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/cli/index.d.ts +137 -112
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +371 -259
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +45 -5
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +97 -17
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +14 -18
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +29 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -18
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +14 -18
- package/dist/core/index.native.js.map +1 -1
- package/dist/fake/index.js +195 -168
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +8 -0
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +3 -0
- package/dist/file/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/orm/index.d.ts +32 -32
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +12 -12
- package/dist/orm/index.js.map +1 -1
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +171 -155
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +0 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +2 -0
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js +22 -6
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +46 -44
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +24 -41
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +8 -3
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +12 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/package.json +7 -7
- package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
- package/src/api/audits/entities/audits.ts +5 -5
- package/src/api/audits/index.browser.ts +1 -1
- package/src/api/audits/index.ts +3 -3
- package/src/api/audits/primitives/$audit.spec.ts +276 -0
- package/src/api/audits/services/AuditService.spec.ts +495 -0
- package/src/api/files/__tests__/$bucket.spec.ts +91 -0
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
- package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
- package/src/api/files/controllers/FileController.spec.ts +558 -0
- package/src/api/files/controllers/FileController.ts +4 -5
- package/src/api/files/entities/files.ts +5 -5
- package/src/api/files/index.browser.ts +1 -1
- package/src/api/files/index.ts +4 -4
- package/src/api/files/jobs/FileJobs.spec.ts +52 -0
- package/src/api/files/services/FileService.spec.ts +109 -0
- package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
- package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
- package/src/api/jobs/entities/jobExecutions.ts +5 -5
- package/src/api/jobs/index.ts +3 -3
- package/src/api/jobs/primitives/$job.spec.ts +476 -0
- package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
- package/src/api/notifications/entities/notifications.ts +5 -5
- package/src/api/notifications/index.browser.ts +1 -1
- package/src/api/notifications/index.ts +4 -4
- package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
- package/src/api/parameters/entities/parameters.ts +7 -17
- package/src/api/parameters/index.ts +3 -3
- package/src/api/parameters/primitives/$config.spec.ts +356 -0
- package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
- package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
- package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
- package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
- package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
- package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
- package/src/api/parameters/schemas/index.ts +15 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
- package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
- package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
- package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
- package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
- package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
- package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
- package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
- package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
- package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
- package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
- package/src/api/users/controllers/AdminUserController.ts +116 -0
- package/src/api/users/controllers/UserController.ts +4 -107
- package/src/api/users/controllers/UserRealmController.ts +3 -0
- package/src/api/users/entities/identities.ts +6 -6
- package/src/api/users/entities/sessions.ts +6 -6
- package/src/api/users/entities/users.ts +9 -9
- package/src/api/users/index.ts +9 -6
- package/src/api/users/primitives/$userRealm.ts +13 -8
- package/src/api/users/services/CredentialService.spec.ts +509 -0
- package/src/api/users/services/CredentialService.ts +46 -0
- package/src/api/users/services/IdentityService.ts +15 -0
- package/src/api/users/services/RegistrationService.spec.ts +630 -0
- package/src/api/users/services/RegistrationService.ts +18 -0
- package/src/api/users/services/SessionService.spec.ts +301 -0
- package/src/api/users/services/SessionService.ts +110 -1
- package/src/api/users/services/UserService.ts +67 -2
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
- package/src/api/verifications/entities/verifications.ts +6 -6
- package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
- package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
- package/src/batch/primitives/$batch.spec.ts +766 -0
- package/src/batch/providers/BatchProvider.spec.ts +786 -0
- package/src/bin/index.ts +0 -1
- package/src/bucket/__tests__/shared.ts +194 -0
- package/src/bucket/primitives/$bucket.spec.ts +104 -0
- package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
- package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
- package/src/cache/core/__tests__/shared.ts +377 -0
- package/src/cache/core/primitives/$cache.spec.ts +111 -0
- package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
- package/src/cli/apps/AlephaCli.ts +25 -4
- package/src/cli/commands/dev.ts +19 -7
- package/src/cli/commands/gen/changelog.spec.ts +315 -0
- package/src/cli/commands/{changelog.ts → gen/changelog.ts} +9 -9
- package/src/cli/commands/gen/openapi.ts +71 -0
- package/src/cli/commands/gen.ts +18 -0
- package/src/cli/commands/init.ts +2 -0
- package/src/cli/commands/root.ts +12 -3
- package/src/cli/commands/typecheck.ts +5 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/services/AlephaCliUtils.ts +71 -32
- package/src/cli/services/GitMessageParser.ts +1 -1
- package/src/command/helpers/Asker.spec.ts +127 -0
- package/src/command/helpers/Runner.spec.ts +126 -0
- package/src/command/primitives/$command.spec.ts +1588 -0
- package/src/command/providers/CliProvider.ts +74 -24
- package/src/core/Alepha.ts +45 -0
- package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
- package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
- package/src/core/__tests__/Alepha-has.spec.ts +41 -0
- package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
- package/src/core/__tests__/Alepha-register.spec.ts +81 -0
- package/src/core/__tests__/Alepha-start.spec.ts +176 -0
- package/src/core/__tests__/Alepha-with.spec.ts +14 -0
- package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
- package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
- package/src/core/__tests__/descriptor.spec.ts +34 -0
- package/src/core/__tests__/fixtures/A.ts +5 -0
- package/src/core/__tests__/pagination.spec.ts +77 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
- package/src/core/primitives/$atom.spec.ts +43 -0
- package/src/core/primitives/$hook.spec.ts +130 -0
- package/src/core/primitives/$inject.spec.ts +175 -0
- package/src/core/primitives/$module.spec.ts +115 -0
- package/src/core/providers/CodecManager.spec.ts +740 -0
- package/src/core/providers/EventManager.spec.ts +762 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/StateManager.spec.ts +365 -0
- package/src/core/providers/TypeProvider.spec.ts +1607 -0
- package/src/core/providers/TypeProvider.ts +20 -26
- package/src/datetime/primitives/$interval.spec.ts +103 -0
- package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
- package/src/email/primitives/$email.spec.ts +175 -0
- package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
- package/src/fake/__tests__/keyName.example.ts +40 -0
- package/src/fake/__tests__/keyName.spec.ts +152 -0
- package/src/fake/__tests__/module.example.ts +32 -0
- package/src/fake/providers/FakeProvider.spec.ts +438 -0
- package/src/file/providers/FileSystemProvider.ts +8 -0
- package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
- package/src/file/providers/NodeFileSystemProvider.ts +5 -0
- package/src/file/services/FileDetector.spec.ts +591 -0
- package/src/lock/core/__tests__/shared.ts +190 -0
- package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
- package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
- package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
- package/src/logger/primitives/$logger.spec.ts +108 -0
- package/src/logger/services/Logger.spec.ts +295 -0
- package/src/mcp/__tests__/errors.spec.ts +175 -0
- package/src/mcp/__tests__/integration.spec.ts +450 -0
- package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
- package/src/mcp/primitives/$prompt.spec.ts +468 -0
- package/src/mcp/primitives/$resource.spec.ts +390 -0
- package/src/mcp/primitives/$tool.spec.ts +406 -0
- package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
- package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
- package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
- package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
- package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
- package/src/orm/__tests__/$repository-save.spec.ts +37 -0
- package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
- package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
- package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
- package/src/orm/__tests__/delete-returning.spec.ts +256 -0
- package/src/orm/__tests__/deletedAt.spec.ts +80 -0
- package/src/orm/__tests__/enums.spec.ts +315 -0
- package/src/orm/__tests__/execute.spec.ts +72 -0
- package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
- package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
- package/src/orm/__tests__/joins.spec.ts +1114 -0
- package/src/orm/__tests__/page.spec.ts +287 -0
- package/src/orm/__tests__/primaryKey.spec.ts +87 -0
- package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
- package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
- package/src/orm/__tests__/references.spec.ts +102 -0
- package/src/orm/__tests__/security.spec.ts +710 -0
- package/src/orm/__tests__/sqlite.spec.ts +111 -0
- package/src/orm/__tests__/string-operators.spec.ts +429 -0
- package/src/orm/__tests__/timestamps.spec.ts +388 -0
- package/src/orm/__tests__/validation.spec.ts +183 -0
- package/src/orm/__tests__/version.spec.ts +64 -0
- package/src/orm/helpers/parseQueryString.spec.ts +196 -0
- package/src/orm/primitives/$repository.spec.ts +137 -0
- package/src/orm/primitives/$sequence.spec.ts +29 -0
- package/src/orm/primitives/$transaction.spec.ts +82 -0
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
- package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
- package/src/orm/services/ModelBuilder.spec.ts +575 -0
- package/src/orm/services/Repository.spec.ts +137 -0
- package/src/queue/core/__tests__/shared.ts +143 -0
- package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
- package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
- package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
- package/src/redis/__tests__/redis.spec.ts +58 -0
- package/src/retry/primitives/$retry.spec.ts +234 -0
- package/src/retry/providers/RetryProvider.spec.ts +438 -0
- package/src/router/__tests__/match.spec.ts +252 -0
- package/src/router/providers/RouterProvider.spec.ts +197 -0
- package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
- package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
- package/src/scheduler/__tests__/shared.ts +77 -0
- package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
- package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
- package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
- package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
- package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
- package/src/security/primitives/$permission.spec.ts +30 -0
- package/src/security/primitives/$permission.ts +2 -2
- package/src/security/primitives/$realm.spec.ts +101 -0
- package/src/security/primitives/$role.spec.ts +52 -0
- package/src/security/primitives/$serviceAccount.spec.ts +61 -0
- package/src/security/providers/SecurityProvider.spec.ts +350 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
- package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
- package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
- package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
- package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
- package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
- package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
- package/src/server/core/primitives/$action.spec.ts +191 -0
- package/src/server/core/primitives/$route.spec.ts +65 -0
- package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
- package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
- package/src/server/core/providers/ServerProvider.ts +3 -1
- package/src/server/core/services/HttpClient.spec.ts +123 -0
- package/src/server/core/services/UserAgentParser.spec.ts +111 -0
- package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
- package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
- package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
- package/src/server/links/__tests__/$action.spec.ts +238 -0
- package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
- package/src/server/links/__tests__/requestId.spec.ts +120 -0
- package/src/server/links/primitives/$remote.spec.ts +228 -0
- package/src/server/links/providers/LinkProvider.spec.ts +54 -0
- package/src/server/links/providers/LinkProvider.ts +49 -3
- package/src/server/links/providers/ServerLinksProvider.ts +1 -53
- package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
- package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
- package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
- package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
- package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
- package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
- package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
- package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
- package/src/server/static/primitives/$serve.spec.ts +193 -0
- package/src/server/swagger/__tests__/ui.spec.ts +52 -0
- package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
- package/src/sms/primitives/$sms.spec.ts +165 -0
- package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
- package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
- package/src/thread/primitives/$thread.spec.ts +186 -0
- package/src/topic/core/__tests__/shared.ts +144 -0
- package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
- package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
- package/src/vite/plugins/viteAlephaDev.ts +16 -4
- package/src/vite/tasks/runAlepha.ts +7 -1
- package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
- package/src/websocket/primitives/$channel.spec.ts +30 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES","identities","users","name","identities"],"sources":["../../../src/api/users/schemas/identityQuerySchema.ts","../../../src/api/users/entities/users.ts","../../../src/api/users/entities/identities.ts","../../../src/api/users/schemas/identityResourceSchema.ts","../../../src/api/users/atoms/realmAuthSettingsAtom.ts","../../../src/api/users/entities/sessions.ts","../../../src/api/users/providers/UserRealmProvider.ts","../../../src/api/users/services/IdentityService.ts","../../../src/api/users/controllers/IdentityController.ts","../../../src/api/users/schemas/sessionQuerySchema.ts","../../../src/api/users/schemas/sessionResourceSchema.ts","../../../src/api/users/services/SessionCrudService.ts","../../../src/api/users/controllers/SessionController.ts","../../../src/api/users/schemas/completePasswordResetRequestSchema.ts","../../../src/api/users/schemas/completeRegistrationRequestSchema.ts","../../../src/api/users/schemas/createUserSchema.ts","../../../src/api/users/schemas/passwordResetIntentResponseSchema.ts","../../../src/api/users/schemas/registerQuerySchema.ts","../../../src/api/users/schemas/registerRequestSchema.ts","../../../src/api/users/schemas/registrationIntentResponseSchema.ts","../../../src/api/users/schemas/updateUserSchema.ts","../../../src/api/users/schemas/userQuerySchema.ts","../../../src/api/users/schemas/userResourceSchema.ts","../../../src/api/users/notifications/UserNotifications.ts","../../../src/api/users/services/CredentialService.ts","../../../src/api/users/services/RegistrationService.ts","../../../src/api/users/services/UserService.ts","../../../src/api/users/controllers/UserController.ts","../../../src/api/users/schemas/userRealmConfigSchema.ts","../../../src/api/users/controllers/UserRealmController.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/users/primitives/$userRealm.ts","../../../src/api/users/schemas/loginSchema.ts","../../../src/api/users/schemas/registerSchema.ts","../../../src/api/users/schemas/resetPasswordSchema.ts","../../../src/api/users/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const identityQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n provider: t.optional(t.string()),\n});\n\nexport type IdentityQuery = Static<typeof identityQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const DEFAULT_USER_REALM_NAME = \"default\";\n\nexport const users = $entity({\n name: \"users\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n\n realm: pg.default(t.text(), DEFAULT_USER_REALM_NAME),\n\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n pattern: \"^[a-zA-Z0-9._-]+$\",\n }),\n ),\n\n email: t.optional(t.string({ format: \"email\" })),\n\n phoneNumber: t.optional(t.e164()),\n\n roles: pg.default(t.array(t.string()), []),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n picture: t.optional(t.string()),\n enabled: pg.default(t.boolean(), true),\n\n emailVerified: pg.default(t.boolean(), false),\n }),\n indexes: [\n { columns: [\"realm\", \"username\"], unique: true },\n { columns: [\"realm\", \"email\"], unique: true },\n { columns: [\"realm\", \"phoneNumber\"], unique: true },\n ],\n});\n\nexport type UserEntity = Static<typeof users.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const identities = $entity({\n name: \"identities\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n userId: pg.ref(t.uuid(), () => users.cols.id),\n password: t.optional(t.text()),\n provider: t.text(),\n providerUserId: t.optional(t.text()),\n providerData: t.optional(t.json()),\n }),\n});\n\nexport type IdentityEntity = Static<typeof identities.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { identities } from \"../entities/identities.ts\";\n\nexport const identityResourceSchema = t.omit(identities.schema, [\"password\"]);\n\nexport type IdentityResource = Static<typeof identityResourceSchema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const realmAuthSettingsAtom = $atom({\n name: \"alepha.api.users.realmAuthSettings\",\n schema: t.object({\n // Branding and display settings\n displayName: t.optional(\n t.string({\n description:\n \"Display name shown on auth pages (e.g., 'Customer Portal')\",\n }),\n ),\n description: t.optional(\n t.string({\n description: \"Description shown on auth pages\",\n }),\n ),\n logoUrl: t.optional(\n t.string({\n description: \"Logo URL for auth pages\",\n }),\n ),\n\n // Auth settings\n registrationAllowed: t.boolean({\n description: \"Enable user self-registration\",\n }),\n emailEnabled: t.boolean({\n description: \"Enable email address as a login/registration credential\",\n }),\n emailRequired: t.boolean({\n description: \"Require email address for user accounts\",\n }),\n usernameEnabled: t.boolean({\n description: \"Enable username as a login/registration credential\",\n }),\n usernameRequired: t.boolean({\n description: \"Require username for user accounts\",\n }),\n phoneEnabled: t.boolean({\n description: \"Enable phone number as a login/registration credential\",\n }),\n phoneRequired: t.boolean({\n description: \"Require phone number for user accounts\",\n }),\n verifyEmailRequired: t.boolean({\n description: \"Require email verification for user accounts\",\n }),\n verifyPhoneRequired: t.boolean({\n description: \"Require phone verification for user accounts\",\n }),\n firstNameLastNameEnabled: t.boolean({\n description: \"Enable first and last name for user accounts\",\n }),\n firstNameLastNameRequired: t.boolean({\n description: \"Require first and last name for user accounts\",\n }),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n passwordPolicy: t.object({\n minLength: t.integer({\n description: \"Minimum password length\",\n default: 8,\n minimum: 1,\n }),\n requireUppercase: t.boolean({\n description: \"Require at least one uppercase letter\",\n }),\n requireLowercase: t.boolean({\n description: \"Require at least one lowercase letter\",\n }),\n requireNumbers: t.boolean({\n description: \"Require at least one number\",\n }),\n requireSpecialCharacters: t.boolean({\n description: \"Require at least one special character\",\n }),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n emailEnabled: true,\n emailRequired: true,\n usernameEnabled: false,\n usernameRequired: false,\n phoneEnabled: false,\n phoneRequired: false,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n firstNameLastNameEnabled: false,\n firstNameLastNameRequired: false,\n // TODO: not implemented yet\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n },\n});\n\nexport type RealmAuthSettings = Static<typeof realmAuthSettingsAtom.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const sessions = $entity({\n name: \"sessions\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n version: pg.version(),\n createdAt: pg.createdAt(),\n updatedAt: pg.updatedAt(),\n refreshToken: t.uuid(),\n userId: pg.ref(t.uuid(), () => users.cols.id),\n expiresAt: t.datetime(),\n ip: t.optional(t.text()),\n userAgent: t.optional(\n t.object({\n os: t.text(),\n browser: t.text(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n }),\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $bucket } from \"alepha/bucket\";\nimport { $repository, type Repository } from \"alepha/orm\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { identities } from \"../entities/identities.ts\";\nimport { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, users } from \"../entities/users.ts\";\nimport type { UserRealmOptions } from \"../primitives/$userRealm.ts\";\n\nexport interface UserRealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface UserRealm {\n name: string;\n repositories: UserRealmRepositories;\n settings: RealmAuthSettings;\n}\n\nexport class UserRealmProvider {\n protected readonly alepha = $inject(Alepha);\n // Default repositories using $repository() for eager initialization\n protected readonly defaultIdentities = $repository(identities);\n protected readonly defaultSessions = $repository(sessions);\n protected readonly defaultUsers = $repository(users);\n\n protected realms = new Map<string, UserRealm>();\n\n public avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n\n protected readonly onConfigure = $hook({\n on: \"configure\",\n handler: () => {\n this.alepha.store.set(\"alepha.server.security.system.user\", {\n id: \"00000000-0000-0000-0000-000000000000\",\n name: \"system\",\n roles: [\"admin\"], // TODO: use realm config\n });\n },\n });\n\n public register(\n userRealmName: string,\n userRealmOptions: UserRealmOptions = {},\n ) {\n this.realms.set(userRealmName, {\n name: userRealmName,\n repositories: {\n identities:\n userRealmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: userRealmOptions.entities?.sessions ?? this.defaultSessions,\n users: userRealmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...userRealmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...userRealmOptions.settings?.passwordPolicy,\n },\n },\n });\n return this.getRealm(userRealmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(userRealmName = DEFAULT_USER_REALM_NAME): UserRealm {\n let realm = this.realms.get(userRealmName);\n\n if (!realm) {\n // Auto-register default realm for backward compatibility\n const realms = Array.from(this.realms.values());\n const firstRealm = realms[0];\n if (userRealmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(userRealmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing user realm '${userRealmName}', please declare $userRealm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(userRealmName).repositories.identities;\n }\n\n public sessionRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(userRealmName).repositories.sessions;\n }\n\n public userRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(userRealmName).repositories.users;\n }\n}\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\n\nexport class IdentityService {\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Find identities with pagination and filtering.\n */\n public async findIdentities(\n q: IdentityQuery = {},\n userRealmName?: string,\n ): Promise<Page<IdentityEntity>> {\n this.log.trace(\"Finding identities\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.identities(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n if (q.provider) {\n where.provider = { like: q.provider };\n }\n\n const result = await this.identities(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Identities found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get an identity by ID.\n */\n public async getIdentityById(\n id: string,\n userRealmName?: string,\n ): Promise<IdentityEntity> {\n this.log.trace(\"Getting identity by ID\", { id, userRealmName });\n const identity = await this.identities(userRealmName).findById(id);\n this.log.debug(\"Identity retrieved\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n return identity;\n }\n\n /**\n * Delete an identity by ID.\n */\n public async deleteIdentity(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting identity\", { id, userRealmName });\n\n // Verify identity exists\n const identity = await this.getIdentityById(id, userRealmName);\n\n await this.identities(userRealmName).deleteById(id);\n this.log.info(\"Identity deleted\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { pg } from \"alepha/orm\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { identityQuerySchema } from \"../schemas/identityQuerySchema.ts\";\nimport { identityResourceSchema } from \"../schemas/identityResourceSchema.ts\";\nimport { IdentityService } from \"../services/IdentityService.ts\";\n\nexport class IdentityController {\n protected readonly url = \"/identities\";\n protected readonly group = \"identities\";\n protected readonly identityService = $inject(IdentityService);\n\n /**\n * Find identities with pagination and filtering.\n */\n public readonly findIdentities = $action({\n path: this.url,\n group: this.group,\n description: \"Find identities with pagination and filtering\",\n schema: {\n query: t.extend(identityQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: pg.page(identityResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.identityService.findIdentities(q, userRealmName);\n },\n });\n\n /**\n * Get an identity by ID.\n */\n public readonly getIdentity = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get an identity by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: identityResourceSchema,\n },\n handler: ({ params, query }) =>\n this.identityService.getIdentityById(params.id, query.userRealmName),\n });\n\n /**\n * Delete an identity.\n */\n public readonly deleteIdentity = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete an identity\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.identityService.deleteIdentity(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const sessionQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n});\n\nexport type SessionQuery = Static<typeof sessionQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const sessionResourceSchema = t.object({\n id: t.uuid(),\n version: t.number(),\n createdAt: t.datetime(),\n updatedAt: t.datetime(),\n refreshToken: t.uuid(),\n userId: t.uuid(),\n expiresAt: t.datetime(),\n ip: t.optional(t.string()),\n userAgent: t.optional(\n t.object({\n os: t.string(),\n browser: t.string(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n});\n\nexport type SessionResource = Static<typeof sessionResourceSchema>;\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { SessionEntity } from \"../entities/sessions.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n /**\n * Find sessions with pagination and filtering.\n */\n public async findSessions(\n q: SessionQuery = {},\n userRealmName?: string,\n ): Promise<Page<SessionEntity>> {\n this.log.trace(\"Finding sessions\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.sessions(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n const result = await this.sessions(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Sessions found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a session by ID.\n */\n public async getSessionById(\n id: string,\n userRealmName?: string,\n ): Promise<SessionEntity> {\n this.log.trace(\"Getting session by ID\", { id, userRealmName });\n const session = await this.sessions(userRealmName).findById(id);\n this.log.debug(\"Session retrieved\", { id, userId: session.userId });\n return session;\n }\n\n /**\n * Delete a session by ID.\n */\n public async deleteSession(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting session\", { id, userRealmName });\n\n // Verify session exists\n await this.getSessionById(id, userRealmName);\n\n await this.sessions(userRealmName).deleteById(id);\n this.log.info(\"Session deleted\", { id });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { pg } from \"alepha/orm\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { sessionQuerySchema } from \"../schemas/sessionQuerySchema.ts\";\nimport { sessionResourceSchema } from \"../schemas/sessionResourceSchema.ts\";\nimport { SessionCrudService } from \"../services/SessionCrudService.ts\";\n\nexport class SessionController {\n protected readonly url = \"/sessions\";\n protected readonly group = \"sessions\";\n protected readonly sessionService = $inject(SessionCrudService);\n\n /**\n * Find sessions with pagination and filtering.\n */\n public readonly findSessions = $action({\n path: this.url,\n group: this.group,\n description: \"Find sessions with pagination and filtering\",\n schema: {\n query: t.extend(sessionQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: pg.page(sessionResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.sessionService.findSessions(q, userRealmName);\n },\n });\n\n /**\n * Get a session by ID.\n */\n public readonly getSession = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a session by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: sessionResourceSchema,\n },\n handler: ({ params, query }) =>\n this.sessionService.getSessionById(params.id, query.userRealmName),\n });\n\n /**\n * Delete a session.\n */\n public readonly deleteSession = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a session\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.sessionService.deleteSession(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Request schema for completing a password reset.\n *\n * Requires the intent ID from Phase 1, the verification code,\n * and the new password.\n */\nexport const completePasswordResetRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The intent ID from createPasswordResetIntent\",\n }),\n code: t.string({\n description: \"6-digit verification code sent via email\",\n }),\n newPassword: t.string({\n minLength: 8,\n description: \"New password (minimum 8 characters)\",\n }),\n});\n\nexport type CompletePasswordResetRequest = Static<\n typeof completePasswordResetRequestSchema\n>;\n","import { type Static, t } from \"alepha\";\n\nexport const completeRegistrationRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The registration intent ID from the first phase\",\n }),\n emailCode: t.optional(\n t.string({\n description: \"Email verification code (if email verification required)\",\n }),\n ),\n phoneCode: t.optional(\n t.string({\n description: \"Phone verification code (if phone verification required)\",\n }),\n ),\n captchaToken: t.optional(\n t.string({\n description: \"Captcha token (if captcha required)\",\n }),\n ),\n});\n\nexport type CompleteRegistrationRequest = Static<\n typeof completeRegistrationRequestSchema\n>;\n","import { type Static, t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const createUserSchema = t.omit(users.insertSchema, [\"realm\"]);\n\nexport type CreateUser = Static<typeof createUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Response schema for password reset intent creation.\n *\n * Contains the intent ID needed for Phase 2 completion,\n * along with expiration time.\n */\nexport const passwordResetIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for this password reset intent\",\n }),\n expiresAt: t.datetime({\n description: \"ISO timestamp when this intent expires\",\n }),\n});\n\nexport type PasswordResetIntentResponse = Static<\n typeof passwordResetIntentResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration query parameters.\n * Allows specifying a custom user realm.\n */\nexport const registerQuerySchema = t.object({\n userRealmName: t.optional(\n t.text({\n description:\n \"The user realm to register the user in (defaults to 'default')\",\n }),\n ),\n});\n\nexport type RegisterQuery = Static<typeof registerQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration request body.\n * Password is always required, other fields depend on realm settings.\n */\nexport const registerRequestSchema = t.object({\n // Password is always required\n password: t.string({\n minLength: 8,\n description: \"Password for the account\",\n }),\n\n // Identity fields (requirements depend on realm settings)\n username: t.optional(\n t.string({\n minLength: 3,\n description: \"Unique username for the account\",\n }),\n ),\n\n // Optional contact fields\n email: t.optional(\n t.string({\n format: \"email\",\n description: \"User's email address\",\n }),\n ),\n phoneNumber: t.optional(\n t.string({\n description: \"User's phone number\",\n }),\n ),\n\n // Optional user profile fields\n firstName: t.optional(\n t.string({\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n description: \"User's last name\",\n }),\n ),\n picture: t.optional(\n t.string({\n description: \"User's profile picture URL\",\n }),\n ),\n});\n\nexport type RegisterRequest = Static<typeof registerRequestSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const registrationIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for the registration intent\",\n }),\n expectCaptcha: t.boolean({\n description: \"Whether captcha verification is required\",\n }),\n expectEmailVerification: t.boolean({\n description: \"Whether email verification is required\",\n }),\n expectPhoneVerification: t.boolean({\n description: \"Whether phone verification is required\",\n }),\n expiresAt: t.datetime({\n description: \"When the registration intent expires\",\n }),\n});\n\nexport type RegistrationIntentResponse = Static<\n typeof registrationIntentResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const updateUserSchema = t.partial(\n t.omit(users.insertSchema, [\n \"id\",\n \"version\",\n \"createdAt\",\n \"updatedAt\",\n \"username\",\n \"emailVerified\",\n ]),\n);\n\nexport type UpdateUser = Static<typeof updateUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const userQuerySchema = t.extend(pageQuerySchema, {\n email: t.optional(t.string()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n roles: t.optional(t.array(t.string())),\n query: t.optional(t.text()),\n});\n\nexport type UserQuery = Static<typeof userQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const userResourceSchema = users.schema;\n\nexport type UserResource = Static<typeof userResourceSchema>;\n","import { t } from \"alepha\";\nimport { $notification } from \"alepha/api/notifications\";\n\nexport class UserNotifications {\n // Code-based notifications (preferred)\n public readonly passwordReset = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Use the code below to verify your identity:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerification = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Use the code below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly phoneVerification = $notification({\n category: \"security\",\n description:\n \"SMS sent to users with a verification code to verify their phone number.\",\n critical: true,\n sensitive: true,\n sms: {\n message: (it) =>\n `Your verification code is: ${it.code}. This code expires in ${it.expiresInMinutes} minutes.`,\n },\n schema: t.object({\n phoneNumber: t.string(),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n // Link-based notifications (alternative)\n public readonly passwordResetLink = $notification({\n category: \"security\",\n description: \"Email sent to users with a link to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Click the link below to create a new password:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.resetUrl}\" style=\"background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tReset Password\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.resetUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n resetUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerificationLink = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a link to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Click the button below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.verifyUrl}\" style=\"background-color: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tVerify Email\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.verifyUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n verifyUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CompletePasswordResetRequest } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport type { PasswordResetIntentResponse } from \"../schemas/passwordResetIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the password reset flow.\n */\ninterface PasswordResetIntent {\n email: string;\n userId: string;\n identityId: string;\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class CredentialService {\n protected readonly log = $logger();\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Phase 1: Create a password reset intent.\n *\n * Validates the email, checks for existing user with credentials,\n * sends verification code, and stores the intent in cache.\n *\n * @param email - User's email address\n * @param userRealmName - Optional realm name\n * @returns Intent response with intentId and expiration (always returns for security)\n */\n public async createPasswordResetIntent(\n email: string,\n userRealmName?: string,\n ): Promise<PasswordResetIntentResponse> {\n this.log.trace(\"Creating password reset intent\", { email, userRealmName });\n\n // Generate intent ID and expiration upfront for consistent response\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n // Silent fail - don't reveal that email doesn't exist\n this.log.debug(\"Password reset requested for non-existent email\", {\n email,\n });\n return { intentId, expiresAt };\n }\n\n // Find the credentials identity for this user\n const identity = await this.identities(userRealmName)\n .findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n })\n .catch(() => undefined);\n\n if (!identity) {\n // User doesn't have credentials identity (maybe OAuth only)\n this.log.debug(\"Password reset requested for user without credentials\", {\n userId: user.id,\n });\n return { intentId, expiresAt };\n }\n\n // Create verification using verification controller\n // This handles: token generation, expiration, rate limiting, cooldown\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n // Send password reset notification with the code\n await this.userNotifications.passwordReset.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n // Store intent in cache\n const intent: PasswordResetIntent = {\n email,\n userId: user.id,\n identityId: identity.id,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Password reset intent created\", {\n intentId,\n userId: user.id,\n email,\n });\n } catch (error) {\n // If rate limit or cooldown hit, still return success for security\n this.log.warn(\"Failed to create password reset verification\", error);\n }\n\n return { intentId, expiresAt };\n }\n\n /**\n * Phase 2: Complete password reset using an intent.\n *\n * Validates the verification code, updates the password,\n * and invalidates all existing sessions.\n *\n * @param body - Request body with intentId, code, and newPassword\n */\n public async completePasswordReset(\n body: CompletePasswordResetRequest,\n ): Promise<void> {\n this.log.trace(\"Completing password reset\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired password reset intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired password reset intent\",\n });\n }\n\n // Verify code using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: intent.email, token: body.code },\n })\n .catch(() => {\n this.log.warn(\"Invalid verification code for password reset\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Invalid or expired verification code\");\n });\n\n // If already verified, this is a code reuse attempt\n if (result.alreadyVerified) {\n this.log.warn(\"Verification code reuse attempt\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Verification code has already been used\");\n }\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(\n body.newPassword,\n );\n\n // Update the identity with new password\n await this.identities(intent.realmName).updateById(intent.identityId, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(intent.realmName).deleteMany({\n userId: { eq: intent.userId },\n });\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n }\n\n // Legacy methods kept for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public async requestPasswordReset(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n await this.createPasswordResetIntent(email, userRealmName);\n return true;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async validateResetToken(\n email: string,\n token: string,\n _userRealmName?: string,\n ): Promise<string> {\n // Verify using verification controller\n const isValid = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => undefined);\n\n if (!isValid?.ok) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n return email;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async resetPassword(\n email: string,\n token: string,\n newPassword: string,\n userRealmName?: string,\n ): Promise<void> {\n // Verify token using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => {\n throw new BadRequestError(\"Invalid or expired reset token\");\n });\n\n // If already verified, this is a token reuse attempt\n if (result.alreadyVerified) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n // Find user and identity\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(newPassword);\n\n // Update the identity with new password\n await this.identities(userRealmName).updateById(identity.id, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(userRealmName).deleteMany({\n userId: { eq: user.id },\n });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, ConflictError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the registration flow.\n */\ninterface RegistrationIntent {\n data: {\n username?: string;\n email?: string;\n phoneNumber?: string;\n firstName?: string;\n lastName?: string;\n picture?: string;\n passwordHash: string;\n };\n requirements: {\n email: boolean;\n phone: boolean;\n captcha: boolean;\n };\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class RegistrationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"registration-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n /**\n * Phase 1: Create a registration intent.\n *\n * Validates the registration data, checks for existing users,\n * creates verification sessions, and stores the intent in cache.\n */\n public async createRegistrationIntent(\n body: RegisterRequest,\n userRealmName?: string,\n ): Promise<RegistrationIntentResponse> {\n this.log.trace(\"Creating registration intent\", {\n email: body.email,\n username: body.username,\n userRealmName,\n });\n\n const realmSettings =\n this.userRealmProvider.getRealm(userRealmName).settings;\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n\n // Check if registration is allowed\n if (realmSettings?.registrationAllowed === false) {\n this.log.warn(\"Registration not allowed for realm\", { userRealmName });\n throw new BadRequestError(\"Registration is not allowed\");\n }\n\n // Validate required fields based on settings\n if (realmSettings?.usernameRequired && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (realmSettings?.emailRequired !== false && !body.email) {\n this.log.debug(\"Registration rejected: email required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Email is required\");\n }\n\n if (realmSettings?.phoneRequired && !body.phoneNumber) {\n this.log.debug(\"Registration rejected: phone required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Phone number is required\");\n }\n\n // Check for existing users (username, email, phone)\n await this.checkUserAvailability(body, userRealmName);\n\n // Hash the password\n const passwordHash = await this.cryptoProvider.hashPassword(body.password);\n\n // Determine requirements based on realm settings\n const requirements = {\n email: realmSettings?.verifyEmailRequired === true && !!body.email,\n phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,\n captcha: false, // Always false for now\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber);\n }\n\n // Generate intent ID and expiration\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Store intent in cache\n const intent: RegistrationIntent = {\n data: {\n username: body.username,\n email: body.email,\n phoneNumber: body.phoneNumber,\n firstName: body.firstName,\n lastName: body.lastName,\n picture: body.picture,\n passwordHash,\n },\n requirements,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Registration intent created\", {\n intentId,\n email: body.email,\n username: body.username,\n requiresEmailVerification: requirements.email,\n requiresPhoneVerification: requirements.phone,\n });\n\n return {\n intentId,\n expectCaptcha: requirements.captcha,\n expectEmailVerification: requirements.email,\n expectPhoneVerification: requirements.phone,\n expiresAt,\n };\n }\n\n /**\n * Phase 2: Complete registration using an intent.\n *\n * Validates all requirements (verification codes, captcha),\n * creates the user and credentials, and returns the user.\n */\n public async completeRegistration(\n body: CompleteRegistrationRequest,\n ): Promise<UserEntity> {\n this.log.trace(\"Completing registration\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired registration intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired registration intent\",\n });\n }\n\n const userRealmName = intent.realmName;\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n const identityRepository =\n this.userRealmProvider.identityRepository(userRealmName);\n\n // Validate email verification if required\n if (intent.requirements.email) {\n if (!body.emailCode) {\n this.log.debug(\"Registration completion missing email code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Email verification code is required\");\n }\n\n if (!intent.data.email) {\n throw new BadRequestError(\"Email is missing from registration intent\");\n }\n\n await this.verifyEmailCode(intent.data.email, body.emailCode);\n }\n\n // Validate phone verification if required\n if (intent.requirements.phone) {\n if (!body.phoneCode) {\n this.log.debug(\"Registration completion missing phone code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Phone verification code is required\");\n }\n\n if (!intent.data.phoneNumber) {\n throw new BadRequestError(\n \"Phone number is missing from registration intent\",\n );\n }\n\n await this.verifyPhoneCode(intent.data.phoneNumber, body.phoneCode);\n }\n\n // Validate captcha if required (placeholder for future implementation)\n if (intent.requirements.captcha) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n // TODO: Validate captcha token\n }\n\n // Final availability check (race condition guard)\n await this.checkUserAvailability(\n {\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n },\n userRealmName,\n );\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Create the user\n const user = await userRepository.create({\n realm: userRealmName,\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n firstName: intent.data.firstName,\n lastName: intent.data.lastName,\n picture: intent.data.picture,\n roles: [\"user\"],\n enabled: true,\n emailVerified: intent.requirements.email, // Marked as verified if we verified during registration\n });\n\n // Create credentials identity\n await identityRepository.create({\n userId: user.id,\n provider: \"credentials\",\n password: intent.data.passwordHash,\n });\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n return user;\n }\n\n /**\n * Check if username, email, and phone are available.\n */\n protected async checkUserAvailability(\n body: Pick<RegisterRequest, \"username\" | \"email\" | \"phoneNumber\">,\n userRealmName?: string,\n ): Promise<void> {\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: body.username });\n throw new ConflictError(\"User with this username already exists\");\n }\n }\n\n if (body.email) {\n const existingUser = await userRepository\n .findOne({ where: { email: { eq: body.email } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: body.email });\n throw new ConflictError(\"User with this email already exists\");\n }\n }\n\n if (body.phoneNumber) {\n const existingUser = await userRepository\n .findOne({ where: { phoneNumber: { eq: body.phoneNumber } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: body.phoneNumber,\n });\n throw new ConflictError(\"User with this phone number already exists\");\n }\n }\n }\n\n /**\n * Send email verification code.\n */\n protected async sendEmailVerification(email: string): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Email verification code sent\", { email });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send email verification code\", error);\n }\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(phoneNumber: string): Promise<void> {\n this.log.debug(\"Sending phone verification code\", { phoneNumber });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber },\n });\n\n await this.userNotifications.phoneVerification.push({\n contact: phoneNumber,\n variables: {\n phoneNumber,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Phone verification code sent\", { phoneNumber });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send phone verification code\", {\n phoneNumber,\n error,\n });\n }\n }\n\n /**\n * Verify email code using verification service.\n */\n protected async verifyEmailCode(email: string, code: string): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification code\", { email });\n throw new BadRequestError(\"Invalid or expired email verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification code already used\", { email });\n throw new BadRequestError(\n \"Email verification code has already been used\",\n );\n }\n }\n\n /**\n * Verify phone code using verification service.\n */\n protected async verifyPhoneCode(\n phoneNumber: string,\n code: string,\n ): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid phone verification code\", { phoneNumber });\n throw new BadRequestError(\"Invalid or expired phone verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Phone verification code already used\", { phoneNumber });\n throw new BadRequestError(\n \"Phone verification code has already been used\",\n );\n }\n }\n}\n","import { $inject } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $logger } from \"alepha/logger\";\nimport { type Page, parseQueryString } from \"alepha/orm\";\nimport { BadRequestError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CreateUser } from \"../schemas/createUserSchema.ts\";\nimport type { UpdateUser } from \"../schemas/updateUserSchema.ts\";\nimport type { UserQuery } from \"../schemas/userQuerySchema.ts\";\n\nexport class UserService {\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n /**\n * Request email verification for a user.\n * @param email - The email address to verify.\n * @param userRealmName - Optional realm name.\n * @param method - The verification method: \"code\" (default) or \"link\".\n * @param verifyUrl - Base URL for verification link (required when method is \"link\").\n */\n public async requestEmailVerification(\n email: string,\n userRealmName?: string,\n method: \"code\" | \"link\" = \"code\",\n verifyUrl?: string,\n ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n this.log.debug(\"Email verification requested for non-existent user\", {\n email,\n });\n return true;\n }\n\n if (user.emailVerified) {\n this.log.debug(\"Email verification requested for already verified user\", {\n email,\n userId: user.id,\n });\n return true;\n }\n\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: method },\n body: { target: email },\n });\n\n if (method === \"link\") {\n // Build verification URL with token\n const url = new URL(verifyUrl || \"/verify-email\", \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = verifyUrl\n ? `${verifyUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications.emailVerificationLink.push({\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", {\n email,\n userId: user.id,\n });\n }\n } catch (error) {\n // Silent fail for security\n this.log.warn(\"Failed to send email verification\", { email, error });\n }\n\n return true;\n }\n\n /**\n * Verify a user's email using a valid verification token.\n * Supports both code (6-digit) and link (UUID) verification tokens.\n */\n public async verifyEmail(\n email: string,\n token: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Verifying email\", { email, userRealmName });\n\n // Detect verification type based on token format\n // Codes are 6-digit numbers, links are UUIDs\n const isCode = /^\\d{6}$/.test(token);\n const type = isCode ? \"code\" : \"link\";\n\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type },\n body: { target: email, token },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification token\", { email, type });\n throw new BadRequestError(\"Invalid or expired verification token\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification token already used\", { email });\n throw new BadRequestError(\"Invalid or expired verification token\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n await this.users(userRealmName).updateById(user.id, {\n emailVerified: true,\n });\n\n this.log.info(\"Email verified\", { email, userId: user.id, type });\n }\n\n /**\n * Check if an email is verified.\n */\n public async isEmailVerified(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n this.log.trace(\"Checking if email is verified\", { email, userRealmName });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n return user?.emailVerified ?? false;\n }\n\n /**\n * Find users with pagination and filtering.\n */\n public async findUsers(\n q: UserQuery = {},\n userRealmName?: string,\n ): Promise<Page<UserEntity>> {\n this.log.trace(\"Finding users\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.users(userRealmName).createQueryWhere();\n\n if (q.email) {\n where.email = { like: q.email };\n }\n\n if (q.enabled !== undefined) {\n where.enabled = { eq: q.enabled };\n }\n\n if (q.emailVerified !== undefined) {\n where.emailVerified = { eq: q.emailVerified };\n }\n\n if (q.roles) {\n where.roles = { arrayContains: q.roles };\n }\n\n if (q.query) {\n Object.assign(where, parseQueryString(q.query));\n }\n\n const result = await this.users(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Users found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a user by ID.\n */\n public async getUserById(\n id: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Getting user by ID\", { id, userRealmName });\n return await this.users(userRealmName).findById(id);\n }\n\n /**\n * Create a new user.\n */\n public async createUser(\n data: CreateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Creating user\", {\n username: data.username,\n email: data.email,\n userRealmName,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n // TODO: one query instead of 3\n\n // Check for existing user based on provided unique fields\n if (data.username) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { username: { eq: data.username } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: data.username });\n throw new BadRequestError(\"User with this username already exists\");\n }\n }\n\n if (data.email) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: data.email } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: data.email });\n throw new BadRequestError(\"User with this email already exists\");\n }\n }\n\n if (data.phoneNumber) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { phoneNumber: { eq: data.phoneNumber } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: data.phoneNumber,\n });\n throw new BadRequestError(\"User with this phone number already exists\");\n }\n }\n\n const user = await this.users(userRealmName).create({\n ...data,\n roles: data.roles ?? [\"user\"], // TODO: Default roles from realm settings\n realm: realm.name,\n });\n\n this.log.info(\"User created\", {\n userId: user.id,\n username: user.username,\n email: user.email,\n });\n\n return user;\n }\n\n /**\n * Update an existing user.\n */\n public async updateUser(\n id: string,\n data: UpdateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Updating user\", { id, userRealmName });\n await this.getUserById(id, userRealmName);\n\n const user = await this.users(userRealmName).updateById(id, data);\n this.log.debug(\"User updated\", { userId: id });\n return user;\n }\n\n /**\n * Delete a user by ID.\n */\n public async deleteUser(id: string, userRealmName?: string): Promise<void> {\n this.log.trace(\"Deleting user\", { id, userRealmName });\n await this.getUserById(id, userRealmName);\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { pg } from \"alepha/orm\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { completePasswordResetRequestSchema } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport { completeRegistrationRequestSchema } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport { createUserSchema } from \"../schemas/createUserSchema.ts\";\nimport { passwordResetIntentResponseSchema } from \"../schemas/passwordResetIntentResponseSchema.ts\";\nimport { registerQuerySchema } from \"../schemas/registerQuerySchema.ts\";\nimport { registerRequestSchema } from \"../schemas/registerRequestSchema.ts\";\nimport { registrationIntentResponseSchema } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { updateUserSchema } from \"../schemas/updateUserSchema.ts\";\nimport { userQuerySchema } from \"../schemas/userQuerySchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { CredentialService } from \"../services/CredentialService.ts\";\nimport { RegistrationService } from \"../services/RegistrationService.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class UserController {\n protected readonly url = \"/users\";\n protected readonly group = \"users\";\n protected readonly credentialService = $inject(CredentialService);\n protected readonly userService = $inject(UserService);\n protected readonly registrationService = $inject(RegistrationService);\n\n /**\n * Phase 1: Create a registration intent.\n * Validates data, creates verification sessions, and stores intent in cache.\n */\n public readonly createRegistrationIntent = $action({\n method: \"POST\",\n path: `${this.url}/register`,\n secure: false,\n schema: {\n body: registerRequestSchema,\n query: registerQuerySchema,\n response: registrationIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.registrationService.createRegistrationIntent(\n body,\n query.userRealmName,\n ),\n });\n\n /**\n * Find users with pagination and filtering.\n */\n public readonly findUsers = $action({\n path: this.url,\n group: this.group,\n description: \"Find users with pagination and filtering\",\n schema: {\n query: t.extend(userQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: pg.page(userResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.userService.findUsers(q, userRealmName);\n },\n });\n\n /**\n * Get a user by ID.\n */\n public readonly getUser = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a user by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userResourceSchema,\n },\n handler: ({ params, query }) =>\n this.userService.getUserById(params.id, query.userRealmName),\n });\n\n /**\n * Create a new user.\n */\n public readonly createUser = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n description: \"Create a new user\",\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: createUserSchema,\n response: userResourceSchema,\n },\n handler: ({ body, query }) =>\n this.userService.createUser(body, query.userRealmName),\n });\n\n /**\n * Phase 2: Complete registration using an intent.\n * Validates verification codes and creates the user.\n */\n public readonly createUserFromIntent = $action({\n method: \"POST\",\n path: `${this.url}/register/complete`,\n secure: false,\n schema: {\n body: completeRegistrationRequestSchema,\n response: userResourceSchema,\n },\n handler: ({ body }) => this.registrationService.completeRegistration(body),\n });\n\n /**\n * Update a user.\n */\n public readonly updateUser = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Update a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: updateUserSchema,\n response: userResourceSchema,\n },\n handler: ({ params, body, query }) =>\n this.userService.updateUser(params.id, body, query.userRealmName),\n });\n\n /**\n * Delete a user.\n */\n public readonly deleteUser = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.userService.deleteUser(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n\n /**\n * Phase 1: Create a password reset intent.\n * Validates email, sends verification code, and stores intent in cache.\n */\n public readonly createPasswordResetIntent = $action({\n method: \"POST\",\n path: `${this.url}/password-reset`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: passwordResetIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.credentialService.createPasswordResetIntent(\n body.email,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete password reset using an intent.\n * Validates verification code, updates password, and invalidates sessions.\n */\n public readonly completePasswordReset = $action({\n method: \"POST\",\n path: `${this.url}/password-reset/complete`,\n secure: false,\n schema: {\n body: completePasswordResetRequestSchema,\n response: okSchema,\n },\n handler: async ({ body }) => {\n await this.credentialService.completePasswordReset(body);\n return { ok: true };\n },\n });\n\n // Legacy endpoints for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public requestPasswordReset = $action({\n path: \"/users/password-reset/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.requestPasswordReset(\n body.email,\n query.userRealmName,\n );\n\n return {\n success: true,\n message:\n \"If an account exists with this email, a password reset code has been sent.\",\n };\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public validateResetToken = $action({\n path: \"/users/password-reset/validate\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n token: t.string(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n valid: t.boolean(),\n email: t.optional(t.email()),\n }),\n },\n handler: async ({ query }) => {\n try {\n const email = await this.credentialService.validateResetToken(\n query.email,\n query.token,\n query.userRealmName,\n );\n return {\n valid: true,\n email,\n };\n } catch {\n return {\n valid: false,\n };\n }\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public resetPassword = $action({\n path: \"/users/password-reset/reset\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n newPassword: t.string({ minLength: 8 }),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.resetPassword(\n body.email,\n body.token,\n body.newPassword,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Password has been reset successfully. Please log in.\",\n };\n },\n });\n\n /**\n * Request email verification.\n * Generates a verification token using verification service and sends an email to the user.\n * @param method - The verification method: \"code\" (default) sends a 6-digit code, \"link\" sends a clickable verification link.\n * @param verifyUrl - Required when method is \"link\". The base URL for the verification link. Token and email will be appended as query params.\n */\n public requestEmailVerification = $action({\n path: \"/users/email-verification/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n method: t.optional(\n t.enum([\"code\", \"link\"], {\n default: \"code\",\n description:\n 'Verification method: \"code\" sends a 6-digit code, \"link\" sends a clickable verification link.',\n }),\n ),\n verifyUrl: t.optional(\n t.string({\n description:\n 'Base URL for verification link. Required when method is \"link\". Token and email will be appended as query params.',\n }),\n ),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n const method = query.method ?? \"code\";\n await this.userService.requestEmailVerification(\n body.email,\n query.userRealmName,\n method,\n query.verifyUrl,\n );\n\n return {\n success: true,\n message:\n method === \"link\"\n ? \"If an account exists with this email, a verification link has been sent.\"\n : \"If an account exists with this email, a verification code has been sent.\",\n };\n },\n });\n\n /**\n * Verify email with a valid token.\n * Updates the user's emailVerified status.\n */\n public verifyEmail = $action({\n path: \"/users/email-verification/verify\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.userService.verifyEmail(\n body.email,\n body.token,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Email has been verified successfully.\",\n };\n },\n });\n\n /**\n * Check if an email is verified.\n */\n public checkEmailVerification = $action({\n path: \"/users/email-verification/check\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n verified: t.boolean(),\n }),\n },\n handler: async ({ query }) => {\n const verified = await this.userService.isEmailVerified(\n query.email,\n query.userRealmName,\n );\n\n return {\n verified,\n };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const userRealmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n});\n\nexport type UserRealmConfig = Static<typeof userRealmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport { userRealmConfigSchema } from \"../schemas/userRealmConfigSchema.ts\";\n\n/**\n * Controller for exposing realm configuration.\n * Uses $route instead of $action to keep endpoints hidden from API documentation.\n */\nexport class UserRealmController {\n protected readonly url = \"/realms\";\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n\n /**\n * Get realm configuration settings.\n * This endpoint is not exposed in the API documentation.\n */\n public readonly getRealmConfig = $action({\n method: \"GET\",\n path: `${this.url}/config`,\n secure: false,\n cache: {\n etag: true,\n control: {\n maxAge: [24, \"hours\"],\n },\n },\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userRealmConfigSchema,\n },\n handler: ({ query }) => {\n const { name: realmName, settings } = this.userRealmProvider.getRealm(\n query.userRealmName,\n );\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n path: `${this.url}/check-username`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.text()),\n }),\n body: t.object({\n username: t.text(),\n }),\n response: t.object({\n available: t.boolean(),\n }),\n },\n handler: async ({ query, body }) => {\n const realmName = query.userRealmName;\n const userRepository = this.userRealmProvider.userRepository(realmName);\n\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n\n return {\n available: !existingUser,\n };\n },\n });\n}\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { FileController } from \"alepha/api/files\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { type ServerRequest, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\n\nexport class SessionService {\n protected readonly alepha = $inject(Alepha);\n protected readonly fsp = $inject(FileSystemProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly fileController = $client<FileController>();\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Random delay to prevent timing attacks (50-200ms)\n * Uses cryptographically secure random number generation\n */\n protected randomDelay(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, randomInt(50, 201)));\n }\n\n /**\n * Validate user credentials and return the user if valid.\n */\n public async login(\n provider: string,\n username: string,\n password: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n const { settings, name } = this.userRealmProvider.getRealm(userRealmName);\n const isEmail = username.includes(\"@\");\n const isPhone = /^[+\\d][\\d\\s()-]+$/.test(username);\n const isUsername = !isEmail && !isPhone;\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.usernameEnabled !== false && isUsername) {\n where.username = username;\n } else if (settings.emailEnabled !== false && isEmail) {\n where.email = username;\n } else if (settings.phoneEnabled === true && isPhone) {\n where.phoneNumber = username;\n } else {\n this.log.warn(\"Invalid login identifier format\", {\n provider,\n username,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const user = await users.findOne({ where }).catch(() => undefined);\n if (!user) {\n this.log.warn(\"User not found during login attempt\", {\n provider,\n username,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.findOne({\n where: {\n provider: { eq: provider },\n userId: { eq: user.id },\n },\n });\n\n const storedPassword = identity.password;\n if (!storedPassword) {\n this.log.error(\"Identity has no password configured\", {\n provider,\n username,\n identityId: identity.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const valid = await this.cryptoProvider.verifyPassword(\n password,\n storedPassword,\n );\n\n if (!valid) {\n this.log.warn(\"Invalid password during login attempt\", {\n provider,\n username,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\n // TODO: store failed login attempts (with request data) and lock account after threshold\n throw error;\n }\n\n this.log.warn(\"Error during login attempt\", error);\n\n throw new InvalidCredentialsError();\n }\n }\n\n public async createSession(\n user: UserAccount,\n expiresIn: number,\n userRealmName?: string,\n ) {\n this.log.trace(\"Creating session\", { userId: user.id, expiresIn });\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n const refreshToken = this.cryptoProvider.randomUUID();\n\n const expiresAt = this.dateTimeProvider\n .now()\n .add(expiresIn, \"seconds\")\n .toISOString();\n\n const session = await this.sessions(userRealmName).create({\n userId: user.id,\n expiresAt,\n ip: request?.ip,\n userAgent: request?.userAgent,\n refreshToken,\n });\n\n this.log.info(\"Session created\", {\n sessionId: session.id,\n userId: user.id,\n ip: request?.ip,\n });\n\n return {\n refreshToken,\n sessionId: session.id,\n };\n }\n\n public async refreshSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Refreshing session\");\n\n const session = await this.sessions(userRealmName).findOne({\n where: {\n refreshToken: { eq: refreshToken },\n },\n });\n\n const now = this.dateTimeProvider.now();\n const expiresAt = this.dateTimeProvider.of(session.expiresAt);\n\n if (this.dateTimeProvider.of(session.expiresAt) < now) {\n this.log.debug(\"Session expired during refresh\", {\n sessionId: session.id,\n userId: session.userId,\n });\n await this.sessions(userRealmName).deleteById(refreshToken);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n this.log.debug(\"Session refreshed\", {\n sessionId: session.id,\n userId: session.userId,\n });\n\n return {\n user,\n expiresIn: expiresAt.unix() - now.unix(),\n sessionId: session.id,\n };\n }\n\n public async deleteSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Deleting session\");\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n }\n\n public async link(\n provider: string,\n profile: OAuth2Profile,\n userRealmName?: string,\n ) {\n this.log.trace(\"Linking OAuth2 profile\", {\n provider,\n profileSub: profile.sub,\n email: profile.email,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities\n .findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n })\n .catch(() => undefined);\n\n // existing identity found, return associated user\n if (identity) {\n this.log.debug(\"Existing identity found\", {\n provider,\n identityId: identity.id,\n userId: identity.userId,\n });\n return users.findById(identity.userId);\n }\n\n if (!profile.email) {\n this.log.debug(\"OAuth2 profile has no email, returning profile as-is\", {\n provider,\n profileSub: profile.sub,\n });\n return {\n id: profile.sub,\n ...profile,\n };\n }\n\n const existing = await users\n .findOne({\n where: {\n email: profile.email,\n },\n })\n .catch(() => undefined);\n\n if (existing) {\n this.log.debug(\"Linking OAuth2 profile to existing user by email\", {\n provider,\n profileSub: profile.sub,\n userId: existing.id,\n email: profile.email,\n });\n await identities.create({\n provider,\n providerUserId: profile.sub,\n userId: existing.id,\n });\n return existing;\n }\n\n // TODO: check usernames for uniqueness, add suffix if needed (e.g. john.doe1)\n // TODO: username must match a-zA-Z0-9._-\n\n const user = await users.create({\n realm: realm.name,\n username: profile.email.split(\"@\")[0],\n email: profile.email,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: [\"user\"], // TODO: make default roles configurable via realm settings\n });\n\n if (profile.picture) {\n this.log.debug(\"Fetching user profile picture from OAuth2 provider\", {\n provider,\n url: profile.picture,\n });\n try {\n const response = await fetch(profile.picture);\n const file = this.fsp.createFile({\n response,\n });\n if (response.ok && response.body) {\n const fileEntity = await this.fileController.uploadFile(\n {\n body: { file },\n },\n {\n user,\n },\n );\n await users.updateById(user.id, { picture: fileEntity.id });\n }\n } catch (error) {\n this.log.warn(\"Failed to fetch user profile picture\", error);\n }\n }\n\n await this.identities(userRealmName).create({\n provider,\n providerUserId: profile.sub,\n userId: user.id,\n });\n\n this.log.info(\"New user created via OAuth2 link\", {\n provider,\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n return user;\n }\n}\n","import { $context } from \"alepha\";\nimport { AlephaApiAudits } from \"alepha/api/audits\";\nimport { AlephaApiFiles } from \"alepha/api/files\";\nimport { AlephaApiJobs } from \"alepha/api/jobs\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $realm,\n type RealmPrimitive,\n type RealmPrimitiveOptions,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authCredentials,\n $authGithub,\n $authGoogle,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport type { identities } from \"../entities/identities.ts\";\nimport type { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, type users } from \"../entities/users.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\n\nexport type UserRealmPrimitive = RealmPrimitive & WithLinkFn & WithLoginFn;\n\n/**\n * Already configured realm for user management.\n *\n * Realm contains two roles: `admin` and `user`.\n *\n * - `admin`: Has full access to all resources and permissions.\n * - `user`: Has access to their own resources and permissions, but cannot access admin-level resources.\n *\n * Realm uses session management for handling user sessions.\n *\n * Environment Variables:\n * - `APP_SECRET`: Secret key for signing tokens (if not provided in options).\n */\n\nexport const $userRealm = (\n options: UserRealmOptions = {},\n): UserRealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const userRealmProvider = alepha.inject(UserRealmProvider);\n const name = options.realm?.name ?? DEFAULT_USER_REALM_NAME;\n\n const userRealm = userRealmProvider.register(name, options);\n\n if (options.modules?.audits) {\n alepha.with(AlephaApiAudits);\n }\n\n if (options.modules?.files) {\n alepha.with(AlephaApiFiles);\n }\n\n if (options.modules?.jobs) {\n alepha.with(AlephaApiJobs);\n }\n\n const realm: UserRealmPrimitive = $realm({\n ...options.realm,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n roles: options.realm?.roles ?? [\n {\n name: \"admin\",\n permissions: [\n {\n name: \"*\",\n },\n ],\n },\n {\n name: \"user\",\n permissions: [\n {\n name: \"*\",\n ownership: true,\n exclude: [\"admin:*\"],\n },\n ],\n },\n ],\n settings: {\n accessToken: {\n expiration: [15, \"minutes\"],\n },\n refreshToken: {\n expiration: [30, \"days\"],\n },\n onCreateSession: async (user, config) => {\n return sessionService.createSession(user, config.expiresIn);\n },\n onRefreshSession: async (refreshToken) => {\n return sessionService.refreshSession(refreshToken);\n },\n onDeleteSession: async (refreshToken) => {\n await sessionService.deleteSession(refreshToken);\n },\n ...options.realm?.settings,\n },\n });\n\n realm.link = (name: string) => {\n return (ctx: LinkAccountOptions) =>\n sessionService.link(name, ctx.user, realm.name);\n };\n\n realm.login = (name: string) => {\n return (credentials: Credentials) => {\n return sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n };\n };\n\n const identities = options.identities ?? {\n credentials: true,\n };\n\n if (identities) {\n const auth: Record<string, AuthPrimitive> = {};\n if (identities.credentials) {\n auth.credentials = $authCredentials(realm);\n } else {\n // if credentials auth is disabled, disable registration as well\n userRealm.settings.registrationAllowed = false;\n }\n\n if (identities.google) {\n auth.google = $authGoogle(realm);\n }\n\n if (identities.github) {\n auth.github = $authGithub(realm);\n }\n\n alepha.with(() => auth);\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface UserRealmOptions {\n /**\n * Secret key for signing tokens.\n *\n * If not provided, the secret from the SecurityProvider will be used (usually from the APP_SECRET environment variable).\n */\n secret?: string;\n\n /**\n * Realm configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n realm?: Partial<RealmPrimitiveOptions>;\n\n /**\n * Override entities.\n */\n entities?: {\n users?: Repository<typeof users.schema>;\n identities?: Repository<typeof identities.schema>;\n sessions?: Repository<typeof sessions.schema>;\n };\n\n settings?: Partial<RealmAuthSettings>;\n\n identities?: {\n credentials?: true;\n google?: true;\n github?: true;\n };\n\n modules?: {\n files?: boolean;\n audits?: boolean;\n jobs?: boolean;\n };\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const loginSchema = t.object({\n username: t.text({\n minLength: 3,\n maxLength: 100,\n description: \"Username or email address for login\",\n }),\n password: t.text({\n minLength: 6,\n description: \"User password\",\n }),\n});\n\nexport type LoginInput = Static<typeof loginSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const registerSchema = t.object({\n username: t.string({\n minLength: 3,\n maxLength: 20,\n pattern: /^[a-zA-Z0-9_]+$/,\n description: \"Username for the new account\",\n }),\n email: t.email({\n description: \"Email address for the new account\",\n }),\n password: t.string({\n minLength: 8,\n description: \"Password for the new account\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the password\",\n }),\n firstName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's last name\",\n }),\n ),\n});\n\nexport type RegisterInput = Static<typeof registerSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const resetPasswordRequestSchema = t.object({\n email: t.email({\n description: \"Email address to send password reset link\",\n }),\n});\n\nexport const resetPasswordSchema = t.object({\n token: t.string({\n description: \"Password reset token from email\",\n }),\n password: t.string({\n minLength: 8,\n description: \"New password\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the new password\",\n }),\n});\n\nexport type ResetPasswordRequest = Static<typeof resetPasswordRequestSchema>;\nexport type ResetPasswordInput = Static<typeof resetPasswordSchema>;\n","import { $module } from \"alepha\";\nimport { AlephaApiNotifications } from \"alepha/api/notifications\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport { AlephaEmail } from \"alepha/email\";\nimport { AlephaServerCompress } from \"alepha/server/compress\";\nimport { AlephaServerHelmet } from \"alepha/server/helmet\";\nimport { IdentityController } from \"./controllers/IdentityController.ts\";\nimport { SessionController } from \"./controllers/SessionController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { UserRealmController } from \"./controllers/UserRealmController.ts\";\nimport { UserNotifications } from \"./notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"./providers/UserRealmProvider.ts\";\nimport { CredentialService } from \"./services/CredentialService.ts\";\nimport { IdentityService } from \"./services/IdentityService.ts\";\nimport { RegistrationService } from \"./services/RegistrationService.ts\";\nimport { SessionCrudService } from \"./services/SessionCrudService.ts\";\nimport { SessionService } from \"./services/SessionService.ts\";\nimport { UserService } from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/realmAuthSettingsAtom.ts\";\nexport * from \"./controllers/IdentityController.ts\";\nexport * from \"./controllers/SessionController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./controllers/UserRealmController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./primitives/$userRealm.ts\";\nexport * from \"./providers/UserRealmProvider.ts\";\nexport * from \"./schemas/completePasswordResetRequestSchema.ts\";\nexport * from \"./schemas/completeRegistrationRequestSchema.ts\";\nexport * from \"./schemas/createUserSchema.ts\";\nexport * from \"./schemas/identityQuerySchema.ts\";\nexport * from \"./schemas/identityResourceSchema.ts\";\nexport * from \"./schemas/loginSchema.ts\";\nexport * from \"./schemas/passwordResetIntentResponseSchema.ts\";\nexport * from \"./schemas/registerSchema.ts\";\nexport * from \"./schemas/registrationIntentResponseSchema.ts\";\nexport * from \"./schemas/resetPasswordSchema.ts\";\nexport * from \"./schemas/sessionQuerySchema.ts\";\nexport * from \"./schemas/sessionResourceSchema.ts\";\nexport * from \"./schemas/updateUserSchema.ts\";\nexport * from \"./schemas/userQuerySchema.ts\";\nexport * from \"./schemas/userRealmConfigSchema.ts\";\nexport * from \"./schemas/userResourceSchema.ts\";\nexport * from \"./services/CredentialService.ts\";\nexport * from \"./services/IdentityService.ts\";\nexport * from \"./services/RegistrationService.ts\";\nexport * from \"./services/SessionCrudService.ts\";\nexport * from \"./services/SessionService.ts\";\nexport * from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides user management API endpoints for Alepha applications.\n *\n * This module includes user CRUD operations, authentication endpoints,\n * password reset functionality, and user profile management capabilities.\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n AlephaApiVerification,\n AlephaApiNotifications,\n AlephaServerHelmet,\n AlephaServerCompress,\n AlephaEmail,\n UserRealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n SessionController,\n IdentityController,\n UserRealmController,\n UserNotifications,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES","identities","users","user","name","identities"],"sources":["../../../src/api/users/schemas/identityQuerySchema.ts","../../../src/api/users/entities/users.ts","../../../src/api/users/entities/identities.ts","../../../src/api/users/schemas/identityResourceSchema.ts","../../../src/api/users/atoms/realmAuthSettingsAtom.ts","../../../src/api/users/entities/sessions.ts","../../../src/api/users/providers/UserRealmProvider.ts","../../../src/api/users/services/IdentityService.ts","../../../src/api/users/controllers/AdminIdentityController.ts","../../../src/api/users/schemas/sessionQuerySchema.ts","../../../src/api/users/schemas/sessionResourceSchema.ts","../../../src/api/users/services/SessionCrudService.ts","../../../src/api/users/controllers/AdminSessionController.ts","../../../src/api/users/schemas/createUserSchema.ts","../../../src/api/users/schemas/updateUserSchema.ts","../../../src/api/users/schemas/userQuerySchema.ts","../../../src/api/users/schemas/userResourceSchema.ts","../../../src/api/users/notifications/UserNotifications.ts","../../../src/api/users/services/UserService.ts","../../../src/api/users/controllers/AdminUserController.ts","../../../src/api/users/schemas/completePasswordResetRequestSchema.ts","../../../src/api/users/schemas/completeRegistrationRequestSchema.ts","../../../src/api/users/schemas/passwordResetIntentResponseSchema.ts","../../../src/api/users/schemas/registerQuerySchema.ts","../../../src/api/users/schemas/registerRequestSchema.ts","../../../src/api/users/schemas/registrationIntentResponseSchema.ts","../../../src/api/users/services/CredentialService.ts","../../../src/api/users/services/RegistrationService.ts","../../../src/api/users/controllers/UserController.ts","../../../src/api/users/schemas/userRealmConfigSchema.ts","../../../src/api/users/controllers/UserRealmController.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/users/primitives/$userRealm.ts","../../../src/api/users/schemas/loginSchema.ts","../../../src/api/users/schemas/registerSchema.ts","../../../src/api/users/schemas/resetPasswordSchema.ts","../../../src/api/users/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const identityQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n provider: t.optional(t.string()),\n});\n\nexport type IdentityQuery = Static<typeof identityQuerySchema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\n\nexport const DEFAULT_USER_REALM_NAME = \"default\";\n\nexport const users = $entity({\n name: \"users\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n\n realm: db.default(t.text(), DEFAULT_USER_REALM_NAME),\n\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n pattern: \"^[a-zA-Z0-9._-]+$\",\n }),\n ),\n\n email: t.optional(t.string({ format: \"email\" })),\n\n phoneNumber: t.optional(t.e164()),\n\n roles: db.default(t.array(t.string()), []),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n picture: t.optional(t.string()),\n enabled: db.default(t.boolean(), true),\n\n emailVerified: db.default(t.boolean(), false),\n }),\n indexes: [\n { columns: [\"realm\", \"username\"], unique: true },\n { columns: [\"realm\", \"email\"], unique: true },\n { columns: [\"realm\", \"phoneNumber\"], unique: true },\n ],\n});\n\nexport type UserEntity = Static<typeof users.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const identities = $entity({\n name: \"identities\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n password: t.optional(t.text()),\n provider: t.text(),\n providerUserId: t.optional(t.text()),\n providerData: t.optional(t.json()),\n }),\n});\n\nexport type IdentityEntity = Static<typeof identities.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { identities } from \"../entities/identities.ts\";\n\nexport const identityResourceSchema = t.omit(identities.schema, [\"password\"]);\n\nexport type IdentityResource = Static<typeof identityResourceSchema>;\n","import { $atom, type Static, t } from \"alepha\";\n\nexport const realmAuthSettingsAtom = $atom({\n name: \"alepha.api.users.realmAuthSettings\",\n schema: t.object({\n // Branding and display settings\n displayName: t.optional(\n t.string({\n description:\n \"Display name shown on auth pages (e.g., 'Customer Portal')\",\n }),\n ),\n description: t.optional(\n t.string({\n description: \"Description shown on auth pages\",\n }),\n ),\n logoUrl: t.optional(\n t.string({\n description: \"Logo URL for auth pages\",\n }),\n ),\n\n // Auth settings\n registrationAllowed: t.boolean({\n description: \"Enable user self-registration\",\n }),\n emailEnabled: t.boolean({\n description: \"Enable email address as a login/registration credential\",\n }),\n emailRequired: t.boolean({\n description: \"Require email address for user accounts\",\n }),\n usernameEnabled: t.boolean({\n description: \"Enable username as a login/registration credential\",\n }),\n usernameRequired: t.boolean({\n description: \"Require username for user accounts\",\n }),\n phoneEnabled: t.boolean({\n description: \"Enable phone number as a login/registration credential\",\n }),\n phoneRequired: t.boolean({\n description: \"Require phone number for user accounts\",\n }),\n verifyEmailRequired: t.boolean({\n description: \"Require email verification for user accounts\",\n }),\n verifyPhoneRequired: t.boolean({\n description: \"Require phone verification for user accounts\",\n }),\n firstNameLastNameEnabled: t.boolean({\n description: \"Enable first and last name for user accounts\",\n }),\n firstNameLastNameRequired: t.boolean({\n description: \"Require first and last name for user accounts\",\n }),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n passwordPolicy: t.object({\n minLength: t.integer({\n description: \"Minimum password length\",\n default: 8,\n minimum: 1,\n }),\n requireUppercase: t.boolean({\n description: \"Require at least one uppercase letter\",\n }),\n requireLowercase: t.boolean({\n description: \"Require at least one lowercase letter\",\n }),\n requireNumbers: t.boolean({\n description: \"Require at least one number\",\n }),\n requireSpecialCharacters: t.boolean({\n description: \"Require at least one special character\",\n }),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n emailEnabled: true,\n emailRequired: true,\n usernameEnabled: false,\n usernameRequired: false,\n phoneEnabled: false,\n phoneRequired: false,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n firstNameLastNameEnabled: false,\n firstNameLastNameRequired: false,\n // TODO: not implemented yet\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n },\n});\n\nexport type RealmAuthSettings = Static<typeof realmAuthSettingsAtom.schema>;\n","import { type Static, t } from \"alepha\";\nimport { $entity, db } from \"alepha/orm\";\nimport { users } from \"./users.ts\";\n\nexport const sessions = $entity({\n name: \"sessions\",\n schema: t.object({\n id: db.primaryKey(t.uuid()),\n version: db.version(),\n createdAt: db.createdAt(),\n updatedAt: db.updatedAt(),\n refreshToken: t.uuid(),\n userId: db.ref(t.uuid(), () => users.cols.id),\n expiresAt: t.datetime(),\n ip: t.optional(t.text()),\n userAgent: t.optional(\n t.object({\n os: t.text(),\n browser: t.text(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n }),\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $bucket } from \"alepha/bucket\";\nimport { $repository, type Repository } from \"alepha/orm\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { identities } from \"../entities/identities.ts\";\nimport { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, users } from \"../entities/users.ts\";\nimport type { UserRealmOptions } from \"../primitives/$userRealm.ts\";\n\nexport interface UserRealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface UserRealm {\n name: string;\n repositories: UserRealmRepositories;\n settings: RealmAuthSettings;\n}\n\nexport class UserRealmProvider {\n protected readonly alepha = $inject(Alepha);\n // Default repositories using $repository() for eager initialization\n protected readonly defaultIdentities = $repository(identities);\n protected readonly defaultSessions = $repository(sessions);\n protected readonly defaultUsers = $repository(users);\n\n protected realms = new Map<string, UserRealm>();\n\n public avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n\n protected readonly onConfigure = $hook({\n on: \"configure\",\n handler: () => {\n this.alepha.store.set(\"alepha.server.security.system.user\", {\n id: \"00000000-0000-0000-0000-000000000000\",\n name: \"system\",\n roles: [\"admin\"], // TODO: use realm config\n });\n },\n });\n\n public register(\n userRealmName: string,\n userRealmOptions: UserRealmOptions = {},\n ) {\n this.realms.set(userRealmName, {\n name: userRealmName,\n repositories: {\n identities:\n userRealmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: userRealmOptions.entities?.sessions ?? this.defaultSessions,\n users: userRealmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...userRealmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...userRealmOptions.settings?.passwordPolicy,\n },\n },\n });\n return this.getRealm(userRealmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(userRealmName = DEFAULT_USER_REALM_NAME): UserRealm {\n let realm = this.realms.get(userRealmName);\n\n if (!realm) {\n // Auto-register default realm for backward compatibility\n const realms = Array.from(this.realms.values());\n const firstRealm = realms[0];\n if (userRealmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(userRealmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing user realm '${userRealmName}', please declare $userRealm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(userRealmName).repositories.identities;\n }\n\n public sessionRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(userRealmName).repositories.sessions;\n }\n\n public userRepository(\n userRealmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(userRealmName).repositories.users;\n }\n}\n","import { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\n\nexport class IdentityService {\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Find identities with pagination and filtering.\n */\n public async findIdentities(\n q: IdentityQuery = {},\n userRealmName?: string,\n ): Promise<Page<IdentityEntity>> {\n this.log.trace(\"Finding identities\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.identities(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n if (q.provider) {\n where.provider = { like: q.provider };\n }\n\n const result = await this.identities(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Identities found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get an identity by ID.\n */\n public async getIdentityById(\n id: string,\n userRealmName?: string,\n ): Promise<IdentityEntity> {\n this.log.trace(\"Getting identity by ID\", { id, userRealmName });\n const identity = await this.identities(userRealmName).findById(id);\n this.log.debug(\"Identity retrieved\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n return identity;\n }\n\n /**\n * Delete an identity by ID.\n */\n public async deleteIdentity(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting identity\", { id, userRealmName });\n\n // Verify identity exists\n const identity = await this.getIdentityById(id, userRealmName);\n\n await this.identities(userRealmName).deleteById(id);\n this.log.info(\"Identity deleted\", {\n id,\n provider: identity.provider,\n userId: identity.userId,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"update\", {\n userRealm: realm.name,\n resourceId: identity.userId,\n description: `Identity provider disconnected: ${identity.provider}`,\n metadata: {\n identityId: id,\n provider: identity.provider,\n userId: identity.userId,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { identityQuerySchema } from \"../schemas/identityQuerySchema.ts\";\nimport { identityResourceSchema } from \"../schemas/identityResourceSchema.ts\";\nimport { IdentityService } from \"../services/IdentityService.ts\";\n\nexport class AdminIdentityController {\n protected readonly url = \"/identities\";\n protected readonly group = \"admin:identities\";\n protected readonly identityService = $inject(IdentityService);\n\n /**\n * Find identities with pagination and filtering.\n */\n public readonly findIdentities = $action({\n path: this.url,\n group: this.group,\n description: \"Find identities with pagination and filtering\",\n schema: {\n query: t.extend(identityQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(identityResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.identityService.findIdentities(q, userRealmName);\n },\n });\n\n /**\n * Get an identity by ID.\n */\n public readonly getIdentity = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get an identity by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: identityResourceSchema,\n },\n handler: ({ params, query }) =>\n this.identityService.getIdentityById(params.id, query.userRealmName),\n });\n\n /**\n * Delete an identity.\n */\n public readonly deleteIdentity = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete an identity\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.identityService.deleteIdentity(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const sessionQuerySchema = t.extend(pageQuerySchema, {\n userId: t.optional(t.uuid()),\n});\n\nexport type SessionQuery = Static<typeof sessionQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const sessionResourceSchema = t.object({\n id: t.uuid(),\n version: t.number(),\n createdAt: t.datetime(),\n updatedAt: t.datetime(),\n refreshToken: t.uuid(),\n userId: t.uuid(),\n expiresAt: t.datetime(),\n ip: t.optional(t.string()),\n userAgent: t.optional(\n t.object({\n os: t.string(),\n browser: t.string(),\n device: t.enum([\"MOBILE\", \"DESKTOP\", \"TABLET\"]),\n }),\n ),\n});\n\nexport type SessionResource = Static<typeof sessionResourceSchema>;\n","import { $inject } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport type { SessionEntity } from \"../entities/sessions.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n /**\n * Find sessions with pagination and filtering.\n */\n public async findSessions(\n q: SessionQuery = {},\n userRealmName?: string,\n ): Promise<Page<SessionEntity>> {\n this.log.trace(\"Finding sessions\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.sessions(userRealmName).createQueryWhere();\n\n if (q.userId) {\n where.userId = { eq: q.userId };\n }\n\n const result = await this.sessions(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Sessions found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a session by ID.\n */\n public async getSessionById(\n id: string,\n userRealmName?: string,\n ): Promise<SessionEntity> {\n this.log.trace(\"Getting session by ID\", { id, userRealmName });\n const session = await this.sessions(userRealmName).findById(id);\n this.log.debug(\"Session retrieved\", { id, userId: session.userId });\n return session;\n }\n\n /**\n * Delete a session by ID.\n */\n public async deleteSession(\n id: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Deleting session\", { id, userRealmName });\n\n // Verify session exists\n await this.getSessionById(id, userRealmName);\n\n await this.sessions(userRealmName).deleteById(id);\n this.log.info(\"Session deleted\", { id });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { sessionQuerySchema } from \"../schemas/sessionQuerySchema.ts\";\nimport { sessionResourceSchema } from \"../schemas/sessionResourceSchema.ts\";\nimport { SessionCrudService } from \"../services/SessionCrudService.ts\";\n\nexport class AdminSessionController {\n protected readonly url = \"/sessions\";\n protected readonly group = \"admin:sessions\";\n protected readonly sessionService = $inject(SessionCrudService);\n\n /**\n * Find sessions with pagination and filtering.\n */\n public readonly findSessions = $action({\n path: this.url,\n group: this.group,\n description: \"Find sessions with pagination and filtering\",\n schema: {\n query: t.extend(sessionQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(sessionResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.sessionService.findSessions(q, userRealmName);\n },\n });\n\n /**\n * Get a session by ID.\n */\n public readonly getSession = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a session by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: sessionResourceSchema,\n },\n handler: ({ params, query }) =>\n this.sessionService.getSessionById(params.id, query.userRealmName),\n });\n\n /**\n * Delete a session.\n */\n public readonly deleteSession = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a session\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.sessionService.deleteSession(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const createUserSchema = t.omit(users.insertSchema, [\"realm\"]);\n\nexport type CreateUser = Static<typeof createUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const updateUserSchema = t.partial(\n t.omit(users.insertSchema, [\n \"id\",\n \"version\",\n \"createdAt\",\n \"updatedAt\",\n \"username\",\n \"emailVerified\",\n ]),\n);\n\nexport type UpdateUser = Static<typeof updateUserSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const userQuerySchema = t.extend(pageQuerySchema, {\n email: t.optional(t.string()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n roles: t.optional(t.array(t.string())),\n query: t.optional(t.text()),\n});\n\nexport type UserQuery = Static<typeof userQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { users } from \"../entities/users.ts\";\n\nexport const userResourceSchema = users.schema;\n\nexport type UserResource = Static<typeof userResourceSchema>;\n","import { t } from \"alepha\";\nimport { $notification } from \"alepha/api/notifications\";\n\nexport class UserNotifications {\n // Code-based notifications (preferred)\n public readonly passwordReset = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Use the code below to verify your identity:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerification = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a verification code to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Use the code below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0; text-align: center;\">\n\t\t\t\t<span style=\"font-size: 32px; font-weight: bold; letter-spacing: 8px; font-family: monospace; background-color: #f5f5f5; padding: 16px 24px; border-radius: 8px; display: inline-block;\">\n\t\t\t\t\t${it.code}\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t\t<p>This code will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly phoneVerification = $notification({\n category: \"security\",\n description:\n \"SMS sent to users with a verification code to verify their phone number.\",\n critical: true,\n sensitive: true,\n sms: {\n message: (it) =>\n `Your verification code is: ${it.code}. This code expires in ${it.expiresInMinutes} minutes.`,\n },\n schema: t.object({\n phoneNumber: t.string(),\n code: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n // Link-based notifications (alternative)\n public readonly passwordResetLink = $notification({\n category: \"security\",\n description: \"Email sent to users with a link to reset their password.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Reset your password\",\n body: (it) => `\n\t\t\t<h1>Reset Your Password</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>We received a request to reset your password. Click the link below to create a new password:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.resetUrl}\" style=\"background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tReset Password\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.resetUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n resetUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n\n public readonly emailVerificationLink = $notification({\n category: \"security\",\n description:\n \"Email sent to users with a link to verify their email address.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Verify your email address\",\n body: (it) => `\n\t\t\t<h1>Verify Your Email Address</h1>\n\t\t\t<p>Hi ${it.email},</p>\n\t\t\t<p>Thanks for signing up! Click the button below to verify your email address:</p>\n\t\t\t<p style=\"margin: 30px 0;\">\n\t\t\t\t<a href=\"${it.verifyUrl}\" style=\"background-color: #28a745; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;\">\n\t\t\t\t\tVerify Email\n\t\t\t\t</a>\n\t\t\t</p>\n\t\t\t<p>Or copy and paste this link into your browser:</p>\n\t\t\t<p style=\"word-break: break-all; color: #666;\">${it.verifyUrl}</p>\n\t\t\t<p>This link will expire in ${it.expiresInMinutes} minutes.</p>\n\t\t\t<p>If you did not create an account, please ignore this email.</p>\n\t\t\t<p>Best regards,<br>The Team</p>\n\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n verifyUrl: t.string(),\n expiresInMinutes: t.number(),\n }),\n });\n}\n","import { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $logger } from \"alepha/logger\";\nimport { type Page, parseQueryString } from \"alepha/orm\";\nimport { BadRequestError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CreateUser } from \"../schemas/createUserSchema.ts\";\nimport type { UpdateUser } from \"../schemas/updateUserSchema.ts\";\nimport type { UserQuery } from \"../schemas/userQuerySchema.ts\";\n\nexport class UserService {\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n /**\n * Request email verification for a user.\n * @param email - The email address to verify.\n * @param userRealmName - Optional realm name.\n * @param method - The verification method: \"code\" (default) or \"link\".\n * @param verifyUrl - Base URL for verification link (required when method is \"link\").\n */\n public async requestEmailVerification(\n email: string,\n userRealmName?: string,\n method: \"code\" | \"link\" = \"code\",\n verifyUrl?: string,\n ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n this.log.debug(\"Email verification requested for non-existent user\", {\n email,\n });\n return true;\n }\n\n if (user.emailVerified) {\n this.log.debug(\"Email verification requested for already verified user\", {\n email,\n userId: user.id,\n });\n return true;\n }\n\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: method },\n body: { target: email },\n });\n\n if (method === \"link\") {\n // Build verification URL with token\n const url = new URL(verifyUrl || \"/verify-email\", \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = verifyUrl\n ? `${verifyUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications.emailVerificationLink.push({\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n this.log.debug(\"Email verification code sent\", {\n email,\n userId: user.id,\n });\n }\n } catch (error) {\n // Silent fail for security\n this.log.warn(\"Failed to send email verification\", { email, error });\n }\n\n return true;\n }\n\n /**\n * Verify a user's email using a valid verification token.\n * Supports both code (6-digit) and link (UUID) verification tokens.\n */\n public async verifyEmail(\n email: string,\n token: string,\n userRealmName?: string,\n ): Promise<void> {\n this.log.trace(\"Verifying email\", { email, userRealmName });\n\n // Detect verification type based on token format\n // Codes are 6-digit numbers, links are UUIDs\n const isCode = /^\\d{6}$/.test(token);\n const type = isCode ? \"code\" : \"link\";\n\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type },\n body: { target: email, token },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification token\", { email, type });\n throw new BadRequestError(\"Invalid or expired verification token\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification token already used\", { email });\n throw new BadRequestError(\"Invalid or expired verification token\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n await this.users(userRealmName).updateById(user.id, {\n emailVerified: true,\n });\n\n this.log.info(\"Email verified\", { email, userId: user.id, type });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Email verified\",\n metadata: { email, verificationType: type },\n });\n }\n\n /**\n * Check if an email is verified.\n */\n public async isEmailVerified(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n this.log.trace(\"Checking if email is verified\", { email, userRealmName });\n\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n return user?.emailVerified ?? false;\n }\n\n /**\n * Find users with pagination and filtering.\n */\n public async findUsers(\n q: UserQuery = {},\n userRealmName?: string,\n ): Promise<Page<UserEntity>> {\n this.log.trace(\"Finding users\", { query: q, userRealmName });\n q.sort ??= \"-createdAt\";\n\n const where = this.users(userRealmName).createQueryWhere();\n\n if (q.email) {\n where.email = { like: q.email };\n }\n\n if (q.enabled !== undefined) {\n where.enabled = { eq: q.enabled };\n }\n\n if (q.emailVerified !== undefined) {\n where.emailVerified = { eq: q.emailVerified };\n }\n\n if (q.roles) {\n where.roles = { arrayContains: q.roles };\n }\n\n if (q.query) {\n Object.assign(where, parseQueryString(q.query));\n }\n\n const result = await this.users(userRealmName).paginate(\n q,\n { where },\n { count: true },\n );\n\n this.log.debug(\"Users found\", {\n count: result.content.length,\n total: result.page.totalElements,\n });\n\n return result;\n }\n\n /**\n * Get a user by ID.\n */\n public async getUserById(\n id: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Getting user by ID\", { id, userRealmName });\n return await this.users(userRealmName).findById(id);\n }\n\n /**\n * Create a new user.\n */\n public async createUser(\n data: CreateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Creating user\", {\n username: data.username,\n email: data.email,\n userRealmName,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n // TODO: one query instead of 3\n\n // Check for existing user based on provided unique fields\n if (data.username) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { username: { eq: data.username } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: data.username });\n throw new BadRequestError(\"User with this username already exists\");\n }\n }\n\n if (data.email) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: data.email } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: data.email });\n throw new BadRequestError(\"User with this email already exists\");\n }\n }\n\n if (data.phoneNumber) {\n const existingUser = await this.users(userRealmName)\n .findOne({\n where: { phoneNumber: { eq: data.phoneNumber } },\n })\n .catch(() => undefined);\n\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: data.phoneNumber,\n });\n throw new BadRequestError(\"User with this phone number already exists\");\n }\n }\n\n const user = await this.users(userRealmName).create({\n ...data,\n roles: data.roles ?? [\"user\"], // TODO: Default roles from realm settings\n realm: realm.name,\n });\n\n this.log.info(\"User created\", {\n userId: user.id,\n username: user.username,\n email: user.email,\n });\n\n await this.auditService.recordUser(\"create\", {\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User created\",\n metadata: {\n username: user.username,\n email: user.email,\n roles: user.roles,\n },\n });\n\n return user;\n }\n\n /**\n * Update an existing user.\n */\n public async updateUser(\n id: string,\n data: UpdateUser,\n userRealmName?: string,\n ): Promise<UserEntity> {\n this.log.trace(\"Updating user\", { id, userRealmName });\n const before = await this.getUserById(id, userRealmName);\n\n const user = await this.users(userRealmName).updateById(id, data);\n this.log.debug(\"User updated\", { userId: id });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n // Build changes object showing what was updated\n const changes: Record<string, { from: unknown; to: unknown }> = {};\n for (const key of Object.keys(data) as (keyof UpdateUser)[]) {\n if (data[key] !== undefined && before[key] !== data[key]) {\n changes[key] = { from: before[key], to: data[key] };\n }\n }\n\n // Detect role changes for special handling\n const isRoleChange =\n data.roles !== undefined &&\n JSON.stringify(before.roles) !== JSON.stringify(data.roles);\n\n await this.auditService.recordUser(\n isRoleChange ? \"role_change\" : \"update\",\n {\n userRealm: realm.name,\n resourceId: user.id,\n description: isRoleChange\n ? \"User roles changed\"\n : `User updated: ${Object.keys(changes).join(\", \")}`,\n metadata: { changes },\n },\n );\n\n return user;\n }\n\n /**\n * Delete a user by ID.\n */\n public async deleteUser(id: string, userRealmName?: string): Promise<void> {\n this.log.trace(\"Deleting user\", { id, userRealmName });\n const user = await this.getUserById(id, userRealmName);\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"delete\", {\n userRealm: realm.name,\n resourceId: id,\n severity: \"warning\",\n description: \"User deleted\",\n metadata: {\n username: user.username,\n email: user.email,\n },\n });\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { createUserSchema } from \"../schemas/createUserSchema.ts\";\nimport { updateUserSchema } from \"../schemas/updateUserSchema.ts\";\nimport { userQuerySchema } from \"../schemas/userQuerySchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class AdminUserController {\n protected readonly url = \"/users\";\n protected readonly group = \"admin:users\";\n protected readonly userService = $inject(UserService);\n\n /**\n * Find users with pagination and filtering.\n */\n public readonly findUsers = $action({\n path: this.url,\n group: this.group,\n description: \"Find users with pagination and filtering\",\n schema: {\n query: t.extend(userQuerySchema, {\n userRealmName: t.optional(t.string()),\n }),\n response: t.page(userResourceSchema),\n },\n handler: ({ query }) => {\n const { userRealmName, ...q } = query;\n return this.userService.findUsers(q, userRealmName);\n },\n });\n\n /**\n * Get a user by ID.\n */\n public readonly getUser = $action({\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Get a user by ID\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userResourceSchema,\n },\n handler: ({ params, query }) =>\n this.userService.getUserById(params.id, query.userRealmName),\n });\n\n /**\n * Create a new user.\n */\n public readonly createUser = $action({\n method: \"POST\",\n path: this.url,\n group: this.group,\n description: \"Create a new user\",\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: createUserSchema,\n response: userResourceSchema,\n },\n handler: ({ body, query }) =>\n this.userService.createUser(body, query.userRealmName),\n });\n\n /**\n * Update a user.\n */\n public readonly updateUser = $action({\n method: \"PATCH\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Update a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: updateUserSchema,\n response: userResourceSchema,\n },\n handler: ({ params, body, query }) =>\n this.userService.updateUser(params.id, body, query.userRealmName),\n });\n\n /**\n * Delete a user.\n */\n public readonly deleteUser = $action({\n method: \"DELETE\",\n path: `${this.url}/:id`,\n group: this.group,\n description: \"Delete a user\",\n schema: {\n params: t.object({\n id: t.uuid(),\n }),\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: okSchema,\n },\n handler: async ({ params, query }) => {\n await this.userService.deleteUser(params.id, query.userRealmName);\n return { ok: true, id: params.id };\n },\n });\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Request schema for completing a password reset.\n *\n * Requires the intent ID from Phase 1, the verification code,\n * and the new password.\n */\nexport const completePasswordResetRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The intent ID from createPasswordResetIntent\",\n }),\n code: t.string({\n description: \"6-digit verification code sent via email\",\n }),\n newPassword: t.string({\n minLength: 8,\n description: \"New password (minimum 8 characters)\",\n }),\n});\n\nexport type CompletePasswordResetRequest = Static<\n typeof completePasswordResetRequestSchema\n>;\n","import { type Static, t } from \"alepha\";\n\nexport const completeRegistrationRequestSchema = t.object({\n intentId: t.uuid({\n description: \"The registration intent ID from the first phase\",\n }),\n emailCode: t.optional(\n t.string({\n description: \"Email verification code (if email verification required)\",\n }),\n ),\n phoneCode: t.optional(\n t.string({\n description: \"Phone verification code (if phone verification required)\",\n }),\n ),\n captchaToken: t.optional(\n t.string({\n description: \"Captcha token (if captcha required)\",\n }),\n ),\n});\n\nexport type CompleteRegistrationRequest = Static<\n typeof completeRegistrationRequestSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Response schema for password reset intent creation.\n *\n * Contains the intent ID needed for Phase 2 completion,\n * along with expiration time.\n */\nexport const passwordResetIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for this password reset intent\",\n }),\n expiresAt: t.datetime({\n description: \"ISO timestamp when this intent expires\",\n }),\n});\n\nexport type PasswordResetIntentResponse = Static<\n typeof passwordResetIntentResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration query parameters.\n * Allows specifying a custom user realm.\n */\nexport const registerQuerySchema = t.object({\n userRealmName: t.optional(\n t.text({\n description:\n \"The user realm to register the user in (defaults to 'default')\",\n }),\n ),\n});\n\nexport type RegisterQuery = Static<typeof registerQuerySchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\n/**\n * Schema for user registration request body.\n * Password is always required, other fields depend on realm settings.\n */\nexport const registerRequestSchema = t.object({\n // Password is always required\n password: t.string({\n minLength: 8,\n description: \"Password for the account\",\n }),\n\n // Identity fields (requirements depend on realm settings)\n username: t.optional(\n t.string({\n minLength: 3,\n description: \"Unique username for the account\",\n }),\n ),\n\n // Optional contact fields\n email: t.optional(\n t.string({\n format: \"email\",\n description: \"User's email address\",\n }),\n ),\n phoneNumber: t.optional(\n t.string({\n description: \"User's phone number\",\n }),\n ),\n\n // Optional user profile fields\n firstName: t.optional(\n t.string({\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n description: \"User's last name\",\n }),\n ),\n picture: t.optional(\n t.string({\n description: \"User's profile picture URL\",\n }),\n ),\n});\n\nexport type RegisterRequest = Static<typeof registerRequestSchema>;\n","import { type Static, t } from \"alepha\";\n\nexport const registrationIntentResponseSchema = t.object({\n intentId: t.uuid({\n description: \"Unique identifier for the registration intent\",\n }),\n expectCaptcha: t.boolean({\n description: \"Whether captcha verification is required\",\n }),\n expectEmailVerification: t.boolean({\n description: \"Whether email verification is required\",\n }),\n expectPhoneVerification: t.boolean({\n description: \"Whether phone verification is required\",\n }),\n expiresAt: t.datetime({\n description: \"When the registration intent expires\",\n }),\n});\n\nexport type RegistrationIntentResponse = Static<\n typeof registrationIntentResponseSchema\n>;\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CompletePasswordResetRequest } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport type { PasswordResetIntentResponse } from \"../schemas/passwordResetIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the password reset flow.\n */\ninterface PasswordResetIntent {\n email: string;\n userId: string;\n identityId: string;\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class CredentialService {\n protected readonly log = $logger();\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Phase 1: Create a password reset intent.\n *\n * Validates the email, checks for existing user with credentials,\n * sends verification code, and stores the intent in cache.\n *\n * @param email - User's email address\n * @param userRealmName - Optional realm name\n * @returns Intent response with intentId and expiration (always returns for security)\n */\n public async createPasswordResetIntent(\n email: string,\n userRealmName?: string,\n ): Promise<PasswordResetIntentResponse> {\n this.log.trace(\"Creating password reset intent\", { email, userRealmName });\n\n // Generate intent ID and expiration upfront for consistent response\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName)\n .findOne({\n where: { email: { eq: email } },\n })\n .catch(() => undefined);\n\n if (!user) {\n // Silent fail - don't reveal that email doesn't exist\n this.log.debug(\"Password reset requested for non-existent email\", {\n email,\n });\n return { intentId, expiresAt };\n }\n\n // Find the credentials identity for this user\n const identity = await this.identities(userRealmName)\n .findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n })\n .catch(() => undefined);\n\n if (!identity) {\n // User doesn't have credentials identity (maybe OAuth only)\n this.log.debug(\"Password reset requested for user without credentials\", {\n userId: user.id,\n });\n return { intentId, expiresAt };\n }\n\n // Create verification using verification controller\n // This handles: token generation, expiration, rate limiting, cooldown\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n // Send password reset notification with the code\n await this.userNotifications.passwordReset.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n\n // Store intent in cache\n const intent: PasswordResetIntent = {\n email,\n userId: user.id,\n identityId: identity.id,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Password reset intent created\", {\n intentId,\n userId: user.id,\n email,\n });\n } catch (error) {\n // If rate limit or cooldown hit, still return success for security\n this.log.warn(\"Failed to create password reset verification\", error);\n }\n\n return { intentId, expiresAt };\n }\n\n /**\n * Phase 2: Complete password reset using an intent.\n *\n * Validates the verification code, updates the password,\n * and invalidates all existing sessions.\n *\n * @param body - Request body with intentId, code, and newPassword\n */\n public async completePasswordReset(\n body: CompletePasswordResetRequest,\n ): Promise<void> {\n this.log.trace(\"Completing password reset\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired password reset intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired password reset intent\",\n });\n }\n\n // Verify code using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: intent.email, token: body.code },\n })\n .catch(() => {\n this.log.warn(\"Invalid verification code for password reset\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Invalid or expired verification code\");\n });\n\n // If already verified, this is a code reuse attempt\n if (result.alreadyVerified) {\n this.log.warn(\"Verification code reuse attempt\", {\n intentId: body.intentId,\n email: intent.email,\n });\n throw new BadRequestError(\"Verification code has already been used\");\n }\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(\n body.newPassword,\n );\n\n // Update the identity with new password\n await this.identities(intent.realmName).updateById(intent.identityId, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(intent.realmName).deleteMany({\n userId: { eq: intent.userId },\n });\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n\n const realm = this.userRealmProvider.getRealm(intent.realmName);\n\n // Audit: password reset\n await this.auditService.recordUser(\"update\", {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n description: \"Password reset completed\",\n metadata: { email: intent.email },\n });\n\n // Audit: sessions invalidated (security event)\n await this.auditService.record(\"security\", \"sessions_invalidated\", {\n userId: intent.userId,\n userEmail: intent.email,\n userRealm: realm.name,\n resourceId: intent.userId,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n });\n }\n\n // Legacy methods kept for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public async requestPasswordReset(\n email: string,\n userRealmName?: string,\n ): Promise<boolean> {\n await this.createPasswordResetIntent(email, userRealmName);\n return true;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async validateResetToken(\n email: string,\n token: string,\n _userRealmName?: string,\n ): Promise<string> {\n // Verify using verification controller\n const isValid = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => undefined);\n\n if (!isValid?.ok) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n return email;\n }\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public async resetPassword(\n email: string,\n token: string,\n newPassword: string,\n userRealmName?: string,\n ): Promise<void> {\n // Verify token using verification controller\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token },\n })\n .catch(() => {\n throw new BadRequestError(\"Invalid or expired reset token\");\n });\n\n // If already verified, this is a token reuse attempt\n if (result.alreadyVerified) {\n throw new BadRequestError(\"Invalid or expired reset token\");\n }\n\n // Find user and identity\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\n\n // Hash the new password\n const hashedPassword = await this.cryptoProvider.hashPassword(newPassword);\n\n // Update the identity with new password\n await this.identities(userRealmName).updateById(identity.id, {\n password: hashedPassword,\n });\n\n // Invalidate all existing sessions for this user\n await this.sessions(userRealmName).deleteMany({\n userId: { eq: user.id },\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n // Audit: password reset (legacy method)\n await this.auditService.recordUser(\"update\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"Password reset completed (legacy)\",\n metadata: { email },\n });\n\n // Audit: sessions invalidated\n await this.auditService.record(\"security\", \"sessions_invalidated\", {\n userId: user.id,\n userEmail: email,\n userRealm: realm.name,\n resourceId: user.id,\n severity: \"warning\",\n description: \"All sessions invalidated after password reset\",\n });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { CryptoProvider } from \"alepha/security\";\nimport { BadRequestError, ConflictError, HttpError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\n\n/**\n * Intent stored in cache during the registration flow.\n */\ninterface RegistrationIntent {\n data: {\n username?: string;\n email?: string;\n phoneNumber?: string;\n firstName?: string;\n lastName?: string;\n picture?: string;\n passwordHash: string;\n };\n requirements: {\n email: boolean;\n phone: boolean;\n captcha: boolean;\n };\n realmName?: string;\n expiresAt: string;\n}\n\nconst INTENT_TTL_MINUTES = 10;\n\nexport class RegistrationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly verificationController = $client<VerificationController>();\n protected readonly userNotifications = $inject(UserNotifications);\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly auditService = $inject(AuditService);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"registration-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n /**\n * Phase 1: Create a registration intent.\n *\n * Validates the registration data, checks for existing users,\n * creates verification sessions, and stores the intent in cache.\n */\n public async createRegistrationIntent(\n body: RegisterRequest,\n userRealmName?: string,\n ): Promise<RegistrationIntentResponse> {\n this.log.trace(\"Creating registration intent\", {\n email: body.email,\n username: body.username,\n userRealmName,\n });\n\n const realmSettings =\n this.userRealmProvider.getRealm(userRealmName).settings;\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n\n // Check if registration is allowed\n if (realmSettings?.registrationAllowed === false) {\n this.log.warn(\"Registration not allowed for realm\", { userRealmName });\n throw new BadRequestError(\"Registration is not allowed\");\n }\n\n // Validate required fields based on settings\n if (realmSettings?.usernameRequired && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (realmSettings?.emailRequired !== false && !body.email) {\n this.log.debug(\"Registration rejected: email required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Email is required\");\n }\n\n if (realmSettings?.phoneRequired && !body.phoneNumber) {\n this.log.debug(\"Registration rejected: phone required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Phone number is required\");\n }\n\n // Check for existing users (username, email, phone)\n await this.checkUserAvailability(body, userRealmName);\n\n // Hash the password\n const passwordHash = await this.cryptoProvider.hashPassword(body.password);\n\n // Determine requirements based on realm settings\n const requirements = {\n email: realmSettings?.verifyEmailRequired === true && !!body.email,\n phone: realmSettings?.verifyPhoneRequired === true && !!body.phoneNumber,\n captcha: false, // Always false for now\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber);\n }\n\n // Generate intent ID and expiration\n const intentId = randomUUID();\n const expiresAt = this.dateTimeProvider\n .now()\n .add(INTENT_TTL_MINUTES, \"minutes\")\n .toISOString();\n\n // Store intent in cache\n const intent: RegistrationIntent = {\n data: {\n username: body.username,\n email: body.email,\n phoneNumber: body.phoneNumber,\n firstName: body.firstName,\n lastName: body.lastName,\n picture: body.picture,\n passwordHash,\n },\n requirements,\n realmName: userRealmName,\n expiresAt,\n };\n\n await this.intentCache.set(intentId, intent);\n\n this.log.info(\"Registration intent created\", {\n intentId,\n email: body.email,\n username: body.username,\n requiresEmailVerification: requirements.email,\n requiresPhoneVerification: requirements.phone,\n });\n\n return {\n intentId,\n expectCaptcha: requirements.captcha,\n expectEmailVerification: requirements.email,\n expectPhoneVerification: requirements.phone,\n expiresAt,\n };\n }\n\n /**\n * Phase 2: Complete registration using an intent.\n *\n * Validates all requirements (verification codes, captcha),\n * creates the user and credentials, and returns the user.\n */\n public async completeRegistration(\n body: CompleteRegistrationRequest,\n ): Promise<UserEntity> {\n this.log.trace(\"Completing registration\", { intentId: body.intentId });\n\n // Fetch intent from cache\n const intent = await this.intentCache.get(body.intentId);\n if (!intent) {\n this.log.warn(\"Invalid or expired registration intent\", {\n intentId: body.intentId,\n });\n throw new HttpError({\n status: 410,\n message: \"Invalid or expired registration intent\",\n });\n }\n\n const userRealmName = intent.realmName;\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n const identityRepository =\n this.userRealmProvider.identityRepository(userRealmName);\n\n // Validate email verification if required\n if (intent.requirements.email) {\n if (!body.emailCode) {\n this.log.debug(\"Registration completion missing email code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Email verification code is required\");\n }\n\n if (!intent.data.email) {\n throw new BadRequestError(\"Email is missing from registration intent\");\n }\n\n await this.verifyEmailCode(intent.data.email, body.emailCode);\n }\n\n // Validate phone verification if required\n if (intent.requirements.phone) {\n if (!body.phoneCode) {\n this.log.debug(\"Registration completion missing phone code\", {\n intentId: body.intentId,\n });\n throw new BadRequestError(\"Phone verification code is required\");\n }\n\n if (!intent.data.phoneNumber) {\n throw new BadRequestError(\n \"Phone number is missing from registration intent\",\n );\n }\n\n await this.verifyPhoneCode(intent.data.phoneNumber, body.phoneCode);\n }\n\n // Validate captcha if required (placeholder for future implementation)\n if (intent.requirements.captcha) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n // TODO: Validate captcha token\n }\n\n // Final availability check (race condition guard)\n await this.checkUserAvailability(\n {\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n },\n userRealmName,\n );\n\n // Atomically delete cache key to prevent replay\n await this.intentCache.invalidate(body.intentId);\n\n // Create the user\n const user = await userRepository.create({\n realm: userRealmName,\n username: intent.data.username,\n email: intent.data.email,\n phoneNumber: intent.data.phoneNumber,\n firstName: intent.data.firstName,\n lastName: intent.data.lastName,\n picture: intent.data.picture,\n roles: [\"user\"],\n enabled: true,\n emailVerified: intent.requirements.email, // Marked as verified if we verified during registration\n });\n\n // Create credentials identity\n await identityRepository.create({\n userId: user.id,\n provider: \"credentials\",\n password: intent.data.passwordHash,\n });\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: \"User registered\",\n metadata: {\n username: user.username,\n email: user.email,\n emailVerified: user.emailVerified,\n registrationMethod: \"credentials\",\n },\n });\n\n return user;\n }\n\n /**\n * Check if username, email, and phone are available.\n */\n protected async checkUserAvailability(\n body: Pick<RegisterRequest, \"username\" | \"email\" | \"phoneNumber\">,\n userRealmName?: string,\n ): Promise<void> {\n const userRepository = this.userRealmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Username already taken\", { username: body.username });\n throw new ConflictError(\"User with this username already exists\");\n }\n }\n\n if (body.email) {\n const existingUser = await userRepository\n .findOne({ where: { email: { eq: body.email } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Email already taken\", { email: body.email });\n throw new ConflictError(\"User with this email already exists\");\n }\n }\n\n if (body.phoneNumber) {\n const existingUser = await userRepository\n .findOne({ where: { phoneNumber: { eq: body.phoneNumber } } })\n .catch(() => undefined);\n if (existingUser) {\n this.log.debug(\"Phone number already taken\", {\n phoneNumber: body.phoneNumber,\n });\n throw new ConflictError(\"User with this phone number already exists\");\n }\n }\n }\n\n /**\n * Send email verification code.\n */\n protected async sendEmailVerification(email: string): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications.emailVerification.push({\n contact: email,\n variables: {\n email,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Email verification code sent\", { email });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send email verification code\", error);\n }\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(phoneNumber: string): Promise<void> {\n this.log.debug(\"Sending phone verification code\", { phoneNumber });\n try {\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber },\n });\n\n await this.userNotifications.phoneVerification.push({\n contact: phoneNumber,\n variables: {\n phoneNumber,\n code: verification.token,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n });\n this.log.debug(\"Phone verification code sent\", { phoneNumber });\n } catch (error) {\n // Silent fail - verification service may have rate limiting\n this.log.warn(\"Failed to send phone verification code\", {\n phoneNumber,\n error,\n });\n }\n }\n\n /**\n * Verify email code using verification service.\n */\n protected async verifyEmailCode(email: string, code: string): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: email, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid email verification code\", { email });\n throw new BadRequestError(\"Invalid or expired email verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Email verification code already used\", { email });\n throw new BadRequestError(\n \"Email verification code has already been used\",\n );\n }\n }\n\n /**\n * Verify phone code using verification service.\n */\n protected async verifyPhoneCode(\n phoneNumber: string,\n code: string,\n ): Promise<void> {\n const result = await this.verificationController\n .validateVerificationCode({\n params: { type: \"code\" },\n body: { target: phoneNumber, token: code },\n })\n .catch(() => {\n this.log.warn(\"Invalid phone verification code\", { phoneNumber });\n throw new BadRequestError(\"Invalid or expired phone verification code\");\n });\n\n if (result.alreadyVerified) {\n this.log.warn(\"Phone verification code already used\", { phoneNumber });\n throw new BadRequestError(\n \"Phone verification code has already been used\",\n );\n }\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $action, okSchema } from \"alepha/server\";\nimport { completePasswordResetRequestSchema } from \"../schemas/completePasswordResetRequestSchema.ts\";\nimport { completeRegistrationRequestSchema } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport { passwordResetIntentResponseSchema } from \"../schemas/passwordResetIntentResponseSchema.ts\";\nimport { registerQuerySchema } from \"../schemas/registerQuerySchema.ts\";\nimport { registerRequestSchema } from \"../schemas/registerRequestSchema.ts\";\nimport { registrationIntentResponseSchema } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { userResourceSchema } from \"../schemas/userResourceSchema.ts\";\nimport { CredentialService } from \"../services/CredentialService.ts\";\nimport { RegistrationService } from \"../services/RegistrationService.ts\";\nimport { UserService } from \"../services/UserService.ts\";\n\nexport class UserController {\n protected readonly url = \"/users\";\n protected readonly group = \"users\";\n protected readonly credentialService = $inject(CredentialService);\n protected readonly userService = $inject(UserService);\n protected readonly registrationService = $inject(RegistrationService);\n\n /**\n * Phase 1: Create a registration intent.\n * Validates data, creates verification sessions, and stores intent in cache.\n */\n public readonly createRegistrationIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register`,\n secure: false,\n schema: {\n body: registerRequestSchema,\n query: registerQuerySchema,\n response: registrationIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.registrationService.createRegistrationIntent(\n body,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete registration using an intent.\n * Validates verification codes and creates the user.\n */\n public readonly createUserFromIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/register/complete`,\n secure: false,\n schema: {\n body: completeRegistrationRequestSchema,\n response: userResourceSchema,\n },\n handler: ({ body }) => this.registrationService.completeRegistration(body),\n });\n\n /**\n * Phase 1: Create a password reset intent.\n * Validates email, sends verification code, and stores intent in cache.\n */\n public readonly createPasswordResetIntent = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: passwordResetIntentResponseSchema,\n },\n handler: ({ body, query }) =>\n this.credentialService.createPasswordResetIntent(\n body.email,\n query.userRealmName,\n ),\n });\n\n /**\n * Phase 2: Complete password reset using an intent.\n * Validates verification code, updates password, and invalidates sessions.\n */\n public readonly completePasswordReset = $action({\n group: this.group,\n method: \"POST\",\n path: `${this.url}/password-reset/complete`,\n secure: false,\n schema: {\n body: completePasswordResetRequestSchema,\n response: okSchema,\n },\n handler: async ({ body }) => {\n await this.credentialService.completePasswordReset(body);\n return { ok: true };\n },\n });\n\n // Legacy endpoints for backward compatibility\n\n /**\n * @deprecated Use createPasswordResetIntent instead\n */\n public requestPasswordReset = $action({\n path: \"/users/password-reset/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.requestPasswordReset(\n body.email,\n query.userRealmName,\n );\n\n return {\n success: true,\n message:\n \"If an account exists with this email, a password reset code has been sent.\",\n };\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public validateResetToken = $action({\n path: \"/users/password-reset/validate\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n token: t.string(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n valid: t.boolean(),\n email: t.optional(t.email()),\n }),\n },\n handler: async ({ query }) => {\n try {\n const email = await this.credentialService.validateResetToken(\n query.email,\n query.token,\n query.userRealmName,\n );\n return {\n valid: true,\n email,\n };\n } catch {\n return {\n valid: false,\n };\n }\n },\n });\n\n /**\n * @deprecated Use completePasswordReset instead\n */\n public resetPassword = $action({\n path: \"/users/password-reset/reset\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n newPassword: t.string({ minLength: 8 }),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.credentialService.resetPassword(\n body.email,\n body.token,\n body.newPassword,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Password has been reset successfully. Please log in.\",\n };\n },\n });\n\n /**\n * Request email verification.\n * Generates a verification token using verification service and sends an email to the user.\n * @param method - The verification method: \"code\" (default) sends a 6-digit code, \"link\" sends a clickable verification link.\n * @param verifyUrl - Required when method is \"link\". The base URL for the verification link. Token and email will be appended as query params.\n */\n public requestEmailVerification = $action({\n path: \"/users/email-verification/request\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n method: t.optional(\n t.enum([\"code\", \"link\"], {\n default: \"code\",\n description:\n 'Verification method: \"code\" sends a 6-digit code, \"link\" sends a clickable verification link.',\n }),\n ),\n verifyUrl: t.optional(\n t.string({\n description:\n 'Base URL for verification link. Required when method is \"link\". Token and email will be appended as query params.',\n }),\n ),\n }),\n body: t.object({\n email: t.email(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n const method = query.method ?? \"code\";\n await this.userService.requestEmailVerification(\n body.email,\n query.userRealmName,\n method,\n query.verifyUrl,\n );\n\n return {\n success: true,\n message:\n method === \"link\"\n ? \"If an account exists with this email, a verification link has been sent.\"\n : \"If an account exists with this email, a verification code has been sent.\",\n };\n },\n });\n\n /**\n * Verify email with a valid token.\n * Updates the user's emailVerified status.\n */\n public verifyEmail = $action({\n path: \"/users/email-verification/verify\",\n group: this.group,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n body: t.object({\n email: t.email(),\n token: t.string(),\n }),\n response: t.object({\n success: t.boolean(),\n message: t.string(),\n }),\n },\n handler: async ({ body, query }) => {\n await this.userService.verifyEmail(\n body.email,\n body.token,\n query.userRealmName,\n );\n\n return {\n success: true,\n message: \"Email has been verified successfully.\",\n };\n },\n });\n\n /**\n * Check if an email is verified.\n */\n public checkEmailVerification = $action({\n path: \"/users/email-verification/check\",\n group: this.group,\n schema: {\n query: t.object({\n email: t.email(),\n userRealmName: t.optional(t.string()),\n }),\n response: t.object({\n verified: t.boolean(),\n }),\n },\n handler: async ({ query }) => {\n const verified = await this.userService.isEmailVerified(\n query.email,\n query.userRealmName,\n );\n\n return {\n verified,\n };\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const userRealmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n});\n\nexport type UserRealmConfig = Static<typeof userRealmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport { userRealmConfigSchema } from \"../schemas/userRealmConfigSchema.ts\";\n\n/**\n * Controller for exposing realm configuration.\n * Uses $route instead of $action to keep endpoints hidden from API documentation.\n */\nexport class UserRealmController {\n protected readonly url = \"/realms\";\n protected readonly group = \"realms\";\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n\n /**\n * Get realm configuration settings.\n * This endpoint is not exposed in the API documentation.\n */\n public readonly getRealmConfig = $action({\n group: this.group,\n method: \"GET\",\n path: `${this.url}/config`,\n secure: false,\n cache: {\n etag: true,\n control: {\n maxAge: [24, \"hours\"],\n },\n },\n schema: {\n query: t.object({\n userRealmName: t.optional(t.string()),\n }),\n response: userRealmConfigSchema,\n },\n handler: ({ query }) => {\n const { name: realmName, settings } = this.userRealmProvider.getRealm(\n query.userRealmName,\n );\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n group: this.group,\n path: `${this.url}/check-username`,\n secure: false,\n schema: {\n query: t.object({\n userRealmName: t.optional(t.text()),\n }),\n body: t.object({\n username: t.text(),\n }),\n response: t.object({\n available: t.boolean(),\n }),\n },\n handler: async ({ query, body }) => {\n const realmName = query.userRealmName;\n const userRepository = this.userRealmProvider.userRepository(realmName);\n\n const existingUser = await userRepository\n .findOne({ where: { username: { eq: body.username } } })\n .catch(() => undefined);\n\n return {\n available: !existingUser,\n };\n },\n });\n}\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport { AuditService } from \"alepha/api/audits\";\nimport type { FileController } from \"alepha/api/files\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { type ServerRequest, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\n\nexport class SessionService {\n protected readonly alepha = $inject(Alepha);\n protected readonly fsp = $inject(FileSystemProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cryptoProvider = $inject(CryptoProvider);\n protected readonly log = $logger();\n protected readonly userRealmProvider = $inject(UserRealmProvider);\n protected readonly fileController = $client<FileController>();\n protected readonly auditService = $inject(AuditService);\n\n public users(userRealmName?: string) {\n return this.userRealmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.userRealmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.userRealmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Random delay to prevent timing attacks (50-200ms)\n * Uses cryptographically secure random number generation\n */\n protected randomDelay(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, randomInt(50, 201)));\n }\n\n /**\n * Validate user credentials and return the user if valid.\n */\n public async login(\n provider: string,\n username: string,\n password: string,\n userRealmName?: string,\n ): Promise<UserEntity> {\n const { settings, name } = this.userRealmProvider.getRealm(userRealmName);\n const isEmail = username.includes(\"@\");\n const isPhone = /^[+\\d][\\d\\s()-]+$/.test(username);\n const isUsername = !isEmail && !isPhone;\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.usernameEnabled !== false && isUsername) {\n where.username = username;\n } else if (settings.emailEnabled !== false && isEmail) {\n where.email = username;\n } else if (settings.phoneEnabled === true && isPhone) {\n where.phoneNumber = username;\n } else {\n this.log.warn(\"Invalid login identifier format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Invalid login identifier format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const user = await users.findOne({ where }).catch(() => undefined);\n if (!user) {\n this.log.warn(\"User not found during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"User not found\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.findOne({\n where: {\n provider: { eq: provider },\n userId: { eq: user.id },\n },\n });\n\n const storedPassword = identity.password;\n if (!storedPassword) {\n this.log.error(\"Identity has no password configured\", {\n provider,\n username,\n identityId: identity.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const valid = await this.cryptoProvider.verifyPassword(\n password,\n storedPassword,\n );\n\n if (!valid) {\n this.log.warn(\"Invalid password during login attempt\", {\n provider,\n username,\n realm: name,\n });\n\n await this.auditService.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Invalid password\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User logged in via ${provider}`,\n metadata: { provider, username },\n });\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\n // TODO: store failed login attempts (with request data) and lock account after threshold\n throw error;\n }\n\n this.log.warn(\"Error during login attempt\", error);\n\n throw new InvalidCredentialsError();\n }\n }\n\n public async createSession(\n user: UserAccount,\n expiresIn: number,\n userRealmName?: string,\n ) {\n this.log.trace(\"Creating session\", { userId: user.id, expiresIn });\n\n const request = this.alepha.context.get<ServerRequest>(\"request\");\n const refreshToken = this.cryptoProvider.randomUUID();\n\n const expiresAt = this.dateTimeProvider\n .now()\n .add(expiresIn, \"seconds\")\n .toISOString();\n\n const session = await this.sessions(userRealmName).create({\n userId: user.id,\n expiresAt,\n ip: request?.ip,\n userAgent: request?.userAgent,\n refreshToken,\n });\n\n this.log.info(\"Session created\", {\n sessionId: session.id,\n userId: user.id,\n ip: request?.ip,\n });\n\n return {\n refreshToken,\n sessionId: session.id,\n };\n }\n\n public async refreshSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Refreshing session\");\n\n const session = await this.sessions(userRealmName).findOne({\n where: {\n refreshToken: { eq: refreshToken },\n },\n });\n\n const now = this.dateTimeProvider.now();\n const expiresAt = this.dateTimeProvider.of(session.expiresAt);\n\n if (this.dateTimeProvider.of(session.expiresAt) < now) {\n this.log.debug(\"Session expired during refresh\", {\n sessionId: session.id,\n userId: session.userId,\n });\n await this.sessions(userRealmName).deleteById(refreshToken);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n const user = await this.users(userRealmName).findOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n this.log.debug(\"Session refreshed\", {\n sessionId: session.id,\n userId: session.userId,\n });\n\n const { name } = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordAuth(\"token_refresh\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n sessionId: session.id,\n description: \"Session token refreshed\",\n });\n\n return {\n user,\n expiresIn: expiresAt.unix() - now.unix(),\n sessionId: session.id,\n };\n }\n\n public async deleteSession(refreshToken: string, userRealmName?: string) {\n this.log.trace(\"Deleting session\");\n\n // Get session info before deletion for audit\n const session = await this.sessions(userRealmName)\n .findOne({\n where: { refreshToken: { eq: refreshToken } },\n })\n .catch(() => undefined);\n\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n\n if (session) {\n const { name } = this.userRealmProvider.getRealm(userRealmName);\n\n await this.auditService.recordAuth(\"logout\", {\n userId: session.userId,\n userRealm: name,\n sessionId: session.id,\n description: \"User logged out\",\n });\n }\n }\n\n public async link(\n provider: string,\n profile: OAuth2Profile,\n userRealmName?: string,\n ) {\n this.log.trace(\"Linking OAuth2 profile\", {\n provider,\n profileSub: profile.sub,\n email: profile.email,\n });\n\n const realm = this.userRealmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities\n .findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n })\n .catch(() => undefined);\n\n // existing identity found, return associated user\n if (identity) {\n this.log.debug(\"Existing identity found\", {\n provider,\n identityId: identity.id,\n userId: identity.userId,\n });\n\n const user = await users.findById(identity.userId);\n\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User logged in via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub },\n });\n\n return user;\n }\n\n if (!profile.email) {\n this.log.debug(\"OAuth2 profile has no email, returning profile as-is\", {\n provider,\n profileSub: profile.sub,\n });\n return {\n id: profile.sub,\n ...profile,\n };\n }\n\n const existing = await users\n .findOne({\n where: {\n email: profile.email,\n },\n })\n .catch(() => undefined);\n\n if (existing) {\n this.log.debug(\"Linking OAuth2 profile to existing user by email\", {\n provider,\n profileSub: profile.sub,\n userId: existing.id,\n email: profile.email,\n });\n await identities.create({\n provider,\n providerUserId: profile.sub,\n userId: existing.id,\n });\n\n await this.auditService.recordAuth(\"login\", {\n userId: existing.id,\n userEmail: existing.email ?? undefined,\n userRealm: realm.name,\n resourceId: existing.id,\n description: `OAuth2 identity linked to existing user (${provider})`,\n metadata: { provider, providerUserId: profile.sub, linked: true },\n });\n\n return existing;\n }\n\n // TODO: check usernames for uniqueness, add suffix if needed (e.g. john.doe1)\n // TODO: username must match a-zA-Z0-9._-\n\n const user = await users.create({\n realm: realm.name,\n username: profile.email.split(\"@\")[0],\n email: profile.email,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: [\"user\"], // TODO: make default roles configurable via realm settings\n });\n\n if (profile.picture) {\n this.log.debug(\"Fetching user profile picture from OAuth2 provider\", {\n provider,\n url: profile.picture,\n });\n try {\n const response = await fetch(profile.picture);\n const file = this.fsp.createFile({\n response,\n });\n if (response.ok && response.body) {\n const fileEntity = await this.fileController.uploadFile(\n {\n body: { file },\n },\n {\n user,\n },\n );\n await users.updateById(user.id, { picture: fileEntity.id });\n }\n } catch (error) {\n this.log.warn(\"Failed to fetch user profile picture\", error);\n }\n }\n\n await this.identities(userRealmName).create({\n provider,\n providerUserId: profile.sub,\n userId: user.id,\n });\n\n this.log.info(\"New user created via OAuth2 link\", {\n provider,\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n // Audit: user created via OAuth\n await this.auditService.recordUser(\"create\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `User created via OAuth2 (${provider})`,\n metadata: {\n provider,\n providerUserId: profile.sub,\n username: user.username,\n email: user.email,\n },\n });\n\n // Audit: login event\n await this.auditService.recordAuth(\"login\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: realm.name,\n resourceId: user.id,\n description: `First login via OAuth2 (${provider})`,\n metadata: { provider, providerUserId: profile.sub, firstLogin: true },\n });\n\n return user;\n }\n}\n","import { $context } from \"alepha\";\nimport { AlephaApiAudits } from \"alepha/api/audits\";\nimport { AlephaApiFiles } from \"alepha/api/files\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $realm,\n type RealmPrimitive,\n type RealmPrimitiveOptions,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authCredentials,\n $authGithub,\n $authGoogle,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport type { identities } from \"../entities/identities.ts\";\nimport type { sessions } from \"../entities/sessions.ts\";\nimport { DEFAULT_USER_REALM_NAME, type users } from \"../entities/users.ts\";\nimport { UserRealmProvider } from \"../providers/UserRealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\n\nexport type UserRealmPrimitive = RealmPrimitive & WithLinkFn & WithLoginFn;\n\n/**\n * Already configured realm for user management.\n *\n * Realm contains two roles: `admin` and `user`.\n *\n * - `admin`: Has full access to all resources and permissions.\n * - `user`: Has access to their own resources and permissions, but cannot access admin-level resources.\n *\n * Realm uses session management for handling user sessions.\n *\n * Environment Variables:\n * - `APP_SECRET`: Secret key for signing tokens (if not provided in options).\n */\n\nexport const $userRealm = (\n options: UserRealmOptions = {},\n): UserRealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const userRealmProvider = alepha.inject(UserRealmProvider);\n\n const name = options.realm?.name ?? DEFAULT_USER_REALM_NAME;\n\n options.settings ??= {};\n\n if (options.settings.emailRequired) {\n options.settings.emailEnabled = true;\n }\n\n if (options.settings.usernameRequired) {\n options.settings.usernameEnabled = true;\n }\n\n if (options.settings.phoneRequired) {\n options.settings.phoneEnabled = true;\n }\n\n const userRealm = userRealmProvider.register(name, options);\n\n alepha.with(AlephaApiFiles);\n alepha.with(AlephaApiAudits);\n\n const realm: UserRealmPrimitive = $realm({\n ...options.realm,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n roles: options.realm?.roles ?? [\n {\n name: \"admin\",\n permissions: [\n {\n name: \"*\",\n },\n ],\n },\n {\n name: \"user\",\n permissions: [\n {\n name: \"*\",\n ownership: true,\n exclude: [\"admin:*\"],\n },\n ],\n },\n ],\n settings: {\n accessToken: {\n expiration: [15, \"minutes\"],\n },\n refreshToken: {\n expiration: [30, \"days\"],\n },\n onCreateSession: async (user, config) => {\n return sessionService.createSession(user, config.expiresIn);\n },\n onRefreshSession: async (refreshToken) => {\n return sessionService.refreshSession(refreshToken);\n },\n onDeleteSession: async (refreshToken) => {\n await sessionService.deleteSession(refreshToken);\n },\n ...options.realm?.settings,\n },\n });\n\n realm.link = (name: string) => {\n return (ctx: LinkAccountOptions) =>\n sessionService.link(name, ctx.user, realm.name);\n };\n\n realm.login = (name: string) => {\n return (credentials: Credentials) => {\n return sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n };\n };\n\n const identities = options.identities ?? {\n credentials: true,\n };\n\n if (identities) {\n const auth: Record<string, AuthPrimitive> = {};\n if (identities.credentials) {\n auth.credentials = $authCredentials(realm);\n } else {\n // if credentials auth is disabled, disable registration as well\n userRealm.settings.registrationAllowed = false;\n }\n\n if (identities.google) {\n auth.google = $authGoogle(realm);\n }\n\n if (identities.github) {\n auth.github = $authGithub(realm);\n }\n\n alepha.with(() => auth);\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface UserRealmOptions {\n /**\n * Secret key for signing tokens.\n *\n * If not provided, the secret from the SecurityProvider will be used (usually from the APP_SECRET environment variable).\n */\n secret?: string;\n\n /**\n * Realm configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n realm?: Partial<RealmPrimitiveOptions>;\n\n /**\n * Override entities.\n */\n entities?: {\n users?: Repository<typeof users.schema>;\n identities?: Repository<typeof identities.schema>;\n sessions?: Repository<typeof sessions.schema>;\n };\n\n settings?: Partial<RealmAuthSettings>;\n\n identities?: {\n credentials?: true;\n google?: true;\n github?: true;\n };\n\n modules?: {\n files?: boolean;\n audits?: boolean;\n jobs?: boolean;\n };\n}\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const loginSchema = t.object({\n username: t.text({\n minLength: 3,\n maxLength: 100,\n description: \"Username or email address for login\",\n }),\n password: t.text({\n minLength: 6,\n description: \"User password\",\n }),\n});\n\nexport type LoginInput = Static<typeof loginSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const registerSchema = t.object({\n username: t.string({\n minLength: 3,\n maxLength: 20,\n pattern: /^[a-zA-Z0-9_]+$/,\n description: \"Username for the new account\",\n }),\n email: t.email({\n description: \"Email address for the new account\",\n }),\n password: t.string({\n minLength: 8,\n description: \"Password for the new account\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the password\",\n }),\n firstName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's first name\",\n }),\n ),\n lastName: t.optional(\n t.string({\n maxLength: 100,\n description: \"User's last name\",\n }),\n ),\n});\n\nexport type RegisterInput = Static<typeof registerSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const resetPasswordRequestSchema = t.object({\n email: t.email({\n description: \"Email address to send password reset link\",\n }),\n});\n\nexport const resetPasswordSchema = t.object({\n token: t.string({\n description: \"Password reset token from email\",\n }),\n password: t.string({\n minLength: 8,\n description: \"New password\",\n }),\n confirmPassword: t.string({\n minLength: 8,\n description: \"Confirmation of the new password\",\n }),\n});\n\nexport type ResetPasswordRequest = Static<typeof resetPasswordRequestSchema>;\nexport type ResetPasswordInput = Static<typeof resetPasswordSchema>;\n","import { $module } from \"alepha\";\nimport { AlephaApiNotifications } from \"alepha/api/notifications\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport { AlephaEmail } from \"alepha/email\";\nimport { AlephaServerCompress } from \"alepha/server/compress\";\nimport { AlephaServerHelmet } from \"alepha/server/helmet\";\nimport { AdminIdentityController } from \"./controllers/AdminIdentityController.ts\";\nimport { AdminSessionController } from \"./controllers/AdminSessionController.ts\";\nimport { AdminUserController } from \"./controllers/AdminUserController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { UserRealmController } from \"./controllers/UserRealmController.ts\";\nimport { UserNotifications } from \"./notifications/UserNotifications.ts\";\nimport { UserRealmProvider } from \"./providers/UserRealmProvider.ts\";\nimport { CredentialService } from \"./services/CredentialService.ts\";\nimport { IdentityService } from \"./services/IdentityService.ts\";\nimport { RegistrationService } from \"./services/RegistrationService.ts\";\nimport { SessionCrudService } from \"./services/SessionCrudService.ts\";\nimport { SessionService } from \"./services/SessionService.ts\";\nimport { UserService } from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./atoms/realmAuthSettingsAtom.ts\";\nexport * from \"./controllers/AdminIdentityController.ts\";\nexport * from \"./controllers/AdminSessionController.ts\";\nexport * from \"./controllers/AdminUserController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./controllers/UserRealmController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./primitives/$userRealm.ts\";\nexport * from \"./providers/UserRealmProvider.ts\";\nexport * from \"./schemas/completePasswordResetRequestSchema.ts\";\nexport * from \"./schemas/completeRegistrationRequestSchema.ts\";\nexport * from \"./schemas/createUserSchema.ts\";\nexport * from \"./schemas/identityQuerySchema.ts\";\nexport * from \"./schemas/identityResourceSchema.ts\";\nexport * from \"./schemas/loginSchema.ts\";\nexport * from \"./schemas/passwordResetIntentResponseSchema.ts\";\nexport * from \"./schemas/registerSchema.ts\";\nexport * from \"./schemas/registrationIntentResponseSchema.ts\";\nexport * from \"./schemas/resetPasswordSchema.ts\";\nexport * from \"./schemas/sessionQuerySchema.ts\";\nexport * from \"./schemas/sessionResourceSchema.ts\";\nexport * from \"./schemas/updateUserSchema.ts\";\nexport * from \"./schemas/userQuerySchema.ts\";\nexport * from \"./schemas/userRealmConfigSchema.ts\";\nexport * from \"./schemas/userResourceSchema.ts\";\nexport * from \"./services/CredentialService.ts\";\nexport * from \"./services/IdentityService.ts\";\nexport * from \"./services/RegistrationService.ts\";\nexport * from \"./services/SessionCrudService.ts\";\nexport * from \"./services/SessionService.ts\";\nexport * from \"./services/UserService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides user management API endpoints for Alepha applications.\n *\n * This module includes user CRUD operations, authentication endpoints,\n * password reset functionality, and user profile management capabilities.\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n AlephaApiVerification,\n AlephaApiNotifications,\n AlephaServerHelmet,\n AlephaServerCompress,\n AlephaEmail,\n UserRealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n AdminUserController,\n AdminSessionController,\n AdminIdentityController,\n UserRealmController,\n UserNotifications,\n ],\n});\n"],"mappings":"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"}
|