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
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import { Alepha } from "alepha";
|
|
2
|
+
import { AlephaApiVerification } from "alepha/api/verifications";
|
|
3
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
4
|
+
import { AlephaEmail, MemoryEmailProvider } from "alepha/email";
|
|
5
|
+
import { AlephaSecurity, CryptoProvider } from "alepha/security";
|
|
6
|
+
import { BadRequestError, ConflictError, HttpError } from "alepha/server";
|
|
7
|
+
import { describe, it } from "vitest";
|
|
8
|
+
import {
|
|
9
|
+
AlephaApiUsers,
|
|
10
|
+
RegistrationService,
|
|
11
|
+
SessionService,
|
|
12
|
+
UserRealmProvider,
|
|
13
|
+
UserService,
|
|
14
|
+
} from "../index.ts";
|
|
15
|
+
|
|
16
|
+
const setup = async (realmSettings?: Record<string, unknown>) => {
|
|
17
|
+
const alepha = Alepha.create();
|
|
18
|
+
|
|
19
|
+
alepha.with(AlephaSecurity);
|
|
20
|
+
alepha.with(AlephaEmail);
|
|
21
|
+
alepha.with(AlephaApiVerification);
|
|
22
|
+
alepha.with(AlephaApiUsers);
|
|
23
|
+
|
|
24
|
+
await alepha.start();
|
|
25
|
+
|
|
26
|
+
const emailProvider = alepha.inject(MemoryEmailProvider);
|
|
27
|
+
emailProvider.records = [];
|
|
28
|
+
|
|
29
|
+
const userRealmProvider = alepha.inject(UserRealmProvider);
|
|
30
|
+
|
|
31
|
+
// Configure realm settings if provided (applies to default realm)
|
|
32
|
+
if (realmSettings) {
|
|
33
|
+
userRealmProvider.register("default", {
|
|
34
|
+
settings: realmSettings as never,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
alepha,
|
|
40
|
+
registrationService: alepha.inject(RegistrationService),
|
|
41
|
+
userService: alepha.inject(UserService),
|
|
42
|
+
sessionService: alepha.inject(SessionService),
|
|
43
|
+
cryptoProvider: alepha.inject(CryptoProvider),
|
|
44
|
+
dateTimeProvider: alepha.inject(DateTimeProvider),
|
|
45
|
+
emailProvider,
|
|
46
|
+
userRealmProvider,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Helper to extract code from email
|
|
51
|
+
const extractCode = (emailBody: string): string => {
|
|
52
|
+
const match = emailBody.match(/(\d{6})/);
|
|
53
|
+
if (!match) throw new Error("Code not found in email");
|
|
54
|
+
return match[1];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
describe("alepha/api/users - RegistrationService", () => {
|
|
58
|
+
describe("Phase 1: createRegistrationIntent", () => {
|
|
59
|
+
it("should create a registration intent with valid data", async ({
|
|
60
|
+
expect,
|
|
61
|
+
}) => {
|
|
62
|
+
const { registrationService } = await setup();
|
|
63
|
+
|
|
64
|
+
const result = await registrationService.createRegistrationIntent({
|
|
65
|
+
email: "newuser@example.com",
|
|
66
|
+
password: "SecurePassword123!",
|
|
67
|
+
username: "newuser",
|
|
68
|
+
firstName: "New",
|
|
69
|
+
lastName: "User",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(result.intentId).toBeDefined();
|
|
73
|
+
expect(result.expiresAt).toBeDefined();
|
|
74
|
+
expect(result.expectCaptcha).toBe(false);
|
|
75
|
+
expect(result.expectEmailVerification).toBe(false);
|
|
76
|
+
expect(result.expectPhoneVerification).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should create intent with email verification required when configured", async ({
|
|
80
|
+
expect,
|
|
81
|
+
}) => {
|
|
82
|
+
const { registrationService, emailProvider, userRealmProvider } =
|
|
83
|
+
await setup();
|
|
84
|
+
|
|
85
|
+
// Register realm with email verification required
|
|
86
|
+
userRealmProvider.register("verify-email-realm", {
|
|
87
|
+
settings: {
|
|
88
|
+
verifyEmailRequired: true,
|
|
89
|
+
} as never,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = await registrationService.createRegistrationIntent(
|
|
93
|
+
{
|
|
94
|
+
email: "verify@example.com",
|
|
95
|
+
password: "SecurePassword123!",
|
|
96
|
+
},
|
|
97
|
+
"verify-email-realm",
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(result.intentId).toBeDefined();
|
|
101
|
+
expect(result.expectEmailVerification).toBe(true);
|
|
102
|
+
expect(result.expectPhoneVerification).toBe(false);
|
|
103
|
+
expect(result.expectCaptcha).toBe(false);
|
|
104
|
+
|
|
105
|
+
// Verify email was sent
|
|
106
|
+
await expect.poll(() => emailProvider.records.length).toBe(1);
|
|
107
|
+
expect(emailProvider.records[0].to).toBe("verify@example.com");
|
|
108
|
+
expect(emailProvider.records[0].subject).toBe(
|
|
109
|
+
"Verify your email address",
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should reject registration when disabled in realm settings", async ({
|
|
114
|
+
expect,
|
|
115
|
+
}) => {
|
|
116
|
+
const { registrationService, userRealmProvider } = await setup();
|
|
117
|
+
|
|
118
|
+
userRealmProvider.register("no-registration-realm", {
|
|
119
|
+
settings: {
|
|
120
|
+
registrationAllowed: false,
|
|
121
|
+
} as never,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await expect(
|
|
125
|
+
registrationService.createRegistrationIntent(
|
|
126
|
+
{
|
|
127
|
+
email: "test@example.com",
|
|
128
|
+
password: "SecurePassword123!",
|
|
129
|
+
},
|
|
130
|
+
"no-registration-realm",
|
|
131
|
+
),
|
|
132
|
+
).rejects.toThrowError(BadRequestError);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should reject when required username is missing", async ({
|
|
136
|
+
expect,
|
|
137
|
+
}) => {
|
|
138
|
+
const { registrationService, userRealmProvider } = await setup();
|
|
139
|
+
|
|
140
|
+
userRealmProvider.register("username-required-realm", {
|
|
141
|
+
settings: {
|
|
142
|
+
usernameRequired: true,
|
|
143
|
+
} as never,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
await expect(
|
|
147
|
+
registrationService.createRegistrationIntent(
|
|
148
|
+
{
|
|
149
|
+
email: "test@example.com",
|
|
150
|
+
password: "SecurePassword123!",
|
|
151
|
+
// username is missing
|
|
152
|
+
},
|
|
153
|
+
"username-required-realm",
|
|
154
|
+
),
|
|
155
|
+
).rejects.toThrowError(BadRequestError);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should reject when required email is missing", async ({ expect }) => {
|
|
159
|
+
const { registrationService } = await setup();
|
|
160
|
+
|
|
161
|
+
// Default realm requires email
|
|
162
|
+
await expect(
|
|
163
|
+
registrationService.createRegistrationIntent({
|
|
164
|
+
username: "testuser",
|
|
165
|
+
password: "SecurePassword123!",
|
|
166
|
+
// email is missing
|
|
167
|
+
}),
|
|
168
|
+
).rejects.toThrowError(BadRequestError);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should reject when required phone is missing", async ({ expect }) => {
|
|
172
|
+
const { registrationService, userRealmProvider } = await setup();
|
|
173
|
+
|
|
174
|
+
userRealmProvider.register("phone-required-realm", {
|
|
175
|
+
settings: {
|
|
176
|
+
phoneRequired: true,
|
|
177
|
+
emailRequired: false,
|
|
178
|
+
} as never,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await expect(
|
|
182
|
+
registrationService.createRegistrationIntent(
|
|
183
|
+
{
|
|
184
|
+
email: "test@example.com",
|
|
185
|
+
password: "SecurePassword123!",
|
|
186
|
+
// phoneNumber is missing
|
|
187
|
+
},
|
|
188
|
+
"phone-required-realm",
|
|
189
|
+
),
|
|
190
|
+
).rejects.toThrowError(BadRequestError);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should reject duplicate username", async ({ expect }) => {
|
|
194
|
+
const { registrationService, userService } = await setup();
|
|
195
|
+
|
|
196
|
+
// Create existing user
|
|
197
|
+
await userService.users().create({
|
|
198
|
+
username: "existinguser",
|
|
199
|
+
email: "existing@example.com",
|
|
200
|
+
roles: ["user"],
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await expect(
|
|
204
|
+
registrationService.createRegistrationIntent({
|
|
205
|
+
username: "existinguser",
|
|
206
|
+
email: "new@example.com",
|
|
207
|
+
password: "SecurePassword123!",
|
|
208
|
+
}),
|
|
209
|
+
).rejects.toThrowError(ConflictError);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("should reject duplicate email", async ({ expect }) => {
|
|
213
|
+
const { registrationService, userService } = await setup();
|
|
214
|
+
|
|
215
|
+
// Create existing user
|
|
216
|
+
await userService.users().create({
|
|
217
|
+
username: "existinguser",
|
|
218
|
+
email: "existing@example.com",
|
|
219
|
+
roles: ["user"],
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await expect(
|
|
223
|
+
registrationService.createRegistrationIntent({
|
|
224
|
+
username: "newuser",
|
|
225
|
+
email: "existing@example.com",
|
|
226
|
+
password: "SecurePassword123!",
|
|
227
|
+
}),
|
|
228
|
+
).rejects.toThrowError(ConflictError);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should reject duplicate phone number", async ({ expect }) => {
|
|
232
|
+
const { registrationService, userService, userRealmProvider } =
|
|
233
|
+
await setup();
|
|
234
|
+
|
|
235
|
+
userRealmProvider.register("phone-realm", {
|
|
236
|
+
settings: {
|
|
237
|
+
phoneEnabled: true,
|
|
238
|
+
emailRequired: false,
|
|
239
|
+
} as never,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Create existing user with phone
|
|
243
|
+
await userService.users("phone-realm").create({
|
|
244
|
+
username: "existinguser",
|
|
245
|
+
email: "existing@example.com",
|
|
246
|
+
phoneNumber: "+1234567890",
|
|
247
|
+
roles: ["user"],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
await expect(
|
|
251
|
+
registrationService.createRegistrationIntent(
|
|
252
|
+
{
|
|
253
|
+
username: "newuser",
|
|
254
|
+
email: "new@example.com",
|
|
255
|
+
phoneNumber: "+1234567890",
|
|
256
|
+
password: "SecurePassword123!",
|
|
257
|
+
},
|
|
258
|
+
"phone-realm",
|
|
259
|
+
),
|
|
260
|
+
).rejects.toThrowError(ConflictError);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should set correct expiration time (10 minutes)", async ({
|
|
264
|
+
expect,
|
|
265
|
+
}) => {
|
|
266
|
+
const { registrationService, dateTimeProvider } = await setup();
|
|
267
|
+
|
|
268
|
+
const before = dateTimeProvider.now();
|
|
269
|
+
|
|
270
|
+
const result = await registrationService.createRegistrationIntent({
|
|
271
|
+
email: "expiry@example.com",
|
|
272
|
+
password: "SecurePassword123!",
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const expiresAt = dateTimeProvider.of(result.expiresAt);
|
|
276
|
+
const expectedExpiry = before.add(10, "minutes");
|
|
277
|
+
|
|
278
|
+
// Should expire approximately 10 minutes from now
|
|
279
|
+
expect(expiresAt.diff(expectedExpiry, "seconds")).toBeLessThan(5);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe("Phase 2: completeRegistration", () => {
|
|
284
|
+
it("should complete registration without verification requirements", async ({
|
|
285
|
+
expect,
|
|
286
|
+
}) => {
|
|
287
|
+
const { registrationService, userService } = await setup();
|
|
288
|
+
|
|
289
|
+
// Create intent
|
|
290
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
291
|
+
email: "complete@example.com",
|
|
292
|
+
password: "SecurePassword123!",
|
|
293
|
+
username: "completeuser",
|
|
294
|
+
firstName: "Complete",
|
|
295
|
+
lastName: "User",
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Complete registration
|
|
299
|
+
const user = await registrationService.completeRegistration({
|
|
300
|
+
intentId: intent.intentId,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect(user.id).toBeDefined();
|
|
304
|
+
expect(user.email).toBe("complete@example.com");
|
|
305
|
+
expect(user.username).toBe("completeuser");
|
|
306
|
+
expect(user.firstName).toBe("Complete");
|
|
307
|
+
expect(user.lastName).toBe("User");
|
|
308
|
+
expect(user.roles).toContain("user");
|
|
309
|
+
expect(user.enabled).toBe(true);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should complete registration with valid email verification code", async ({
|
|
313
|
+
expect,
|
|
314
|
+
}) => {
|
|
315
|
+
const { registrationService, emailProvider, userRealmProvider } =
|
|
316
|
+
await setup();
|
|
317
|
+
|
|
318
|
+
userRealmProvider.register("email-verify-realm", {
|
|
319
|
+
settings: {
|
|
320
|
+
verifyEmailRequired: true,
|
|
321
|
+
} as never,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Create intent (sends verification email)
|
|
325
|
+
const intent = await registrationService.createRegistrationIntent(
|
|
326
|
+
{
|
|
327
|
+
email: "emailverify@example.com",
|
|
328
|
+
password: "SecurePassword123!",
|
|
329
|
+
},
|
|
330
|
+
"email-verify-realm",
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
expect(intent.expectEmailVerification).toBe(true);
|
|
334
|
+
|
|
335
|
+
// Extract code from email
|
|
336
|
+
await expect.poll(() => emailProvider.records.length).toBe(1);
|
|
337
|
+
const emailCode = extractCode(emailProvider.records[0].body);
|
|
338
|
+
|
|
339
|
+
// Complete registration with code
|
|
340
|
+
const user = await registrationService.completeRegistration({
|
|
341
|
+
intentId: intent.intentId,
|
|
342
|
+
emailCode,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
expect(user.email).toBe("emailverify@example.com");
|
|
346
|
+
expect(user.emailVerified).toBe(true);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should reject expired intent (410 Gone)", async ({ expect }) => {
|
|
350
|
+
const { registrationService, dateTimeProvider } = await setup();
|
|
351
|
+
|
|
352
|
+
// Create intent
|
|
353
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
354
|
+
email: "expired@example.com",
|
|
355
|
+
password: "SecurePassword123!",
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Travel forward 11 minutes (intent expires at 10)
|
|
359
|
+
dateTimeProvider.travel(11, "minutes");
|
|
360
|
+
|
|
361
|
+
// Attempt to complete
|
|
362
|
+
await expect(
|
|
363
|
+
registrationService.completeRegistration({
|
|
364
|
+
intentId: intent.intentId,
|
|
365
|
+
}),
|
|
366
|
+
).rejects.toThrow(HttpError);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("should reject invalid intent ID", async ({ expect }) => {
|
|
370
|
+
const { registrationService } = await setup();
|
|
371
|
+
|
|
372
|
+
await expect(
|
|
373
|
+
registrationService.completeRegistration({
|
|
374
|
+
intentId: "550e8400-e29b-41d4-a716-446655440000",
|
|
375
|
+
}),
|
|
376
|
+
).rejects.toThrow(HttpError);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("should reject when email code is required but not provided", async ({
|
|
380
|
+
expect,
|
|
381
|
+
}) => {
|
|
382
|
+
const { registrationService, userRealmProvider } = await setup();
|
|
383
|
+
|
|
384
|
+
userRealmProvider.register("email-required-realm", {
|
|
385
|
+
settings: {
|
|
386
|
+
verifyEmailRequired: true,
|
|
387
|
+
} as never,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const intent = await registrationService.createRegistrationIntent(
|
|
391
|
+
{
|
|
392
|
+
email: "needscode@example.com",
|
|
393
|
+
password: "SecurePassword123!",
|
|
394
|
+
},
|
|
395
|
+
"email-required-realm",
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
await expect(
|
|
399
|
+
registrationService.completeRegistration({
|
|
400
|
+
intentId: intent.intentId,
|
|
401
|
+
// emailCode not provided
|
|
402
|
+
}),
|
|
403
|
+
).rejects.toThrowError(BadRequestError);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it("should reject invalid email verification code", async ({ expect }) => {
|
|
407
|
+
const { registrationService, userRealmProvider } = await setup();
|
|
408
|
+
|
|
409
|
+
userRealmProvider.register("email-verify-realm", {
|
|
410
|
+
settings: {
|
|
411
|
+
verifyEmailRequired: true,
|
|
412
|
+
} as never,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const intent = await registrationService.createRegistrationIntent(
|
|
416
|
+
{
|
|
417
|
+
email: "wrongcode@example.com",
|
|
418
|
+
password: "SecurePassword123!",
|
|
419
|
+
},
|
|
420
|
+
"email-verify-realm",
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
await expect(
|
|
424
|
+
registrationService.completeRegistration({
|
|
425
|
+
intentId: intent.intentId,
|
|
426
|
+
emailCode: "000000", // Wrong code
|
|
427
|
+
}),
|
|
428
|
+
).rejects.toThrowError(BadRequestError);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should not allow intent reuse after successful registration", async ({
|
|
432
|
+
expect,
|
|
433
|
+
}) => {
|
|
434
|
+
const { registrationService } = await setup();
|
|
435
|
+
|
|
436
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
437
|
+
email: "onetime@example.com",
|
|
438
|
+
password: "SecurePassword123!",
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// First completion should succeed
|
|
442
|
+
await registrationService.completeRegistration({
|
|
443
|
+
intentId: intent.intentId,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Second attempt should fail (intent deleted)
|
|
447
|
+
await expect(
|
|
448
|
+
registrationService.completeRegistration({
|
|
449
|
+
intentId: intent.intentId,
|
|
450
|
+
}),
|
|
451
|
+
).rejects.toThrow(HttpError);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should detect race condition when email is taken during verification", async ({
|
|
455
|
+
expect,
|
|
456
|
+
}) => {
|
|
457
|
+
const { registrationService, userService, userRealmProvider } =
|
|
458
|
+
await setup();
|
|
459
|
+
|
|
460
|
+
userRealmProvider.register("race-realm", {
|
|
461
|
+
settings: {
|
|
462
|
+
verifyEmailRequired: false,
|
|
463
|
+
} as never,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Create intent
|
|
467
|
+
const intent = await registrationService.createRegistrationIntent(
|
|
468
|
+
{
|
|
469
|
+
email: "race@example.com",
|
|
470
|
+
password: "SecurePassword123!",
|
|
471
|
+
},
|
|
472
|
+
"race-realm",
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// Simulate another user registering with same email while verification pending
|
|
476
|
+
await userService.users("race-realm").create({
|
|
477
|
+
email: "race@example.com",
|
|
478
|
+
username: "racewinner",
|
|
479
|
+
roles: ["user"],
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Attempt to complete should fail
|
|
483
|
+
await expect(
|
|
484
|
+
registrationService.completeRegistration({
|
|
485
|
+
intentId: intent.intentId,
|
|
486
|
+
}),
|
|
487
|
+
).rejects.toThrowError(ConflictError);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("should create credentials identity with hashed password", async ({
|
|
491
|
+
expect,
|
|
492
|
+
}) => {
|
|
493
|
+
const { registrationService, sessionService, userRealmProvider } =
|
|
494
|
+
await setup();
|
|
495
|
+
|
|
496
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
497
|
+
email: "withpassword@example.com",
|
|
498
|
+
password: "SecurePassword123!",
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const user = await registrationService.completeRegistration({
|
|
502
|
+
intentId: intent.intentId,
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Verify login works with the password
|
|
506
|
+
const loggedInUser = await sessionService.login(
|
|
507
|
+
"credentials",
|
|
508
|
+
"withpassword@example.com",
|
|
509
|
+
"SecurePassword123!",
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
expect(loggedInUser?.id).toBe(user.id);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it("should allow login with username after registration", async ({
|
|
516
|
+
expect,
|
|
517
|
+
}) => {
|
|
518
|
+
const { registrationService, sessionService } = await setup({
|
|
519
|
+
usernameEnabled: true,
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
523
|
+
email: "logintest@example.com",
|
|
524
|
+
username: "loginuser",
|
|
525
|
+
password: "SecurePassword123!",
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
const user = await registrationService.completeRegistration({
|
|
529
|
+
intentId: intent.intentId,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Verify login works with username
|
|
533
|
+
const loggedInUser = await sessionService.login(
|
|
534
|
+
"credentials",
|
|
535
|
+
"loginuser",
|
|
536
|
+
"SecurePassword123!",
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(loggedInUser?.id).toBe(user.id);
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
describe("Full registration flow integration", () => {
|
|
544
|
+
it("should complete full registration flow without verification", async ({
|
|
545
|
+
expect,
|
|
546
|
+
}) => {
|
|
547
|
+
const { registrationService, sessionService } = await setup({
|
|
548
|
+
usernameEnabled: true,
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Phase 1: Create intent
|
|
552
|
+
const intent = await registrationService.createRegistrationIntent({
|
|
553
|
+
email: "fullflow@example.com",
|
|
554
|
+
username: "fullflowuser",
|
|
555
|
+
password: "SecurePassword123!",
|
|
556
|
+
firstName: "Full",
|
|
557
|
+
lastName: "Flow",
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
expect(intent.expectEmailVerification).toBe(false);
|
|
561
|
+
expect(intent.expectPhoneVerification).toBe(false);
|
|
562
|
+
|
|
563
|
+
// Phase 2: Complete registration
|
|
564
|
+
const user = await registrationService.completeRegistration({
|
|
565
|
+
intentId: intent.intentId,
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
expect(user.email).toBe("fullflow@example.com");
|
|
569
|
+
expect(user.username).toBe("fullflowuser");
|
|
570
|
+
|
|
571
|
+
// Verify user can login
|
|
572
|
+
const session = await sessionService.login(
|
|
573
|
+
"credentials",
|
|
574
|
+
"fullflowuser",
|
|
575
|
+
"SecurePassword123!",
|
|
576
|
+
);
|
|
577
|
+
expect(session?.id).toBe(user.id);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("should complete full registration flow with email verification", async ({
|
|
581
|
+
expect,
|
|
582
|
+
}) => {
|
|
583
|
+
const {
|
|
584
|
+
registrationService,
|
|
585
|
+
sessionService,
|
|
586
|
+
emailProvider,
|
|
587
|
+
userRealmProvider,
|
|
588
|
+
} = await setup();
|
|
589
|
+
|
|
590
|
+
userRealmProvider.register("full-verify-realm", {
|
|
591
|
+
settings: {
|
|
592
|
+
verifyEmailRequired: true,
|
|
593
|
+
} as never,
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// Phase 1: Create intent
|
|
597
|
+
const intent = await registrationService.createRegistrationIntent(
|
|
598
|
+
{
|
|
599
|
+
email: "fullverify@example.com",
|
|
600
|
+
password: "SecurePassword123!",
|
|
601
|
+
},
|
|
602
|
+
"full-verify-realm",
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
expect(intent.expectEmailVerification).toBe(true);
|
|
606
|
+
|
|
607
|
+
// Extract verification code
|
|
608
|
+
await expect.poll(() => emailProvider.records.length).toBe(1);
|
|
609
|
+
const emailCode = extractCode(emailProvider.records[0].body);
|
|
610
|
+
|
|
611
|
+
// Phase 2: Complete registration with code
|
|
612
|
+
const user = await registrationService.completeRegistration({
|
|
613
|
+
intentId: intent.intentId,
|
|
614
|
+
emailCode,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
expect(user.email).toBe("fullverify@example.com");
|
|
618
|
+
expect(user.emailVerified).toBe(true);
|
|
619
|
+
|
|
620
|
+
// Verify user can login
|
|
621
|
+
const session = await sessionService.login(
|
|
622
|
+
"credentials",
|
|
623
|
+
"fullverify@example.com",
|
|
624
|
+
"SecurePassword123!",
|
|
625
|
+
"full-verify-realm",
|
|
626
|
+
);
|
|
627
|
+
expect(session?.id).toBe(user.id);
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { $inject } from "alepha";
|
|
3
|
+
import { AuditService } from "alepha/api/audits";
|
|
3
4
|
import type { VerificationController } from "alepha/api/verifications";
|
|
4
5
|
import { $cache } from "alepha/cache";
|
|
5
6
|
import { DateTimeProvider } from "alepha/datetime";
|
|
@@ -45,6 +46,7 @@ export class RegistrationService {
|
|
|
45
46
|
protected readonly verificationController = $client<VerificationController>();
|
|
46
47
|
protected readonly userNotifications = $inject(UserNotifications);
|
|
47
48
|
protected readonly userRealmProvider = $inject(UserRealmProvider);
|
|
49
|
+
protected readonly auditService = $inject(AuditService);
|
|
48
50
|
|
|
49
51
|
protected readonly intentCache = $cache<RegistrationIntent>({
|
|
50
52
|
name: "registration-intents",
|
|
@@ -273,6 +275,22 @@ export class RegistrationService {
|
|
|
273
275
|
username: user.username,
|
|
274
276
|
});
|
|
275
277
|
|
|
278
|
+
const realm = this.userRealmProvider.getRealm(userRealmName);
|
|
279
|
+
|
|
280
|
+
await this.auditService.recordUser("create", {
|
|
281
|
+
userId: user.id,
|
|
282
|
+
userEmail: user.email ?? undefined,
|
|
283
|
+
userRealm: realm.name,
|
|
284
|
+
resourceId: user.id,
|
|
285
|
+
description: "User registered",
|
|
286
|
+
metadata: {
|
|
287
|
+
username: user.username,
|
|
288
|
+
email: user.email,
|
|
289
|
+
emailVerified: user.emailVerified,
|
|
290
|
+
registrationMethod: "credentials",
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
276
294
|
return user;
|
|
277
295
|
}
|
|
278
296
|
|