alepha 0.20.2 → 0.20.4

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.
Files changed (304) hide show
  1. package/README.md +0 -1
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui.css +1 -1
  4. package/dist/api/audits/index.browser.js +49 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.js +49 -0
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +2 -61
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js.map +1 -1
  12. package/dist/api/keys/index.d.ts +4 -4
  13. package/dist/api/keys/index.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +1 -10
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/parameters/index.browser.js +37 -0
  17. package/dist/api/parameters/index.browser.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +12 -68
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/parameters/index.js +57 -4
  21. package/dist/api/parameters/index.js.map +1 -1
  22. package/dist/api/payments/index.js.map +1 -1
  23. package/dist/api/users/index.browser.js +6 -0
  24. package/dist/api/users/index.browser.js.map +1 -1
  25. package/dist/api/users/index.d.ts +148 -227
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +60 -14
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts.map +1 -1
  30. package/dist/api/verifications/index.js +2 -1
  31. package/dist/api/verifications/index.js.map +1 -1
  32. package/dist/bucket/index.d.ts +77 -107
  33. package/dist/bucket/index.d.ts.map +1 -1
  34. package/dist/bucket/index.js +153 -5
  35. package/dist/bucket/index.js.map +1 -1
  36. package/dist/bucket/index.workerd.js +12 -2
  37. package/dist/bucket/index.workerd.js.map +1 -1
  38. package/dist/cache/core/index.d.ts +26 -0
  39. package/dist/cache/core/index.d.ts.map +1 -1
  40. package/dist/cache/core/index.js +11 -1
  41. package/dist/cache/core/index.js.map +1 -1
  42. package/dist/cache/core/index.workerd.js +11 -1
  43. package/dist/cache/core/index.workerd.js.map +1 -1
  44. package/dist/captcha/index.js.map +1 -1
  45. package/dist/cli/config/index.d.ts +7 -5
  46. package/dist/cli/config/index.d.ts.map +1 -1
  47. package/dist/cli/config/index.js +2 -3
  48. package/dist/cli/config/index.js.map +1 -1
  49. package/dist/cli/core/index.d.ts +637 -11660
  50. package/dist/cli/core/index.d.ts.map +1 -1
  51. package/dist/cli/core/index.js +707 -532
  52. package/dist/cli/core/index.js.map +1 -1
  53. package/dist/cli/devtools/index.d.ts +4 -8
  54. package/dist/cli/devtools/index.d.ts.map +1 -1
  55. package/dist/cli/devtools/index.js +20 -16
  56. package/dist/cli/devtools/index.js.map +1 -1
  57. package/dist/cli/platform/index.d.ts +51 -77
  58. package/dist/cli/platform/index.d.ts.map +1 -1
  59. package/dist/cli/platform/index.js +65 -15
  60. package/dist/cli/platform/index.js.map +1 -1
  61. package/dist/cli/vendor/index.d.ts +10 -13
  62. package/dist/cli/vendor/index.d.ts.map +1 -1
  63. package/dist/cli/vendor/index.js +30 -12
  64. package/dist/cli/vendor/index.js.map +1 -1
  65. package/dist/command/index.js +1 -1
  66. package/dist/command/index.js.map +1 -1
  67. package/dist/core/index.browser.js +27 -3
  68. package/dist/core/index.browser.js.map +1 -1
  69. package/dist/core/index.d.ts +8 -11
  70. package/dist/core/index.d.ts.map +1 -1
  71. package/dist/core/index.js +27 -3
  72. package/dist/core/index.js.map +1 -1
  73. package/dist/core/index.native.js +27 -3
  74. package/dist/core/index.native.js.map +1 -1
  75. package/dist/core/index.workerd.js +27 -3
  76. package/dist/core/index.workerd.js.map +1 -1
  77. package/dist/crypto/index.js.map +1 -1
  78. package/dist/datetime/index.d.ts +69 -10
  79. package/dist/datetime/index.d.ts.map +1 -1
  80. package/dist/datetime/index.js +135 -13
  81. package/dist/datetime/index.js.map +1 -1
  82. package/dist/email/core/index.js.map +1 -1
  83. package/dist/email/smtp/index.js +130 -16
  84. package/dist/email/smtp/index.js.map +1 -1
  85. package/dist/fake/index.js.map +1 -1
  86. package/dist/lock/core/index.d.ts +30 -2
  87. package/dist/lock/core/index.d.ts.map +1 -1
  88. package/dist/lock/core/index.js +35 -12
  89. package/dist/lock/core/index.js.map +1 -1
  90. package/dist/lock/redis/index.js.map +1 -1
  91. package/dist/logger/index.js +32 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/mcp/index.d.ts +238 -31
  94. package/dist/mcp/index.d.ts.map +1 -1
  95. package/dist/mcp/index.js +198 -67
  96. package/dist/mcp/index.js.map +1 -1
  97. package/dist/orm/core/index.browser.js +2 -362
  98. package/dist/orm/core/index.browser.js.map +1 -1
  99. package/dist/orm/core/index.bun.js +18 -409
  100. package/dist/orm/core/index.bun.js.map +1 -1
  101. package/dist/orm/core/index.d.ts +41 -194
  102. package/dist/orm/core/index.d.ts.map +1 -1
  103. package/dist/orm/core/index.js +27 -422
  104. package/dist/orm/core/index.js.map +1 -1
  105. package/dist/orm/postgres/index.bun.js +17 -20
  106. package/dist/orm/postgres/index.bun.js.map +1 -1
  107. package/dist/orm/postgres/index.d.ts +1 -5
  108. package/dist/orm/postgres/index.d.ts.map +1 -1
  109. package/dist/orm/postgres/index.js +17 -20
  110. package/dist/orm/postgres/index.js.map +1 -1
  111. package/dist/react/core/index.d.ts +102 -1
  112. package/dist/react/core/index.d.ts.map +1 -1
  113. package/dist/react/core/index.js +65 -1
  114. package/dist/react/core/index.js.map +1 -1
  115. package/dist/react/form/index.d.ts +6 -0
  116. package/dist/react/form/index.d.ts.map +1 -1
  117. package/dist/react/form/index.js +7 -7
  118. package/dist/react/form/index.js.map +1 -1
  119. package/dist/react/i18n/index.d.ts +7 -1
  120. package/dist/react/i18n/index.d.ts.map +1 -1
  121. package/dist/react/i18n/index.js +6 -0
  122. package/dist/react/i18n/index.js.map +1 -1
  123. package/dist/react/intro/index.js +22 -17
  124. package/dist/react/intro/index.js.map +1 -1
  125. package/dist/react/router/index.browser.js +98 -4
  126. package/dist/react/router/index.browser.js.map +1 -1
  127. package/dist/react/router/index.d.ts +58 -5
  128. package/dist/react/router/index.d.ts.map +1 -1
  129. package/dist/react/router/index.js +122 -6
  130. package/dist/react/router/index.js.map +1 -1
  131. package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
  132. package/dist/react/testing/index.js +1 -1
  133. package/dist/react/testing/index.js.map +1 -1
  134. package/dist/react/ui/index.d.ts +195 -1
  135. package/dist/react/ui/index.d.ts.map +1 -1
  136. package/dist/react/ui/index.js +64 -1
  137. package/dist/react/ui/index.js.map +1 -1
  138. package/dist/react/websocket/index.js.map +1 -1
  139. package/dist/redis/index.js.map +1 -1
  140. package/dist/scheduler/index.d.ts +1 -2
  141. package/dist/scheduler/index.d.ts.map +1 -1
  142. package/dist/scheduler/index.js +1 -1
  143. package/dist/scheduler/index.js.map +1 -1
  144. package/dist/scheduler/index.workerd.js +1 -1
  145. package/dist/scheduler/index.workerd.js.map +1 -1
  146. package/dist/security/index.browser.js.map +1 -1
  147. package/dist/security/index.d.ts.map +1 -1
  148. package/dist/security/index.js +2 -2
  149. package/dist/security/index.js.map +1 -1
  150. package/dist/server/auth/index.d.ts.map +1 -1
  151. package/dist/server/auth/index.js +24 -10
  152. package/dist/server/auth/index.js.map +1 -1
  153. package/dist/server/cookies/index.js.map +1 -1
  154. package/dist/server/core/index.browser.js +10 -3
  155. package/dist/server/core/index.browser.js.map +1 -1
  156. package/dist/server/core/index.d.ts +1 -4
  157. package/dist/server/core/index.d.ts.map +1 -1
  158. package/dist/server/core/index.js +47 -9
  159. package/dist/server/core/index.js.map +1 -1
  160. package/dist/server/links/index.browser.js.map +1 -1
  161. package/dist/server/links/index.js.map +1 -1
  162. package/dist/server/metrics/index.js +19 -1
  163. package/dist/server/metrics/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.js.map +1 -1
  165. package/dist/server/static/index.js.map +1 -1
  166. package/dist/server/swagger/index.d.ts.map +1 -1
  167. package/dist/server/swagger/index.js +4 -5
  168. package/dist/server/swagger/index.js.map +1 -1
  169. package/dist/sms/index.js.map +1 -1
  170. package/dist/system/index.browser.js.map +1 -1
  171. package/dist/system/index.js.map +1 -1
  172. package/dist/system/index.workerd.js.map +1 -1
  173. package/dist/topic/core/index.js.map +1 -1
  174. package/dist/websocket/index.browser.js +32 -5
  175. package/dist/websocket/index.browser.js.map +1 -1
  176. package/dist/websocket/index.d.ts +3 -1
  177. package/dist/websocket/index.d.ts.map +1 -1
  178. package/dist/websocket/index.js +42 -6
  179. package/dist/websocket/index.js.map +1 -1
  180. package/package.json +685 -274
  181. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  182. package/src/api/jobs/__tests__/$job.spec.ts +5 -1
  183. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  184. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  185. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  186. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  187. package/src/api/users/entities/sessions.ts +6 -0
  188. package/src/api/users/jobs/UserJobs.ts +44 -17
  189. package/src/api/users/providers/RealmProvider.ts +4 -0
  190. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  191. package/src/api/users/services/SessionService.ts +27 -0
  192. package/src/api/users/services/UserService.ts +1 -5
  193. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  194. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  195. package/src/api/verifications/services/VerificationService.ts +1 -0
  196. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  197. package/src/bucket/index.ts +19 -2
  198. package/src/bucket/primitives/$bucket.ts +9 -1
  199. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  200. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  201. package/src/cache/core/index.ts +29 -0
  202. package/src/cache/core/primitives/$cache.ts +14 -1
  203. package/src/cli/config/defineConfig.ts +13 -15
  204. package/src/cli/core/__tests__/init.spec.ts +214 -7
  205. package/src/cli/core/commands/init.ts +12 -0
  206. package/src/cli/core/services/PackageManagerUtils.ts +23 -6
  207. package/src/cli/core/services/ProjectScaffolder.ts +315 -33
  208. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  209. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  210. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  211. package/src/cli/core/templates/agentMd.ts +2 -10
  212. package/src/cli/core/templates/apiIndexTs.ts +23 -1
  213. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  214. package/src/cli/core/templates/mainCss.ts +1 -0
  215. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  216. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  217. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  218. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  219. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  220. package/src/cli/core/templates/webAppRouterTs.ts +104 -1
  221. package/src/cli/core/templates/webIndexTs.ts +23 -1
  222. package/src/cli/devtools/index.ts +12 -26
  223. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  224. package/src/cli/platform/index.ts +15 -24
  225. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  226. package/src/cli/vendor/index.ts +14 -23
  227. package/src/command/providers/CliProvider.ts +1 -1
  228. package/src/core/Alepha.ts +11 -1
  229. package/src/core/helpers/ref.ts +18 -0
  230. package/src/core/index.shared.ts +1 -0
  231. package/src/core/interfaces/Service.ts +3 -1
  232. package/src/core/providers/SchemaValidator.ts +9 -1
  233. package/src/core/providers/TypeProvider.ts +2 -3
  234. package/src/datetime/REFACTORING.md +118 -0
  235. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  236. package/src/lock/core/index.ts +31 -0
  237. package/src/lock/core/primitives/$lock.ts +14 -1
  238. package/src/logger/services/Logger.ts +1 -1
  239. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  240. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  241. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  242. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  243. package/src/mcp/helpers/jsonrpc.ts +26 -1
  244. package/src/mcp/index.ts +10 -5
  245. package/src/mcp/interfaces/McpTypes.ts +83 -6
  246. package/src/mcp/primitives/$prompt.ts +18 -1
  247. package/src/mcp/primitives/$resource.ts +18 -1
  248. package/src/mcp/primitives/$tool.ts +83 -7
  249. package/src/mcp/providers/McpServerProvider.ts +74 -16
  250. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  251. package/src/orm/REFACTORING.md +330 -0
  252. package/src/orm/__tests__/$repository-tests.ts +1 -0
  253. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  254. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  255. package/src/orm/core/index.shared.ts +0 -2
  256. package/src/orm/core/index.ts +1 -2
  257. package/src/orm/core/primitives/$repository.ts +3 -6
  258. package/src/orm/core/primitives/$transactional.ts +11 -0
  259. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  260. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  261. package/src/orm/core/schemas/updateSchema.ts +1 -1
  262. package/src/orm/core/services/ModelBuilder.ts +1 -13
  263. package/src/orm/core/services/PgRelationManager.ts +4 -2
  264. package/src/orm/core/services/Repository.ts +1 -42
  265. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  266. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  267. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  268. package/src/react/core/hooks/useQuery.ts +153 -0
  269. package/src/react/core/index.ts +1 -0
  270. package/src/react/form/services/FormModel.ts +15 -6
  271. package/src/react/form/services/parseField.ts +8 -0
  272. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  273. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  274. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  275. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  276. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  277. package/src/react/router/primitives/$page.ts +28 -4
  278. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  279. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  280. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  281. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  282. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  283. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  284. package/src/react/ui/index.ts +6 -0
  285. package/src/react/ui/services/SchemaControl.ts +209 -0
  286. package/src/scheduler/providers/CronProvider.ts +1 -1
  287. package/src/security/primitives/$basicAuth.ts +1 -1
  288. package/src/security/primitives/$issuer.ts +6 -3
  289. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  290. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  291. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  292. package/src/server/core/errors/ValidationError.ts +13 -1
  293. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  294. package/src/server/core/primitives/$action.ts +16 -5
  295. package/src/server/core/providers/ServerProvider.ts +1 -1
  296. package/src/server/core/providers/ServerRouterProvider.ts +28 -6
  297. package/src/server/core/services/HttpClient.ts +1 -1
  298. package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
  299. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  300. package/src/websocket/services/WebSocketClient.ts +11 -5
  301. package/src/mcp/transports/SseMcpTransport.ts +0 -182
  302. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  303. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  304. package/src/orm/core/primitives/$view.ts +0 -88
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES"],"sources":["../../../src/api/users/audits/UserAudits.ts","../../../src/api/users/buckets/UserBuckets.ts","../../../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/RealmProvider.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/realmConfigSchema.ts","../../../src/api/users/controllers/RealmController.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/jobs/UserJobs.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/users/primitives/$realm.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 { $inject } from \"alepha\";\nimport { AuditService, type CreateAudit } from \"alepha/api/audits\";\n\ntype AuditContext = Omit<CreateAudit, \"type\" | \"action\">;\n\n/**\n * User-specific audit wrapper service.\n *\n * This service wraps the core AuditService to provide user-related audit logging.\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserAudits)`.\n */\nexport class UserAudits {\n protected readonly auditService = $inject(AuditService);\n\n /**\n * Record a user-related audit event.\n */\n public recordUser(\n action:\n | \"create\"\n | \"update\"\n | \"delete\"\n | \"role_change\"\n | \"enable\"\n | \"disable\",\n context: AuditContext,\n ) {\n return this.auditService.recordUser(action, context);\n }\n\n /**\n * Record an authentication-related audit event.\n */\n public recordAuth(\n action: \"login\" | \"logout\" | \"login_failed\" | \"token_refresh\",\n context: AuditContext,\n ) {\n return this.auditService.recordAuth(action, context);\n }\n\n /**\n * Record a generic audit event.\n */\n public record(category: string, action: string, context: AuditContext) {\n return this.auditService.record(category, action, context);\n }\n}\n","import { $bucket } from \"alepha/bucket\";\n\n/**\n * User-specific file storage wrapper service.\n *\n * This service provides file storage for user-related files such as:\n * - User avatars/profile pictures\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserBuckets)`.\n */\nexport class UserBuckets {\n /**\n * Bucket for user avatar storage.\n */\n public readonly avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n}\n","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, sql } 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: 30,\n // pattern is handled at the realm settings level\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 organizationId: db.organization(),\n }),\n indexes: [\n {\n expressions: (self) => [self.realm, sql`LOWER(${self.username})`],\n unique: true,\n name: \"users_realm_username_lower_idx\",\n },\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 indexes: [\n \"userId\",\n \"provider\",\n { columns: [\"userId\", \"provider\"] },\n { columns: [\"provider\", \"providerUserId\"], unique: true },\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\n/**\n * Tri-state field requirement for realm auth settings.\n *\n * - `\"none\"`: Field is disabled and not shown.\n * - `\"optional\"`: Field is shown but not required.\n * - `\"required\"`: Field is shown and required.\n */\nexport type FieldRequirement = \"none\" | \"optional\" | \"required\";\n\nconst fieldRequirement = (description: string) =>\n t.union([t.const(\"none\"), t.const(\"optional\"), t.const(\"required\")], {\n description,\n });\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 email: fieldRequirement(\n \"Email address field requirement for user accounts\",\n ),\n username: fieldRequirement(\"Username field requirement for user accounts\"),\n usernameRegExp: t.string({\n description:\n \"Regular expression that usernames must match (if username is enabled)\",\n }),\n phoneNumber: fieldRequirement(\n \"Phone number field requirement 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 firstNameLastName: fieldRequirement(\n \"First and last name field requirement for user accounts\",\n ),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n captchaRequired: t.boolean({\n description:\n \"Require captcha verification on registration (needs a CaptchaProvider registered, e.g. TurnstileCaptchaProvider)\",\n }),\n adminEmails: t.array(t.email(), {\n description:\n \"List of email addresses that are automatically promoted to admin role on login\",\n }),\n adminUsernames: t.array(t.text(), {\n description:\n \"List of usernames that are automatically promoted to admin role on login\",\n }),\n defaultRoles: t.array(t.string(), {\n description: \"Default roles assigned to newly registered users\",\n }),\n verifyEmailUrl: t.optional(\n t.string({\n description:\n \"Base URL for email verification links (used when verification method is 'link'). Token and email are appended as query params.\",\n }),\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 loginRateLimit: t.object({\n ipMaxAttempts: t.integer({\n description:\n \"Max failed login attempts per IP before temporary lockout\",\n default: 15,\n minimum: 1,\n }),\n accountMaxAttempts: t.integer({\n description:\n \"Max failed login attempts per account before temporary lockout\",\n default: 5,\n minimum: 1,\n }),\n windowMs: t.integer({\n description: \"Rate limit window duration in milliseconds\",\n default: 15 * 60 * 1000,\n minimum: 1000,\n }),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n email: \"required\" as FieldRequirement,\n username: \"none\" as FieldRequirement,\n usernameRegExp: \"^[a-zA-Z0-9_]{3,30}$\",\n phoneNumber: \"none\" as FieldRequirement,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n captchaRequired: false,\n firstNameLastName: \"none\" as FieldRequirement,\n adminEmails: [],\n adminUsernames: [],\n defaultRoles: [\"user\"],\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n loginRateLimit: {\n ipMaxAttempts: 15,\n accountMaxAttempts: 5,\n windowMs: 15 * 60 * 1000,\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 indexes: [\"userId\", \"expiresAt\", { column: \"refreshToken\", unique: true }],\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport type { ParameterPrimitive } from \"alepha/api/parameters\";\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 { RealmFeatures, RealmOptions } from \"../primitives/$realm.ts\";\n\nexport interface RealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface Realm {\n name: string;\n repositories: RealmRepositories;\n settings: RealmAuthSettings;\n features: RealmFeatures;\n settingsParameter?: ParameterPrimitive<typeof realmAuthSettingsAtom.schema>;\n getSettings(): Promise<RealmAuthSettings>;\n}\n\nexport class RealmProvider {\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, Realm>();\n\n public register(realmName: string, realmOptions: RealmOptions = {}) {\n if (realmName.includes(\".\")) {\n throw new AlephaError(\n `Realm name \"${realmName}\" must not contain dots — dots are reserved for parameter tree paths`,\n );\n }\n\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n avatars: false,\n audits: false,\n ...realmOptions.features,\n };\n\n const realm: Realm = {\n name: realmName,\n repositories: {\n identities: realmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: realmOptions.entities?.sessions ?? this.defaultSessions,\n users: realmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...realmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...realmOptions.settings?.passwordPolicy,\n },\n loginRateLimit: {\n ...realmAuthSettingsAtom.options.default.loginRateLimit,\n ...realmOptions.settings?.loginRateLimit,\n },\n },\n features,\n getSettings: async function () {\n if (this.settingsParameter) {\n return await this.settingsParameter.get();\n }\n return this.settings;\n },\n };\n this.realms.set(realmName, realm);\n return this.getRealm(realmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(realmName = DEFAULT_USER_REALM_NAME): Realm {\n let realm = this.realms.get(realmName);\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 (realmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(realmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing realm '${realmName}', please declare $realm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(realmName).repositories.identities;\n }\n\n public sessionRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(realmName).repositories.sessions;\n }\n\n public userRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(realmName).repositories.users;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\n\nexport class IdentityService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.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).getById(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.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:identity:read\"] })],\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 use: [$secure({ permissions: [\"admin:identity:read\"] })],\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 use: [$secure({ permissions: [\"admin:identity:delete\"] })],\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 { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.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).getById(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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:session:read\"] })],\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 use: [$secure({ permissions: [\"admin:session:read\"] })],\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 use: [$secure({ permissions: [\"admin:session:delete\"] })],\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 accountLockout = $notification({\n category: \"security\",\n description:\n \"Email sent to users when their account is temporarily locked due to too many failed login attempts.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Account temporarily locked\",\n body: (it) => `\n\t\t\t\t<h1>Account Temporarily Locked</h1>\n\t\t\t\t<p>Hi ${it.email},</p>\n\t\t\t\t<p>Your account has been temporarily locked due to too many failed login attempts.</p>\n\t\t\t\t<p>If this was you, please wait ${it.lockoutMinutes} minutes before trying again. If you've forgotten your password, you can reset it using the password reset feature.</p>\n\t\t\t\t<p>If this wasn't you, someone may be trying to access your account. We recommend changing your password as soon as possible.</p>\n\t\t\t\t<p>Best regards,<br>The Team</p>\n\t\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n lockoutMinutes: 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, Alepha } 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 { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.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 ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\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 from realm settings (server-controlled, not user input)\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n const baseUrl = realmSettings.verifyEmailUrl ?? \"/verify-email\";\n const url = new URL(baseUrl, \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = realmSettings.verifyEmailUrl\n ? `${baseUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications(userRealmName)?.emailVerificationLink.push(\n {\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n },\n );\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications(userRealmName)?.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).getOne({\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.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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).findOne({\n where: { email: { eq: email } },\n });\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).getById(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.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n\n // Check for existing user based on provided unique fields (scoped to realm)\n if (data.username) {\n const existingUser = await this.users(userRealmName).findOne({\n where: { realm: realm.name, username: { ilike: data.username } },\n });\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).findOne({\n where: { realm: realm.name, email: { eq: data.email } },\n });\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).findOne({\n where: { realm: realm.name, phoneNumber: { eq: data.phoneNumber } },\n });\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 ?? realmSettings.defaultRoles,\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.userAudits(userRealmName)?.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.realmProvider.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.userAudits(userRealmName)?.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 // Clean up related sessions and identities before deleting the user\n await this.realmProvider\n .sessionRepository(userRealmName)\n .deleteMany({ userId: { eq: id } });\n await this.realmProvider\n .identityRepository(userRealmName)\n .deleteMany({ userId: { eq: id } });\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:user:read\"] })],\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 use: [$secure({ permissions: [\"admin:user:read\"] })],\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 use: [$secure({ permissions: [\"admin:user:create\"] })],\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 use: [$secure({ permissions: [\"admin:user:update\"] })],\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 use: [$secure({ permissions: [\"admin:user:delete\"] })],\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, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const realmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n captchaSiteKey: t.optional(\n t.string({\n description:\n \"Public site key for the captcha widget (when settings.captchaRequired is true)\",\n }),\n ),\n});\n\nexport type RealmConfig = Static<typeof realmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { CaptchaProvider } from \"alepha/captcha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { $etag } from \"alepha/server/etag\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { realmConfigSchema } from \"../schemas/realmConfigSchema.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 RealmController {\n protected readonly url = \"/realms\";\n protected readonly group = \"realms\";\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n protected readonly captchaProvider = $inject(CaptchaProvider);\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 use: [$etag()],\n schema: {\n query: t.object({\n realmName: t.optional(t.string()),\n }),\n response: realmConfigSchema,\n },\n handler: async ({ query }) => {\n const realm = this.realmProvider.getRealm(query.realmName);\n const settings = await realm.getSettings();\n const realmName = realm.name;\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n captchaSiteKey: settings.captchaRequired\n ? this.captchaProvider.getSiteKey()\n : undefined,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n group: this.group,\n path: `${this.url}/check-username`,\n schema: {\n query: t.object({\n realmName: 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.realmName;\n const userRepository = this.realmProvider.userRepository(realmName);\n\n const existingUser = await userRepository.findOne({\n where: { username: { eq: body.username } },\n });\n\n return {\n available: !existingUser,\n };\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 // Captcha token — required when the realm has `captchaRequired: true`.\n // Validated at intent creation (before any verification email is sent).\n captchaToken: t.optional(\n t.string({\n description: \"Captcha response token (if captcha is required)\",\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 captchaSiteKey: t.optional(\n t.string({\n description:\n \"Public site key the client should render (when expectCaptcha is true)\",\n }),\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, Alepha } 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 type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 alepha = $inject(Alepha);\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 realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"api:users:password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Validate a password against the realm's password policy.\n */\n public validatePasswordPolicy(\n password: string,\n policy: RealmAuthSettings[\"passwordPolicy\"],\n ): void {\n if (password.length < policy.minLength) {\n throw new BadRequestError(\n `Password must be at least ${policy.minLength} characters`,\n );\n }\n if (policy.requireUppercase && !/[A-Z]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one uppercase letter\",\n );\n }\n if (policy.requireLowercase && !/[a-z]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one lowercase letter\",\n );\n }\n if (policy.requireNumbers && !/\\d/.test(password)) {\n throw new BadRequestError(\"Password must contain at least one number\");\n }\n if (policy.requireSpecialCharacters && !/[^a-zA-Z0-9]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one special character\",\n );\n }\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 // Check if password reset is allowed for this realm\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n if (realmSettings.resetPasswordAllowed === false) {\n this.log.debug(\"Password reset not allowed for realm\", { userRealmName });\n return { intentId, expiresAt };\n }\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\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).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\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(userRealmName)?.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 // Validate password against realm policy before consuming the verification code\n const realm = this.realmProvider.getRealm(intent.realmName);\n const realmSettings = await realm.getSettings();\n this.validatePasswordPolicy(body.newPassword, realmSettings.passwordPolicy);\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 // 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 // Invalidate intent after all operations succeed,\n // so the user can retry if password update or session cleanup fails.\n // The verification code was already consumed (verifiedAt set), so replay is\n // prevented even if the intent is still in cache.\n await this.intentCache.invalidate(body.intentId);\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n\n // Audit: password reset\n await this.userAudits(intent.realmName)?.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.userAudits(intent.realmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\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\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 // Validate password against realm policy\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n this.validatePasswordPolicy(newPassword, realmSettings.passwordPolicy);\n\n // Find user and identity\n const user = await this.users(userRealmName).getOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).getOne({\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 // Audit: password reset (legacy method)\n await this.userAudits(userRealmName)?.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.userAudits(userRealmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\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}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject, Alepha, AlephaError } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { CaptchaProvider } from \"alepha/captcha\";\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 { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { CredentialService } from \"./CredentialService.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 alepha = $inject(Alepha);\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 realmProvider = $inject(RealmProvider);\n protected readonly credentialService = $inject(CredentialService);\n protected readonly captchaProvider = $inject(CaptchaProvider);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"api:users:registrations\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n protected readonly rateLimitCache = $cache<number>({\n name: \"api:users:registration-rate-limit\",\n ttl: [15, \"minutes\"],\n });\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n\n throw new AlephaError(\n `Notifications feature is not enabled for realm \"${realmName}\". Please enable it in the realm settings to use registration notifications.`,\n );\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 // IP rate limiting\n const request = this.alepha.store.get(\"alepha.http.request\");\n const ipKey = request?.ip ? `register:ip:${request.ip}` : undefined;\n if (ipKey) {\n const count = (await this.rateLimitCache.get(ipKey)) ?? 0;\n if (count >= 10) {\n this.log.warn(\"Registration rate limit exceeded\", { ip: request?.ip });\n throw new BadRequestError(\n \"Too many registration attempts, please try again later\",\n );\n }\n await this.rateLimitCache.set(ipKey, count + 1);\n }\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\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?.username === \"required\" && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (body.username) {\n // Security note: usernameRegExp is admin-controlled (from realmAuthSettingsAtom),\n // not user input. Default is ^[a-zA-Z0-9_]{3,30}$ which is ReDoS-safe.\n // No need for regex timeout or safe-regex validation here.\n const usernameRegExp = realmSettings?.usernameRegExp;\n if (usernameRegExp) {\n const regex = new RegExp(usernameRegExp);\n if (!regex.test(body.username)) {\n this.log.debug(\"Registration rejected: username regex mismatch\", {\n userRealmName,\n username: body.username,\n });\n throw new BadRequestError(\n \"Username does not meet the required format\",\n );\n }\n }\n }\n\n if (realmSettings?.email === \"required\" && !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?.phoneNumber === \"required\" && !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 // Validate password against realm policy\n this.credentialService.validatePasswordPolicy(\n body.password,\n realmSettings.passwordPolicy,\n );\n\n // Validate captcha BEFORE any side effects (email sends, rate-limit counters).\n // This blocks bots from triggering verification emails on other people's addresses.\n if (realmSettings?.captchaRequired === true) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n const valid = await this.captchaProvider.verify(body.captchaToken);\n if (!valid) {\n throw new BadRequestError(\"Captcha verification failed\");\n }\n }\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, // validated above, single-use — no gate at complete time\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email, userRealmName);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber, userRealmName);\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 captchaSiteKey: requirements.captcha\n ? this.captchaProvider.getSiteKey()\n : undefined,\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.realmProvider.userRepository(userRealmName);\n const identityRepository =\n this.realmProvider.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 // Captcha is validated at intent creation (see createIntent) so bots can't\n // trigger verification emails; nothing to re-check here.\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 const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n\n // Create the user\n const user = await userRepository.create({\n realm: realm.name,\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: realmSettings.defaultRoles,\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 // Invalidate intent after both user and identity creation succeed,\n // so the user can retry if either operation fails.\n await this.intentCache.invalidate(body.intentId);\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n await this.userAudits(userRealmName)?.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 realm = this.realmProvider.getRealm(userRealmName);\n const userRepository = this.realmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository.findOne({\n where: { realm: realm.name, username: { ilike: body.username } },\n });\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.findOne({\n where: { realm: realm.name, email: { eq: body.email } },\n });\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.findOne({\n where: { realm: realm.name, phoneNumber: { eq: body.phoneNumber } },\n });\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(\n email: string,\n realmName?: string,\n ): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications(realmName).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\", { email });\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(\n phoneNumber: string,\n realmName?: string,\n ): 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(realmName).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 { $secure } from \"alepha/security\";\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 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 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 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 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. When using \"link\", configure verifyEmailUrl in realm settings.',\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 );\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 use: [$secure()],\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 { $inject } from \"alepha\";\nimport { $job } from \"alepha/api/jobs\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { sessions } from \"../entities/sessions.ts\";\n\n/**\n * User-specific jobs wrapper service.\n *\n * This service handles user-related scheduled jobs such as:\n * - Session purge (cleaning up expired sessions)\n * - Verification code cleanup\n * - Inactive user notifications\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserJobs)`.\n */\nexport class UserJobs {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly sessionRepository = $repository(sessions);\n\n /**\n * Purge expired sessions from the database.\n *\n * Runs hourly (at :00) and deletes sessions whose `expiresAt` has passed.\n */\n public readonly purgeExpiredSessions = $job({\n name: \"api:users:purgeExpiredSessions\",\n cron: \"0 * * * *\", // Hourly at minute 0\n handler: async () => {\n const now = this.dateTimeProvider.nowISOString();\n\n this.log.info(\"Starting expired sessions purge\", { cutoffTime: now });\n\n const expiredSessions = await this.sessionRepository.findMany({\n where: {\n expiresAt: { lt: now },\n },\n });\n\n if (expiredSessions.length === 0) {\n this.log.info(\"No expired sessions found\");\n return;\n }\n\n this.log.info(\"Found expired sessions\", {\n count: expiredSessions.length,\n });\n\n const deletedIds = await this.sessionRepository.deleteMany({\n expiresAt: { lt: now },\n });\n\n this.log.info(\"Expired sessions purged successfully\", {\n deletedCount: deletedIds.length,\n });\n },\n });\n}\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { FileController } from \"alepha/api/files\";\nimport { CacheProvider } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { BadRequestError, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 realmProvider = $inject(RealmProvider);\n protected readonly fileController = $client<FileController>();\n protected readonly cacheProvider = $inject(CacheProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Check if user should be auto-promoted to admin based on adminEmails/adminUsernames settings.\n * If user matches and doesn't have admin role, promote them.\n */\n protected async ensureAdminRole(\n user: {\n id: string;\n email?: string | null;\n username?: string | null;\n roles: string[];\n },\n userRealmName?: string,\n ): Promise<boolean> {\n if (user.roles.includes(\"admin\")) return false;\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const settings = await realm.getSettings();\n const { name } = realm;\n const adminEmails = settings.adminEmails ?? [];\n const adminUsernames = settings.adminUsernames ?? [];\n\n const isAdminByEmail = user.email && adminEmails.includes(user.email);\n const isAdminByUsername =\n user.username && adminUsernames.includes(user.username);\n\n if (!isAdminByEmail && !isAdminByUsername) return false;\n\n // Promote to admin\n user.roles = [...user.roles.filter((r) => r !== \"admin\"), \"admin\"];\n await this.users(userRealmName).updateById(user.id, { roles: user.roles });\n\n const reason = isAdminByEmail ? \"adminEmails\" : \"adminUsernames\";\n this.log.info(`User auto-promoted to admin via ${reason} setting`, {\n userId: user.id,\n email: user.email,\n username: user.username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordUser(\"role_change\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User auto-promoted to admin via ${reason} setting`,\n metadata: { addedRole: \"admin\", reason },\n });\n\n return true;\n }\n\n /**\n * Generate a unique username from an OAuth profile.\n *\n * 1. Extract candidate from email prefix\n * 2. Sanitize against realm's usernameRegExp (strip invalid chars, truncate)\n * 3. Check case-insensitive uniqueness, append suffix (2, 3, ...) if taken\n * 4. Fall back to \"user\" + random 6-char alphanumeric if all else fails\n */\n protected async generateUniqueUsername(\n profile: OAuth2Profile,\n realmSettings: any,\n users: any,\n ): Promise<string> {\n const maxLength = 30;\n const maxSuffixAttempts = 10;\n\n // Extract candidate from email or profile name\n let candidate = profile.email?.split(\"@\")[0] ?? profile.name ?? \"\";\n\n // Strip characters not allowed in usernames (keep alphanumeric, underscore, dot, hyphen)\n candidate = candidate.replace(/[^a-zA-Z0-9_.-]/g, \"\");\n\n // If realm has a custom regex, further sanitize\n if (realmSettings?.usernameRegExp) {\n try {\n const regex = new RegExp(realmSettings.usernameRegExp);\n if (!regex.test(candidate)) {\n // Strip to basic alphanumeric as safe fallback\n candidate = candidate.replace(/[^a-zA-Z0-9_]/g, \"\");\n }\n } catch {\n // Invalid regex, continue with sanitized candidate\n }\n }\n\n // Ensure minimum length\n if (candidate.length < 3) {\n candidate = `user${candidate}`;\n }\n\n // Truncate to leave room for suffix\n candidate = candidate.slice(0, maxLength - 2);\n\n // Check uniqueness (case-insensitive exact match)\n const isAvailable = async (name: string) => {\n const existing = await users.findOne({\n where: { username: { ilike: name } },\n });\n return !existing;\n };\n\n if (await isAvailable(candidate)) {\n return candidate;\n }\n\n // Try with numeric suffix\n for (let i = 2; i <= maxSuffixAttempts + 1; i++) {\n const withSuffix = `${candidate}${i}`;\n if (withSuffix.length <= maxLength && (await isAvailable(withSuffix))) {\n return withSuffix;\n }\n }\n\n // Final fallback: random username\n const random = Math.random().toString(36).slice(2, 8);\n return `user${random}`;\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 protected static readonly LOGIN_CACHE_NAME = \"login-rate-limit\";\n\n /**\n * Check if a login key is currently locked out.\n * Read-only — does not increment the counter.\n */\n protected async isLoginLocked(key: string, max: number): Promise<boolean> {\n try {\n const count = await this.cacheProvider.getTyped<number>(\n SessionService.LOGIN_CACHE_NAME,\n key,\n );\n return count != null && count >= max;\n } catch (error) {\n this.log.warn(\n \"Failed to check login rate limit, allowing attempt\",\n error,\n );\n return false;\n }\n }\n\n /**\n * Record a failed login attempt. Uses getTyped + setTyped (not incr) so that\n * each write refreshes the TTL — implementing sliding-window behavior.\n *\n * Returns `true` if this failure just crossed the lockout threshold.\n */\n protected async recordFailedLogin(\n key: string,\n max: number,\n windowMs: number,\n ): Promise<boolean> {\n try {\n const count =\n (await this.cacheProvider.getTyped<number>(\n SessionService.LOGIN_CACHE_NAME,\n key,\n )) ?? 0;\n const newCount = count + 1;\n await this.cacheProvider.setTyped(\n SessionService.LOGIN_CACHE_NAME,\n key,\n newCount,\n { ttl: windowMs },\n );\n return newCount === max;\n } catch (error) {\n this.log.warn(\"Failed to record failed login attempt\", error);\n return false;\n }\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 realm = this.realmProvider.getRealm(userRealmName);\n const settings = await realm.getSettings();\n const { name } = realm;\n const { loginRateLimit } = settings;\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 // IP rate limit check (global, cross-realm) — before any DB work\n const request = this.alepha.store.get(\"alepha.http.request\");\n const ipKey = request?.ip ? `login:ip:${request.ip}` : undefined;\n\n if (ipKey) {\n const ipLocked = await this.isLoginLocked(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n );\n if (ipLocked) {\n this.log.warn(\"Login blocked — IP rate limit exceeded\", {\n ip: request?.ip,\n });\n throw new InvalidCredentialsError();\n }\n }\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.username !== \"none\" && isUsername) {\n // validate username format if regex is provided\n if (settings.usernameRegExp) {\n const regex = new RegExp(settings.usernameRegExp);\n if (!regex.test(username)) {\n this.log.warn(\"Username does not match required format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Username does not match required format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n }\n where.username = { ilike: username };\n } else if (settings.email !== \"none\" && isEmail) {\n where.email = username;\n } else if (settings.phoneNumber !== \"none\" && 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.userAudits(userRealmName)?.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 });\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.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"User not found\",\n metadata: { provider, username },\n });\n\n // Only increment IP counter (no user ID to track)\n if (ipKey) {\n const justLocked = await this.recordFailedLogin(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (justLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n success: false,\n description:\n \"IP temporarily locked due to too many failed login attempts\",\n metadata: { ip: request?.ip },\n },\n );\n }\n }\n\n throw new InvalidCredentialsError();\n }\n\n // Check if user account is enabled\n if (!user.enabled) {\n this.log.warn(\"Login attempt for disabled account\", {\n userId: user.id,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Login attempt for disabled account\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n // Account rate limit check (per-realm)\n const accountKey = `login:account:${name}:${user.id}`;\n const accountLocked = await this.isLoginLocked(\n accountKey,\n loginRateLimit.accountMaxAttempts,\n );\n if (accountLocked) {\n this.log.warn(\"Login blocked — account rate limit exceeded\", {\n userId: user.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.getOne({\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.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Invalid password\",\n metadata: { provider, username },\n });\n\n // Record failed attempt on both IP and account counters\n if (ipKey) {\n const ipJustLocked = await this.recordFailedLogin(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (ipJustLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n success: false,\n description:\n \"IP temporarily locked due to too many failed login attempts\",\n metadata: { ip: request?.ip },\n },\n );\n }\n }\n\n const accountJustLocked = await this.recordFailedLogin(\n accountKey,\n loginRateLimit.accountMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (accountJustLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n resourceId: user.id,\n success: false,\n description:\n \"Account temporarily locked due to too many failed login attempts\",\n metadata: { userId: user.id },\n },\n );\n\n // Notify user about account lockout\n if (user.email) {\n const lockoutMinutes = Math.round(loginRateLimit.windowMs / 60_000);\n await this.userNotifications(userRealmName)?.accountLockout.push({\n contact: user.email,\n variables: { email: user.email, lockoutMinutes },\n });\n }\n }\n\n throw new InvalidCredentialsError();\n }\n\n await this.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\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.store.get(\"alepha.http.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 // getOne() throws DbEntityNotFoundError if not found — never returns null.\n // No null check needed here.\n const session = await this.sessions(userRealmName).getOne({\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(session.id);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n const user = await this.users(userRealmName).getOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n // Check if user account is still enabled\n if (!user.enabled) {\n this.log.warn(\"Session refresh for disabled account\", {\n userId: user.id,\n sessionId: session.id,\n });\n await this.sessions(userRealmName).deleteById(session.id);\n throw new UnauthorizedError(\"Account disabled\");\n }\n\n // Auto-promote to admin if configured (handles \"I promote you admin\" case)\n await this.ensureAdminRole(user, userRealmName);\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\n // Get session info before deletion for audit\n const session = await this.sessions(userRealmName).findOne({\n where: { refreshToken: { eq: refreshToken } },\n });\n\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n\n if (session) {\n const { name } = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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.realmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities.findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n });\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.getById(identity.userId);\n\n await this.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\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.findOne({\n where: {\n realm: realm.name,\n email: profile.email,\n },\n });\n\n if (existing) {\n // Refuse auto-link if the OAuth provider explicitly says email is not verified\n if (profile.email_verified === false) {\n this.log.warn(\n \"OAuth2 profile email not verified by provider, refusing auto-link\",\n { provider, email: profile.email, userId: existing.id },\n );\n throw new BadRequestError(\n \"Cannot link account: email not verified by provider\",\n );\n }\n\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.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(existing, userRealmName);\n\n return existing;\n }\n\n const realmSettings = await realm.getSettings();\n const adminEmails = realmSettings?.adminEmails ?? [];\n const isAdmin = profile.email && adminEmails.includes(profile.email);\n\n if (realmSettings?.registrationAllowed === false && !isAdmin) {\n this.log.warn(\"Registration not allowed for realm via OAuth2\", {\n provider,\n userRealmName,\n });\n throw new BadRequestError(\"Account doesn't exist\");\n }\n\n const username = await this.generateUniqueUsername(\n profile,\n realmSettings,\n users,\n );\n\n const user = await users.create({\n realm: realm.name,\n username,\n email: profile.email,\n firstName: profile.given_name,\n lastName: profile.family_name,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: realmSettings.defaultRoles,\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.userAudits(userRealmName)?.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.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n }\n}\n","import { $context } from \"alepha\";\nimport { AlephaApiKeys, ApiKeyService } from \"alepha/api/keys\";\nimport { $parameter, AlephaApiParameters } from \"alepha/api/parameters\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $issuer,\n $permission,\n type IssuerPrimitive,\n type IssuerPrimitiveOptions,\n type IssuerResolver,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authApple,\n $authCredentials,\n $authFacebook,\n $authFranceConnect,\n $authGithub,\n $authGoogle,\n $authMicrosoft,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport { UserBuckets } from \"../buckets/UserBuckets.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 { UserJobs } from \"../jobs/UserJobs.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\n\nexport type RealmPrimitive = IssuerPrimitive & 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 $realm = (options: RealmOptions = {}): RealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const realmProvider = alepha.inject(RealmProvider);\n\n const name = options.issuer?.name ?? DEFAULT_USER_REALM_NAME;\n\n options.settings ??= {};\n\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n avatars: false,\n audits: false,\n ...options.features,\n };\n\n // When notifications are disabled, force verification-dependent settings to false\n // These features require sending codes via email/SMS which won't work without notifications\n if (!features.notifications) {\n options.settings.verifyEmailRequired = false;\n options.settings.verifyPhoneRequired = false;\n options.settings.resetPasswordAllowed = false;\n }\n\n const realmRegistration = realmProvider.register(name, options);\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Enable features based on configuration\n // Each feature registers its wrapper service which internally uses the module primitives\n\n if (features.avatars) {\n alepha.with(UserBuckets);\n }\n\n if (features.audits) {\n alepha.with(UserAudits);\n }\n\n if (features.jobs) {\n alepha.with(UserJobs);\n }\n\n if (features.notifications) {\n alepha.with(UserNotifications);\n alepha.with(AlephaApiVerification);\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Collect custom resolvers that will be registered during $issuer.onInit()\n // This ensures they are registered AFTER the realm is created (not on the default test realm)\n const customResolvers: IssuerResolver[] = [\n ...(options.issuer?.resolvers ?? []),\n ];\n\n // Enable API key authentication - must be added to customResolvers before $issuer() call\n if (features.apiKeys) {\n alepha.with(AlephaApiKeys);\n const apiKeyService = alepha.inject(ApiKeyService);\n customResolvers.push(apiKeyService.createResolver());\n }\n\n const realm: RealmPrimitive = $issuer({\n ...options.issuer,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n resolvers: customResolvers,\n roles: options.issuer?.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.issuer?.settings,\n },\n });\n\n $permission({\n name: \"admin:access\",\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 async (credentials: Credentials) => {\n const user = await sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n // Compose display name from first+last for OIDC `name` claim.\n // Without this, credentials-registered users appear as \"Anonymous User\".\n const composedName =\n [user.firstName, user.lastName]\n .filter((s): s is string => !!s?.trim())\n .join(\" \")\n .trim() || undefined;\n return { ...user, name: composedName };\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 realmRegistration.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 if (identities.apple) {\n auth.apple = $authApple(realm);\n }\n\n if (identities.facebook) {\n auth.facebook = $authFacebook(realm);\n }\n\n if (identities.microsoft) {\n auth.microsoft = $authMicrosoft(realm);\n }\n\n if (identities.franceconnect) {\n auth.franceconnect = $authFranceConnect(realm);\n }\n\n alepha.with(() => auth);\n }\n\n if (features.parameters) {\n alepha.with(AlephaApiParameters);\n const settingsParam = $parameter({\n name: `api.realms.settings.${name}`,\n description: `Authentication and registration settings for realm \"${name}\"`,\n schema: realmAuthSettingsAtom.schema,\n default: realmRegistration.settings,\n });\n realmRegistration.settingsParameter = settingsParam;\n alepha.with(() => ({ [`realmSettings_${name}`]: settingsParam }));\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmFeatures {\n /**\n * Will enable Job module.\n *\n * - Enable session purge functionality for cleaning up expired sessions.\n *\n * @default false\n */\n jobs?: boolean;\n\n /**\n * Enable notification system for password reset, verification emails, etc.\n *\n * @default false\n */\n notifications?: boolean;\n\n /**\n * Enable API key authentication for programmatic access.\n *\n * When enabled, users can create API keys to access protected endpoints\n * without using JWT tokens. API keys are useful for:\n * - Programmatic access (CLI tools, scripts)\n * - Long-lived authentication tokens\n * - Third-party integrations (MCP servers)\n *\n * API keys can be passed via:\n * - Query parameter: `?api_key=ak_xxx`\n * - Bearer header: `Authorization: Bearer ak_xxx`\n *\n * @default false\n */\n apiKeys?: boolean;\n\n /**\n * Enable runtime configuration management.\n *\n * Allows configuring realm settings at runtime with versioning and scheduled activation.\n *\n * @default false\n */\n parameters?: boolean;\n\n /**\n * Enable avatar uploads for user profiles.\n *\n * @default false\n */\n avatars?: boolean;\n\n /**\n * Enable audit trail for compliance and event logging.\n *\n * @default false\n */\n audits?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmOptions {\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 * Issuer configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n issuer?: Partial<IssuerPrimitiveOptions>;\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 apple?: true;\n facebook?: true;\n microsoft?: true;\n franceconnect?: true;\n };\n\n /**\n * Enable or disable realm features.\n *\n * Features control which modules are loaded with the realm.\n */\n features?: Partial<RealmFeatures>;\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: 8,\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 { UserAudits } from \"./audits/UserAudits.ts\";\nimport { UserBuckets } from \"./buckets/UserBuckets.ts\";\nimport { AdminIdentityController } from \"./controllers/AdminIdentityController.ts\";\nimport { AdminSessionController } from \"./controllers/AdminSessionController.ts\";\nimport { AdminUserController } from \"./controllers/AdminUserController.ts\";\nimport { RealmController } from \"./controllers/RealmController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { UserJobs } from \"./jobs/UserJobs.ts\";\nimport { UserNotifications } from \"./notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"./providers/RealmProvider.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 \"./audits/UserAudits.ts\";\nexport * from \"./buckets/UserBuckets.ts\";\nexport * from \"./controllers/AdminIdentityController.ts\";\nexport * from \"./controllers/AdminSessionController.ts\";\nexport * from \"./controllers/AdminUserController.ts\";\nexport * from \"./controllers/RealmController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./jobs/UserJobs.ts\";\nexport * from \"./notifications/UserNotifications.ts\";\nexport * from \"./primitives/$realm.ts\";\nexport * from \"./providers/RealmProvider.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/realmConfigSchema.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/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 * Complete user management with multi-realm support for multi-tenant applications.\n *\n * **Features:**\n * - User registration, login, and profile management\n * - Password reset workflows\n * - Email verification\n * - Session management with multiple devices\n * - Identity management (social logins, SSO)\n * - Multi-realm support for tenant isolation\n * - Credential management\n * - Entities: `users`, `identities`, `sessions`\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n RealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n AdminUserController,\n AdminSessionController,\n AdminIdentityController,\n RealmController,\n ],\n variants: [UserJobs, UserNotifications, UserAudits, UserBuckets],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["INTENT_TTL_MINUTES"],"sources":["../../../src/api/users/audits/UserAudits.ts","../../../src/api/users/buckets/UserBuckets.ts","../../../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/RealmProvider.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/realmConfigSchema.ts","../../../src/api/users/controllers/RealmController.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/jobs/UserJobs.ts","../../../src/api/users/services/SessionService.ts","../../../src/api/users/primitives/$realm.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 { $inject } from \"alepha\";\nimport { AuditService, type CreateAudit } from \"alepha/api/audits\";\n\ntype AuditContext = Omit<CreateAudit, \"type\" | \"action\">;\n\n/**\n * User-specific audit wrapper service.\n *\n * This service wraps the core AuditService to provide user-related audit logging.\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserAudits)`.\n */\nexport class UserAudits {\n protected readonly auditService = $inject(AuditService);\n\n /**\n * Record a user-related audit event.\n */\n public recordUser(\n action:\n | \"create\"\n | \"update\"\n | \"delete\"\n | \"role_change\"\n | \"enable\"\n | \"disable\",\n context: AuditContext,\n ) {\n return this.auditService.recordUser(action, context);\n }\n\n /**\n * Record an authentication-related audit event.\n */\n public recordAuth(\n action: \"login\" | \"logout\" | \"login_failed\" | \"token_refresh\",\n context: AuditContext,\n ) {\n return this.auditService.recordAuth(action, context);\n }\n\n /**\n * Record a generic audit event.\n */\n public record(category: string, action: string, context: AuditContext) {\n return this.auditService.record(category, action, context);\n }\n}\n","import { $bucket } from \"alepha/bucket\";\n\n/**\n * User-specific file storage wrapper service.\n *\n * This service provides file storage for user-related files such as:\n * - User avatars/profile pictures\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserBuckets)`.\n */\nexport class UserBuckets {\n /**\n * Bucket for user avatar storage.\n */\n public readonly avatars = $bucket({\n maxSize: 5 * 1024 * 1024, // 5 MB\n mimeTypes: [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"],\n });\n}\n","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, sql } 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: 30,\n // pattern is handled at the realm settings level\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 organizationId: db.organization(),\n }),\n indexes: [\n {\n expressions: (self) => [self.realm, sql`LOWER(${self.username})`],\n unique: true,\n name: \"users_realm_username_lower_idx\",\n },\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 indexes: [\n \"userId\",\n \"provider\",\n { columns: [\"userId\", \"provider\"] },\n { columns: [\"provider\", \"providerUserId\"], unique: true },\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\n/**\n * Tri-state field requirement for realm auth settings.\n *\n * - `\"none\"`: Field is disabled and not shown.\n * - `\"optional\"`: Field is shown but not required.\n * - `\"required\"`: Field is shown and required.\n */\nexport type FieldRequirement = \"none\" | \"optional\" | \"required\";\n\nconst fieldRequirement = (description: string) =>\n t.union([t.const(\"none\"), t.const(\"optional\"), t.const(\"required\")], {\n description,\n });\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 email: fieldRequirement(\n \"Email address field requirement for user accounts\",\n ),\n username: fieldRequirement(\"Username field requirement for user accounts\"),\n usernameRegExp: t.string({\n description:\n \"Regular expression that usernames must match (if username is enabled)\",\n }),\n phoneNumber: fieldRequirement(\n \"Phone number field requirement 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 firstNameLastName: fieldRequirement(\n \"First and last name field requirement for user accounts\",\n ),\n resetPasswordAllowed: t.boolean({\n description: \"Enable forgot password functionality\",\n }),\n captchaRequired: t.boolean({\n description:\n \"Require captcha verification on registration (needs a CaptchaProvider registered, e.g. TurnstileCaptchaProvider)\",\n }),\n adminEmails: t.array(t.email(), {\n description:\n \"List of email addresses that are automatically promoted to admin role on login\",\n }),\n adminUsernames: t.array(t.text(), {\n description:\n \"List of usernames that are automatically promoted to admin role on login\",\n }),\n defaultRoles: t.array(t.string(), {\n description: \"Default roles assigned to newly registered users\",\n }),\n verifyEmailUrl: t.optional(\n t.string({\n description:\n \"Base URL for email verification links (used when verification method is 'link'). Token and email are appended as query params.\",\n }),\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 loginRateLimit: t.object({\n ipMaxAttempts: t.integer({\n description:\n \"Max failed login attempts per IP before temporary lockout\",\n default: 15,\n minimum: 1,\n }),\n accountMaxAttempts: t.integer({\n description:\n \"Max failed login attempts per account before temporary lockout\",\n default: 5,\n minimum: 1,\n }),\n windowMs: t.integer({\n description: \"Rate limit window duration in milliseconds\",\n default: 15 * 60 * 1000,\n minimum: 1000,\n }),\n }),\n refreshToken: t.object({\n expirationIdle: t.optional(\n t.integer({\n description:\n \"Maximum time in milliseconds a refresh token may stay unused before being invalidated. \" +\n \"When set, sessions whose last refresh is older than this window are rejected and deleted, \" +\n \"even if the absolute `expiresAt` has not been reached. Recommended for SaaS auth posture \" +\n \"(SOC2/ISO27001). Leave undefined to disable idle invalidation (default).\",\n minimum: 1000,\n }),\n ),\n }),\n }),\n default: {\n // for a fresh hello world setup, we accept registration and email login\n registrationAllowed: true,\n email: \"required\" as FieldRequirement,\n username: \"none\" as FieldRequirement,\n usernameRegExp: \"^[a-zA-Z0-9_]{3,30}$\",\n phoneNumber: \"none\" as FieldRequirement,\n verifyEmailRequired: false,\n verifyPhoneRequired: false,\n resetPasswordAllowed: false,\n captchaRequired: false,\n firstNameLastName: \"none\" as FieldRequirement,\n adminEmails: [],\n adminUsernames: [],\n defaultRoles: [\"user\"],\n passwordPolicy: {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSpecialCharacters: false,\n },\n loginRateLimit: {\n ipMaxAttempts: 15,\n accountMaxAttempts: 5,\n windowMs: 15 * 60 * 1000,\n },\n refreshToken: {\n // expirationIdle: undefined — opt-in\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 /**\n * Last time the session was used to refresh an access token.\n * Used by realm `refreshToken.expirationIdle` to invalidate idle sessions.\n * `null` on existing rows pre-migration — falls back to `createdAt`.\n */\n lastUsedAt: t.optional(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 indexes: [\"userId\", \"expiresAt\", { column: \"refreshToken\", unique: true }],\n});\n\nexport type SessionEntity = Static<typeof sessions.schema>;\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport type { ParameterPrimitive } from \"alepha/api/parameters\";\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 { RealmFeatures, RealmOptions } from \"../primitives/$realm.ts\";\n\nexport interface RealmRepositories {\n identities: Repository<typeof identities.schema>;\n sessions: Repository<typeof sessions.schema>;\n users: Repository<typeof users.schema>;\n}\n\nexport interface Realm {\n name: string;\n repositories: RealmRepositories;\n settings: RealmAuthSettings;\n features: RealmFeatures;\n settingsParameter?: ParameterPrimitive<typeof realmAuthSettingsAtom.schema>;\n getSettings(): Promise<RealmAuthSettings>;\n}\n\nexport class RealmProvider {\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, Realm>();\n\n public register(realmName: string, realmOptions: RealmOptions = {}) {\n if (realmName.includes(\".\")) {\n throw new AlephaError(\n `Realm name \"${realmName}\" must not contain dots — dots are reserved for parameter tree paths`,\n );\n }\n\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n avatars: false,\n audits: false,\n ...realmOptions.features,\n };\n\n const realm: Realm = {\n name: realmName,\n repositories: {\n identities: realmOptions.entities?.identities ?? this.defaultIdentities,\n sessions: realmOptions.entities?.sessions ?? this.defaultSessions,\n users: realmOptions.entities?.users ?? this.defaultUsers,\n },\n // TODO: Remove deep merge when alepha supports it natively\n settings: {\n ...realmAuthSettingsAtom.options.default,\n ...realmOptions.settings,\n passwordPolicy: {\n ...realmAuthSettingsAtom.options.default.passwordPolicy,\n ...realmOptions.settings?.passwordPolicy,\n },\n loginRateLimit: {\n ...realmAuthSettingsAtom.options.default.loginRateLimit,\n ...realmOptions.settings?.loginRateLimit,\n },\n refreshToken: {\n ...realmAuthSettingsAtom.options.default.refreshToken,\n ...realmOptions.settings?.refreshToken,\n },\n },\n features,\n getSettings: async function () {\n if (this.settingsParameter) {\n return await this.settingsParameter.get();\n }\n return this.settings;\n },\n };\n this.realms.set(realmName, realm);\n return this.getRealm(realmName);\n }\n\n /**\n * Gets a registered realm by name, auto-creating default if needed.\n */\n public getRealm(realmName = DEFAULT_USER_REALM_NAME): Realm {\n let realm = this.realms.get(realmName);\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 (realmName === DEFAULT_USER_REALM_NAME && firstRealm) {\n realm = firstRealm;\n } else if (this.alepha.isTest()) {\n realm = this.register(realmName); // Auto-create default realm in tests\n } else {\n throw new AlephaError(\n `Missing realm '${realmName}', please declare $realm in your application.`,\n );\n }\n }\n\n return realm;\n }\n\n public identityRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof identities.schema> {\n return this.getRealm(realmName).repositories.identities;\n }\n\n public sessionRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof sessions.schema> {\n return this.getRealm(realmName).repositories.sessions;\n }\n\n public userRepository(\n realmName = DEFAULT_USER_REALM_NAME,\n ): Repository<typeof users.schema> {\n return this.getRealm(realmName).repositories.users;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { IdentityEntity } from \"../entities/identities.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { IdentityQuery } from \"../schemas/identityQuerySchema.ts\";\n\nexport class IdentityService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.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).getById(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.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:identity:read\"] })],\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 use: [$secure({ permissions: [\"admin:identity:read\"] })],\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 use: [$secure({ permissions: [\"admin:identity:delete\"] })],\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 { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { SessionQuery } from \"../schemas/sessionQuerySchema.ts\";\n\nexport class SessionCrudService {\n protected readonly log = $logger();\n protected readonly realmProvider = $inject(RealmProvider);\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.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).getById(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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:session:read\"] })],\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 use: [$secure({ permissions: [\"admin:session:read\"] })],\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 use: [$secure({ permissions: [\"admin:session:delete\"] })],\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});\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 accountLockout = $notification({\n category: \"security\",\n description:\n \"Email sent to users when their account is temporarily locked due to too many failed login attempts.\",\n critical: true,\n sensitive: true,\n email: {\n subject: \"Account temporarily locked\",\n body: (it) => `\n\t\t\t\t<h1>Account Temporarily Locked</h1>\n\t\t\t\t<p>Hi ${it.email},</p>\n\t\t\t\t<p>Your account has been temporarily locked due to too many failed login attempts.</p>\n\t\t\t\t<p>If this was you, please wait ${it.lockoutMinutes} minutes before trying again. If you've forgotten your password, you can reset it using the password reset feature.</p>\n\t\t\t\t<p>If this wasn't you, someone may be trying to access your account. We recommend changing your password as soon as possible.</p>\n\t\t\t\t<p>Best regards,<br>The Team</p>\n\t\t\t`,\n },\n schema: t.object({\n email: t.string({ format: \"email\" }),\n lockoutMinutes: 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, Alepha } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $logger } from \"alepha/logger\";\nimport type { Page } from \"alepha/orm\";\nimport { BadRequestError } from \"alepha/server\";\nimport { $client } from \"alepha/server/links\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly verificationController = $client<VerificationController>();\n protected readonly realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.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 ): Promise<boolean> {\n this.log.trace(\"Requesting email verification\", {\n email,\n userRealmName,\n method,\n });\n\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\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 from realm settings (server-controlled, not user input)\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n const baseUrl = realmSettings.verifyEmailUrl ?? \"/verify-email\";\n const url = new URL(baseUrl, \"http://localhost\");\n url.searchParams.set(\"email\", email);\n url.searchParams.set(\"token\", verification.token);\n const fullVerifyUrl = realmSettings.verifyEmailUrl\n ? `${baseUrl}${url.search}`\n : url.pathname + url.search;\n\n await this.userNotifications(userRealmName)?.emailVerificationLink.push(\n {\n contact: email,\n variables: {\n email,\n verifyUrl: fullVerifyUrl,\n expiresInMinutes: Math.floor(verification.codeExpiration / 60),\n },\n },\n );\n\n this.log.debug(\"Email verification link sent\", {\n email,\n userId: user.id,\n });\n } else {\n await this.userNotifications(userRealmName)?.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).getOne({\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.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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).findOne({\n where: { email: { eq: email } },\n });\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 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).getById(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.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n\n // Check for existing user based on provided unique fields (scoped to realm)\n if (data.username) {\n const existingUser = await this.users(userRealmName).findOne({\n where: { realm: realm.name, username: { ilike: data.username } },\n });\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).findOne({\n where: { realm: realm.name, email: { eq: data.email } },\n });\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).findOne({\n where: { realm: realm.name, phoneNumber: { eq: data.phoneNumber } },\n });\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 ?? realmSettings.defaultRoles,\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.userAudits(userRealmName)?.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.realmProvider.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.userAudits(userRealmName)?.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 // Clean up related sessions and identities before deleting the user\n await this.realmProvider\n .sessionRepository(userRealmName)\n .deleteMany({ userId: { eq: id } });\n await this.realmProvider\n .identityRepository(userRealmName)\n .deleteMany({ userId: { eq: id } });\n\n await this.users(userRealmName).deleteById(id);\n this.log.info(\"User deleted\", { userId: id });\n\n const realm = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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 { $secure } from \"alepha/security\";\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 use: [$secure({ permissions: [\"admin:user:read\"] })],\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 use: [$secure({ permissions: [\"admin:user:read\"] })],\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 use: [$secure({ permissions: [\"admin:user:create\"] })],\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 use: [$secure({ permissions: [\"admin:user:update\"] })],\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 use: [$secure({ permissions: [\"admin:user:delete\"] })],\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, t } from \"alepha\";\nimport { authenticationProviderSchema } from \"alepha/server/auth\";\nimport { realmAuthSettingsAtom } from \"../atoms/realmAuthSettingsAtom.ts\";\n\nexport const realmConfigSchema = t.object({\n settings: realmAuthSettingsAtom.schema,\n realmName: t.string(),\n authenticationMethods: t.array(authenticationProviderSchema),\n captchaSiteKey: t.optional(\n t.string({\n description:\n \"Public site key for the captcha widget (when settings.captchaRequired is true)\",\n }),\n ),\n});\n\nexport type RealmConfig = Static<typeof realmConfigSchema>;\n","import { $inject, t } from \"alepha\";\nimport { CaptchaProvider } from \"alepha/captcha\";\nimport { $action } from \"alepha/server\";\nimport { ServerAuthProvider } from \"alepha/server/auth\";\nimport { $etag } from \"alepha/server/etag\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { realmConfigSchema } from \"../schemas/realmConfigSchema.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 RealmController {\n protected readonly url = \"/realms\";\n protected readonly group = \"realms\";\n protected readonly realmProvider = $inject(RealmProvider);\n protected readonly serverAuthProvider = $inject(ServerAuthProvider);\n protected readonly captchaProvider = $inject(CaptchaProvider);\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 use: [$etag()],\n schema: {\n query: t.object({\n realmName: t.optional(t.string()),\n }),\n response: realmConfigSchema,\n },\n handler: async ({ query }) => {\n const realm = this.realmProvider.getRealm(query.realmName);\n const settings = await realm.getSettings();\n const realmName = realm.name;\n\n const authenticationMethods =\n this.serverAuthProvider.getAuthenticationProviders({\n realmName,\n });\n\n return {\n settings,\n realmName,\n authenticationMethods,\n captchaSiteKey: settings.captchaRequired\n ? this.captchaProvider.getSiteKey()\n : undefined,\n };\n },\n });\n\n public readonly checkUsernameAvailability = $action({\n group: this.group,\n path: `${this.url}/check-username`,\n schema: {\n query: t.object({\n realmName: 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.realmName;\n const userRepository = this.realmProvider.userRepository(realmName);\n\n const existingUser = await userRepository.findOne({\n where: { username: { eq: body.username } },\n });\n\n return {\n available: !existingUser,\n };\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 // Captcha token — required when the realm has `captchaRequired: true`.\n // Validated at intent creation (before any verification email is sent).\n captchaToken: t.optional(\n t.string({\n description: \"Captcha response token (if captcha is required)\",\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 captchaSiteKey: t.optional(\n t.string({\n description:\n \"Public site key the client should render (when expectCaptcha is true)\",\n }),\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, Alepha } 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 type { RealmAuthSettings } from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 alepha = $inject(Alepha);\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 realmProvider = $inject(RealmProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n protected readonly intentCache = $cache<PasswordResetIntent>({\n name: \"api:users:password-reset-intents\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Validate a password against the realm's password policy.\n */\n public validatePasswordPolicy(\n password: string,\n policy: RealmAuthSettings[\"passwordPolicy\"],\n ): void {\n if (password.length < policy.minLength) {\n throw new BadRequestError(\n `Password must be at least ${policy.minLength} characters`,\n );\n }\n if (policy.requireUppercase && !/[A-Z]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one uppercase letter\",\n );\n }\n if (policy.requireLowercase && !/[a-z]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one lowercase letter\",\n );\n }\n if (policy.requireNumbers && !/\\d/.test(password)) {\n throw new BadRequestError(\"Password must contain at least one number\");\n }\n if (policy.requireSpecialCharacters && !/[^a-zA-Z0-9]/.test(password)) {\n throw new BadRequestError(\n \"Password must contain at least one special character\",\n );\n }\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 // Check if password reset is allowed for this realm\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n if (realmSettings.resetPasswordAllowed === false) {\n this.log.debug(\"Password reset not allowed for realm\", { userRealmName });\n return { intentId, expiresAt };\n }\n\n // Find user by email (silent fail for security)\n const user = await this.users(userRealmName).findOne({\n where: { email: { eq: email } },\n });\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).findOne({\n where: {\n userId: { eq: user.id },\n provider: { eq: \"credentials\" },\n },\n });\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(userRealmName)?.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 // Validate password against realm policy before consuming the verification code\n const realm = this.realmProvider.getRealm(intent.realmName);\n const realmSettings = await realm.getSettings();\n this.validatePasswordPolicy(body.newPassword, realmSettings.passwordPolicy);\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 // 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 // Invalidate intent after all operations succeed,\n // so the user can retry if password update or session cleanup fails.\n // The verification code was already consumed (verifiedAt set), so replay is\n // prevented even if the intent is still in cache.\n await this.intentCache.invalidate(body.intentId);\n\n this.log.info(\"Password reset completed\", {\n userId: intent.userId,\n email: intent.email,\n });\n\n // Audit: password reset\n await this.userAudits(intent.realmName)?.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.userAudits(intent.realmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\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\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 // Validate password against realm policy\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n this.validatePasswordPolicy(newPassword, realmSettings.passwordPolicy);\n\n // Find user and identity\n const user = await this.users(userRealmName).getOne({\n where: { email: { eq: email } },\n });\n\n const identity = await this.identities(userRealmName).getOne({\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 // Audit: password reset (legacy method)\n await this.userAudits(userRealmName)?.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.userAudits(userRealmName)?.record(\n \"security\",\n \"sessions_invalidated\",\n {\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}\n","import { randomUUID } from \"node:crypto\";\nimport { $inject, Alepha, AlephaError } from \"alepha\";\nimport type { VerificationController } from \"alepha/api/verifications\";\nimport { $cache } from \"alepha/cache\";\nimport { CaptchaProvider } from \"alepha/captcha\";\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 { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport type { CompleteRegistrationRequest } from \"../schemas/completeRegistrationRequestSchema.ts\";\nimport type { RegisterRequest } from \"../schemas/registerRequestSchema.ts\";\nimport type { RegistrationIntentResponse } from \"../schemas/registrationIntentResponseSchema.ts\";\nimport { CredentialService } from \"./CredentialService.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 alepha = $inject(Alepha);\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 realmProvider = $inject(RealmProvider);\n protected readonly credentialService = $inject(CredentialService);\n protected readonly captchaProvider = $inject(CaptchaProvider);\n\n protected readonly intentCache = $cache<RegistrationIntent>({\n name: \"api:users:registrations\",\n ttl: [INTENT_TTL_MINUTES, \"minutes\"],\n });\n\n protected readonly rateLimitCache = $cache<number>({\n name: \"api:users:registration-rate-limit\",\n ttl: [15, \"minutes\"],\n });\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n\n throw new AlephaError(\n `Notifications feature is not enabled for realm \"${realmName}\". Please enable it in the realm settings to use registration notifications.`,\n );\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 // IP rate limiting\n const request = this.alepha.store.get(\"alepha.http.request\");\n const ipKey = request?.ip ? `register:ip:${request.ip}` : undefined;\n if (ipKey) {\n const count = (await this.rateLimitCache.get(ipKey)) ?? 0;\n if (count >= 10) {\n this.log.warn(\"Registration rate limit exceeded\", { ip: request?.ip });\n throw new BadRequestError(\n \"Too many registration attempts, please try again later\",\n );\n }\n await this.rateLimitCache.set(ipKey, count + 1);\n }\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\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?.username === \"required\" && !body.username) {\n this.log.debug(\"Registration rejected: username required\", {\n userRealmName,\n });\n throw new BadRequestError(\"Username is required\");\n }\n\n if (body.username) {\n // Security note: usernameRegExp is admin-controlled (from realmAuthSettingsAtom),\n // not user input. Default is ^[a-zA-Z0-9_]{3,30}$ which is ReDoS-safe.\n // No need for regex timeout or safe-regex validation here.\n const usernameRegExp = realmSettings?.usernameRegExp;\n if (usernameRegExp) {\n const regex = new RegExp(usernameRegExp);\n if (!regex.test(body.username)) {\n this.log.debug(\"Registration rejected: username regex mismatch\", {\n userRealmName,\n username: body.username,\n });\n throw new BadRequestError(\n \"Username does not meet the required format\",\n );\n }\n }\n }\n\n if (realmSettings?.email === \"required\" && !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?.phoneNumber === \"required\" && !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 // Validate password against realm policy\n this.credentialService.validatePasswordPolicy(\n body.password,\n realmSettings.passwordPolicy,\n );\n\n // Validate captcha BEFORE any side effects (email sends, rate-limit counters).\n // This blocks bots from triggering verification emails on other people's addresses.\n if (realmSettings?.captchaRequired === true) {\n if (!body.captchaToken) {\n throw new BadRequestError(\"Captcha verification is required\");\n }\n const valid = await this.captchaProvider.verify(body.captchaToken);\n if (!valid) {\n throw new BadRequestError(\"Captcha verification failed\");\n }\n }\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, // validated above, single-use — no gate at complete time\n };\n\n // Create verification sessions and send codes\n if (requirements.email && body.email) {\n await this.sendEmailVerification(body.email, userRealmName);\n }\n\n if (requirements.phone && body.phoneNumber) {\n await this.sendPhoneVerification(body.phoneNumber, userRealmName);\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 captchaSiteKey: requirements.captcha\n ? this.captchaProvider.getSiteKey()\n : undefined,\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.realmProvider.userRepository(userRealmName);\n const identityRepository =\n this.realmProvider.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 // Captcha is validated at intent creation (see createIntent) so bots can't\n // trigger verification emails; nothing to re-check here.\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 const realm = this.realmProvider.getRealm(userRealmName);\n const realmSettings = await realm.getSettings();\n\n // Create the user\n const user = await userRepository.create({\n realm: realm.name,\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: realmSettings.defaultRoles,\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 // Invalidate intent after both user and identity creation succeed,\n // so the user can retry if either operation fails.\n await this.intentCache.invalidate(body.intentId);\n\n this.log.info(\"User registered successfully\", {\n userId: user.id,\n email: user.email,\n username: user.username,\n });\n\n await this.userAudits(userRealmName)?.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 realm = this.realmProvider.getRealm(userRealmName);\n const userRepository = this.realmProvider.userRepository(userRealmName);\n\n if (body.username) {\n const existingUser = await userRepository.findOne({\n where: { realm: realm.name, username: { ilike: body.username } },\n });\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.findOne({\n where: { realm: realm.name, email: { eq: body.email } },\n });\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.findOne({\n where: { realm: realm.name, phoneNumber: { eq: body.phoneNumber } },\n });\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(\n email: string,\n realmName?: string,\n ): Promise<void> {\n this.log.debug(\"Sending email verification code\", { email });\n\n const verification =\n await this.verificationController.requestVerificationCode({\n params: { type: \"code\" },\n body: { target: email },\n });\n\n await this.userNotifications(realmName).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\", { email });\n }\n\n /**\n * Send phone verification code.\n */\n protected async sendPhoneVerification(\n phoneNumber: string,\n realmName?: string,\n ): 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(realmName).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 { $secure } from \"alepha/security\";\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 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 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 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 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. When using \"link\", configure verifyEmailUrl in realm settings.',\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 );\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 use: [$secure()],\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 { $inject } from \"alepha\";\nimport { $job } from \"alepha/api/jobs\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { sessions } from \"../entities/sessions.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\n\n/**\n * User-specific jobs wrapper service.\n *\n * This service handles user-related scheduled jobs such as:\n * - Session purge (cleaning up expired sessions)\n * - Verification code cleanup\n * - Inactive user notifications\n *\n * Declared as a module variant — not auto-injected. It is instantiated\n * lazily the first time something calls `alepha.inject(UserJobs)`.\n */\nexport class UserJobs {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly sessionRepository = $repository(sessions);\n protected readonly realmProvider = $inject(RealmProvider);\n\n /**\n * Purge expired sessions from the database.\n *\n * Runs hourly (at :00) and deletes:\n * - sessions whose absolute `expiresAt` has passed\n * - sessions whose `lastUsedAt` exceeds the realm's `refreshToken.expirationIdle`\n * (when configured). Falls back to `createdAt` for sessions without a\n * recorded `lastUsedAt`.\n *\n * The idle sweep is best-effort cleanup — runtime enforcement happens in\n * `SessionService.refreshSession()`.\n */\n public readonly purgeExpiredSessions = $job({\n name: \"api:users:purgeExpiredSessions\",\n cron: \"0 * * * *\", // Hourly at minute 0\n handler: async () => {\n const now = this.dateTimeProvider.nowISOString();\n\n this.log.info(\"Starting expired sessions purge\", { cutoffTime: now });\n\n const absoluteDeletedIds = await this.sessionRepository.deleteMany({\n expiresAt: { lt: now },\n });\n\n if (absoluteDeletedIds.length > 0) {\n this.log.info(\"Expired sessions purged (absolute)\", {\n deletedCount: absoluteDeletedIds.length,\n });\n }\n\n // Idle sweep — only if the default realm has expirationIdle configured.\n // Multi-realm setups with per-realm session tables should add their own\n // job; this default job sweeps the default sessions table.\n const realm = this.realmProvider.getRealm();\n const settings = await realm.getSettings();\n const idleMs = settings.refreshToken?.expirationIdle;\n if (idleMs && idleMs > 0) {\n const cutoff = this.dateTimeProvider\n .now()\n .subtract(idleMs, \"milliseconds\")\n .toISOString();\n\n // Two passes: rows with an explicit lastUsedAt, and pre-migration rows\n // where lastUsedAt is null — those fall back to createdAt.\n const lastUsedDeletedIds = await this.sessionRepository.deleteMany({\n lastUsedAt: { lt: cutoff },\n });\n const fallbackDeletedIds = await this.sessionRepository.deleteMany({\n lastUsedAt: { isNull: true },\n createdAt: { lt: cutoff },\n });\n\n const idleTotal = lastUsedDeletedIds.length + fallbackDeletedIds.length;\n if (idleTotal > 0) {\n this.log.info(\"Expired sessions purged (idle)\", {\n deletedCount: idleTotal,\n thresholdMs: idleMs,\n });\n }\n }\n },\n });\n}\n","import { randomInt } from \"node:crypto\";\nimport { $inject, Alepha } from \"alepha\";\nimport type { FileController } from \"alepha/api/files\";\nimport { CacheProvider } from \"alepha/cache\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport {\n CryptoProvider,\n InvalidCredentialsError,\n type UserAccount,\n} from \"alepha/security\";\nimport { BadRequestError, UnauthorizedError } from \"alepha/server\";\nimport type { OAuth2Profile } from \"alepha/server/auth\";\nimport { $client } from \"alepha/server/links\";\nimport { FileSystemProvider } from \"alepha/system\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport type { UserEntity } from \"../entities/users.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.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 realmProvider = $inject(RealmProvider);\n protected readonly fileController = $client<FileController>();\n protected readonly cacheProvider = $inject(CacheProvider);\n\n protected userAudits(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.audits) {\n return this.alepha.inject(UserAudits);\n }\n return undefined;\n }\n\n protected userNotifications(realmName?: string) {\n const realm = this.realmProvider.getRealm(realmName);\n if (realm.features.notifications) {\n return this.alepha.inject(UserNotifications);\n }\n return undefined;\n }\n\n public users(userRealmName?: string) {\n return this.realmProvider.userRepository(userRealmName);\n }\n\n public sessions(userRealmName?: string) {\n return this.realmProvider.sessionRepository(userRealmName);\n }\n\n public identities(userRealmName?: string) {\n return this.realmProvider.identityRepository(userRealmName);\n }\n\n /**\n * Check if user should be auto-promoted to admin based on adminEmails/adminUsernames settings.\n * If user matches and doesn't have admin role, promote them.\n */\n protected async ensureAdminRole(\n user: {\n id: string;\n email?: string | null;\n username?: string | null;\n roles: string[];\n },\n userRealmName?: string,\n ): Promise<boolean> {\n if (user.roles.includes(\"admin\")) return false;\n\n const realm = this.realmProvider.getRealm(userRealmName);\n const settings = await realm.getSettings();\n const { name } = realm;\n const adminEmails = settings.adminEmails ?? [];\n const adminUsernames = settings.adminUsernames ?? [];\n\n const isAdminByEmail = user.email && adminEmails.includes(user.email);\n const isAdminByUsername =\n user.username && adminUsernames.includes(user.username);\n\n if (!isAdminByEmail && !isAdminByUsername) return false;\n\n // Promote to admin\n user.roles = [...user.roles.filter((r) => r !== \"admin\"), \"admin\"];\n await this.users(userRealmName).updateById(user.id, { roles: user.roles });\n\n const reason = isAdminByEmail ? \"adminEmails\" : \"adminUsernames\";\n this.log.info(`User auto-promoted to admin via ${reason} setting`, {\n userId: user.id,\n email: user.email,\n username: user.username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordUser(\"role_change\", {\n userId: user.id,\n userEmail: user.email ?? undefined,\n userRealm: name,\n resourceId: user.id,\n description: `User auto-promoted to admin via ${reason} setting`,\n metadata: { addedRole: \"admin\", reason },\n });\n\n return true;\n }\n\n /**\n * Generate a unique username from an OAuth profile.\n *\n * 1. Extract candidate from email prefix\n * 2. Sanitize against realm's usernameRegExp (strip invalid chars, truncate)\n * 3. Check case-insensitive uniqueness, append suffix (2, 3, ...) if taken\n * 4. Fall back to \"user\" + random 6-char alphanumeric if all else fails\n */\n protected async generateUniqueUsername(\n profile: OAuth2Profile,\n realmSettings: any,\n users: any,\n ): Promise<string> {\n const maxLength = 30;\n const maxSuffixAttempts = 10;\n\n // Extract candidate from email or profile name\n let candidate = profile.email?.split(\"@\")[0] ?? profile.name ?? \"\";\n\n // Strip characters not allowed in usernames (keep alphanumeric, underscore, dot, hyphen)\n candidate = candidate.replace(/[^a-zA-Z0-9_.-]/g, \"\");\n\n // If realm has a custom regex, further sanitize\n if (realmSettings?.usernameRegExp) {\n try {\n const regex = new RegExp(realmSettings.usernameRegExp);\n if (!regex.test(candidate)) {\n // Strip to basic alphanumeric as safe fallback\n candidate = candidate.replace(/[^a-zA-Z0-9_]/g, \"\");\n }\n } catch {\n // Invalid regex, continue with sanitized candidate\n }\n }\n\n // Ensure minimum length\n if (candidate.length < 3) {\n candidate = `user${candidate}`;\n }\n\n // Truncate to leave room for suffix\n candidate = candidate.slice(0, maxLength - 2);\n\n // Check uniqueness (case-insensitive exact match)\n const isAvailable = async (name: string) => {\n const existing = await users.findOne({\n where: { username: { ilike: name } },\n });\n return !existing;\n };\n\n if (await isAvailable(candidate)) {\n return candidate;\n }\n\n // Try with numeric suffix\n for (let i = 2; i <= maxSuffixAttempts + 1; i++) {\n const withSuffix = `${candidate}${i}`;\n if (withSuffix.length <= maxLength && (await isAvailable(withSuffix))) {\n return withSuffix;\n }\n }\n\n // Final fallback: random username\n const random = Math.random().toString(36).slice(2, 8);\n return `user${random}`;\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 protected static readonly LOGIN_CACHE_NAME = \"login-rate-limit\";\n\n /**\n * Check if a login key is currently locked out.\n * Read-only — does not increment the counter.\n */\n protected async isLoginLocked(key: string, max: number): Promise<boolean> {\n try {\n const count = await this.cacheProvider.getTyped<number>(\n SessionService.LOGIN_CACHE_NAME,\n key,\n );\n return count != null && count >= max;\n } catch (error) {\n this.log.warn(\n \"Failed to check login rate limit, allowing attempt\",\n error,\n );\n return false;\n }\n }\n\n /**\n * Record a failed login attempt. Uses getTyped + setTyped (not incr) so that\n * each write refreshes the TTL — implementing sliding-window behavior.\n *\n * Returns `true` if this failure just crossed the lockout threshold.\n */\n protected async recordFailedLogin(\n key: string,\n max: number,\n windowMs: number,\n ): Promise<boolean> {\n try {\n const count =\n (await this.cacheProvider.getTyped<number>(\n SessionService.LOGIN_CACHE_NAME,\n key,\n )) ?? 0;\n const newCount = count + 1;\n await this.cacheProvider.setTyped(\n SessionService.LOGIN_CACHE_NAME,\n key,\n newCount,\n { ttl: windowMs },\n );\n return newCount === max;\n } catch (error) {\n this.log.warn(\"Failed to record failed login attempt\", error);\n return false;\n }\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 realm = this.realmProvider.getRealm(userRealmName);\n const settings = await realm.getSettings();\n const { name } = realm;\n const { loginRateLimit } = settings;\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 // IP rate limit check (global, cross-realm) — before any DB work\n const request = this.alepha.store.get(\"alepha.http.request\");\n const ipKey = request?.ip ? `login:ip:${request.ip}` : undefined;\n\n if (ipKey) {\n const ipLocked = await this.isLoginLocked(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n );\n if (ipLocked) {\n this.log.warn(\"Login blocked — IP rate limit exceeded\", {\n ip: request?.ip,\n });\n throw new InvalidCredentialsError();\n }\n }\n\n await this.randomDelay();\n\n try {\n const where = users.createQueryWhere();\n\n where.realm = name;\n\n if (settings.username !== \"none\" && isUsername) {\n // validate username format if regex is provided\n if (settings.usernameRegExp) {\n const regex = new RegExp(settings.usernameRegExp);\n if (!regex.test(username)) {\n this.log.warn(\"Username does not match required format\", {\n provider,\n username,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"Username does not match required format\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n }\n where.username = { ilike: username };\n } else if (settings.email !== \"none\" && isEmail) {\n where.email = username;\n } else if (settings.phoneNumber !== \"none\" && 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.userAudits(userRealmName)?.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 });\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.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n description: \"User not found\",\n metadata: { provider, username },\n });\n\n // Only increment IP counter (no user ID to track)\n if (ipKey) {\n const justLocked = await this.recordFailedLogin(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (justLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n success: false,\n description:\n \"IP temporarily locked due to too many failed login attempts\",\n metadata: { ip: request?.ip },\n },\n );\n }\n }\n\n throw new InvalidCredentialsError();\n }\n\n // Check if user account is enabled\n if (!user.enabled) {\n this.log.warn(\"Login attempt for disabled account\", {\n userId: user.id,\n realm: name,\n });\n\n await this.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Login attempt for disabled account\",\n metadata: { provider, username },\n });\n\n throw new InvalidCredentialsError();\n }\n\n // Account rate limit check (per-realm)\n const accountKey = `login:account:${name}:${user.id}`;\n const accountLocked = await this.isLoginLocked(\n accountKey,\n loginRateLimit.accountMaxAttempts,\n );\n if (accountLocked) {\n this.log.warn(\"Login blocked — account rate limit exceeded\", {\n userId: user.id,\n realm: name,\n });\n throw new InvalidCredentialsError();\n }\n\n const identity = await identities.getOne({\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.userAudits(userRealmName)?.recordAuth(\"login_failed\", {\n userRealm: name,\n resourceId: user.id,\n description: \"Invalid password\",\n metadata: { provider, username },\n });\n\n // Record failed attempt on both IP and account counters\n if (ipKey) {\n const ipJustLocked = await this.recordFailedLogin(\n ipKey,\n loginRateLimit.ipMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (ipJustLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n success: false,\n description:\n \"IP temporarily locked due to too many failed login attempts\",\n metadata: { ip: request?.ip },\n },\n );\n }\n }\n\n const accountJustLocked = await this.recordFailedLogin(\n accountKey,\n loginRateLimit.accountMaxAttempts,\n loginRateLimit.windowMs,\n );\n if (accountJustLocked) {\n await this.userAudits(userRealmName)?.record(\n \"security\",\n \"rate_limited\",\n {\n userRealm: name,\n resourceId: user.id,\n success: false,\n description:\n \"Account temporarily locked due to too many failed login attempts\",\n metadata: { userId: user.id },\n },\n );\n\n // Notify user about account lockout\n if (user.email) {\n const lockoutMinutes = Math.round(loginRateLimit.windowMs / 60_000);\n await this.userNotifications(userRealmName)?.accountLockout.push({\n contact: user.email,\n variables: { email: user.email, lockoutMinutes },\n });\n }\n }\n\n throw new InvalidCredentialsError();\n }\n\n await this.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n } catch (error) {\n if (error instanceof InvalidCredentialsError) {\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.store.get(\"alepha.http.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 lastUsedAt: this.dateTimeProvider.nowISOString(),\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 // getOne() throws DbEntityNotFoundError if not found — never returns null.\n // No null check needed here.\n const session = await this.sessions(userRealmName).getOne({\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(session.id);\n throw new UnauthorizedError(\"Session expired\");\n }\n\n // Idle timeout check — opt-in via realm settings.\n // Falls back to createdAt when lastUsedAt is null (pre-migration rows or\n // sessions that never refreshed since the column was introduced).\n const realm = this.realmProvider.getRealm(userRealmName);\n const settings = await realm.getSettings();\n const idleMs = settings.refreshToken?.expirationIdle;\n if (idleMs && idleMs > 0) {\n const lastUsedRef = session.lastUsedAt ?? session.createdAt;\n const idleSince = now.diff(this.dateTimeProvider.of(lastUsedRef));\n if (idleSince > idleMs) {\n this.log.info(\"Session expired (idle timeout)\", {\n sessionId: session.id,\n userId: session.userId,\n idleMs: idleSince,\n thresholdMs: idleMs,\n });\n await this.sessions(userRealmName).deleteById(session.id);\n throw new UnauthorizedError(\"Session expired\");\n }\n }\n\n const user = await this.users(userRealmName).getOne({\n where: {\n id: { eq: session.userId },\n },\n });\n\n // Check if user account is still enabled\n if (!user.enabled) {\n this.log.warn(\"Session refresh for disabled account\", {\n userId: user.id,\n sessionId: session.id,\n });\n await this.sessions(userRealmName).deleteById(session.id);\n throw new UnauthorizedError(\"Account disabled\");\n }\n\n // Auto-promote to admin if configured (handles \"I promote you admin\" case)\n await this.ensureAdminRole(user, userRealmName);\n\n // Update lastUsedAt — sliding-window for idle timeout enforcement.\n await this.sessions(userRealmName).updateById(session.id, {\n lastUsedAt: now.toISOString(),\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\n // Get session info before deletion for audit\n const session = await this.sessions(userRealmName).findOne({\n where: { refreshToken: { eq: refreshToken } },\n });\n\n await this.sessions(userRealmName).deleteOne({\n refreshToken,\n });\n this.log.debug(\"Session deleted\");\n\n if (session) {\n const { name } = this.realmProvider.getRealm(userRealmName);\n\n await this.userAudits(userRealmName)?.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.realmProvider.getRealm(userRealmName);\n const identities = this.identities(userRealmName);\n const users = this.users(userRealmName);\n\n const identity = await identities.findOne({\n where: {\n provider,\n providerUserId: profile.sub,\n },\n });\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.getById(identity.userId);\n\n await this.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\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.findOne({\n where: {\n realm: realm.name,\n email: profile.email,\n },\n });\n\n if (existing) {\n // Refuse auto-link if the OAuth provider explicitly says email is not verified\n if (profile.email_verified === false) {\n this.log.warn(\n \"OAuth2 profile email not verified by provider, refusing auto-link\",\n { provider, email: profile.email, userId: existing.id },\n );\n throw new BadRequestError(\n \"Cannot link account: email not verified by provider\",\n );\n }\n\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.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(existing, userRealmName);\n\n return existing;\n }\n\n const realmSettings = await realm.getSettings();\n const adminEmails = realmSettings?.adminEmails ?? [];\n const isAdmin = profile.email && adminEmails.includes(profile.email);\n\n if (realmSettings?.registrationAllowed === false && !isAdmin) {\n this.log.warn(\"Registration not allowed for realm via OAuth2\", {\n provider,\n userRealmName,\n });\n throw new BadRequestError(\"Account doesn't exist\");\n }\n\n const username = await this.generateUniqueUsername(\n profile,\n realmSettings,\n users,\n );\n\n const user = await users.create({\n realm: realm.name,\n username,\n email: profile.email,\n firstName: profile.given_name,\n lastName: profile.family_name,\n // we trust the OAuth2 provider\n emailVerified: true,\n roles: realmSettings.defaultRoles,\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.userAudits(userRealmName)?.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.userAudits(userRealmName)?.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 // Auto-promote to admin if configured\n await this.ensureAdminRole(user, userRealmName);\n\n return user;\n }\n}\n","import { $context } from \"alepha\";\nimport { AlephaApiKeys, ApiKeyService } from \"alepha/api/keys\";\nimport { $parameter, AlephaApiParameters } from \"alepha/api/parameters\";\nimport { AlephaApiVerification } from \"alepha/api/verifications\";\nimport type { Repository } from \"alepha/orm\";\nimport {\n $issuer,\n $permission,\n type IssuerPrimitive,\n type IssuerPrimitiveOptions,\n type IssuerResolver,\n SecurityProvider,\n} from \"alepha/security\";\nimport {\n $authApple,\n $authCredentials,\n $authFacebook,\n $authFranceConnect,\n $authGithub,\n $authGoogle,\n $authMicrosoft,\n type AuthPrimitive,\n type Credentials,\n type LinkAccountOptions,\n type WithLinkFn,\n type WithLoginFn,\n} from \"alepha/server/auth\";\nimport {\n type RealmAuthSettings,\n realmAuthSettingsAtom,\n} from \"../atoms/realmAuthSettingsAtom.ts\";\nimport { UserAudits } from \"../audits/UserAudits.ts\";\nimport { UserBuckets } from \"../buckets/UserBuckets.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 { UserJobs } from \"../jobs/UserJobs.ts\";\nimport { UserNotifications } from \"../notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"../providers/RealmProvider.ts\";\nimport { SessionService } from \"../services/SessionService.ts\";\n\nexport type RealmPrimitive = IssuerPrimitive & 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 $realm = (options: RealmOptions = {}): RealmPrimitive => {\n const { alepha } = $context();\n const sessionService = alepha.inject(SessionService);\n const securityProvider = alepha.inject(SecurityProvider);\n const realmProvider = alepha.inject(RealmProvider);\n\n const name = options.issuer?.name ?? DEFAULT_USER_REALM_NAME;\n\n options.settings ??= {};\n\n // Merge features with defaults\n const features: RealmFeatures = {\n jobs: false,\n notifications: false,\n apiKeys: false,\n parameters: false,\n avatars: false,\n audits: false,\n ...options.features,\n };\n\n // When notifications are disabled, force verification-dependent settings to false\n // These features require sending codes via email/SMS which won't work without notifications\n if (!features.notifications) {\n options.settings.verifyEmailRequired = false;\n options.settings.verifyPhoneRequired = false;\n options.settings.resetPasswordAllowed = false;\n }\n\n const realmRegistration = realmProvider.register(name, options);\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Enable features based on configuration\n // Each feature registers its wrapper service which internally uses the module primitives\n\n if (features.avatars) {\n alepha.with(UserBuckets);\n }\n\n if (features.audits) {\n alepha.with(UserAudits);\n }\n\n if (features.jobs) {\n alepha.with(UserJobs);\n }\n\n if (features.notifications) {\n alepha.with(UserNotifications);\n alepha.with(AlephaApiVerification);\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n // Collect custom resolvers that will be registered during $issuer.onInit()\n // This ensures they are registered AFTER the realm is created (not on the default test realm)\n const customResolvers: IssuerResolver[] = [\n ...(options.issuer?.resolvers ?? []),\n ];\n\n // Enable API key authentication - must be added to customResolvers before $issuer() call\n if (features.apiKeys) {\n alepha.with(AlephaApiKeys);\n const apiKeyService = alepha.inject(ApiKeyService);\n customResolvers.push(apiKeyService.createResolver());\n }\n\n const realm: RealmPrimitive = $issuer({\n ...options.issuer,\n name,\n secret: options.secret ?? securityProvider.secretKey,\n resolvers: customResolvers,\n roles: options.issuer?.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.issuer?.settings,\n },\n });\n\n $permission({\n name: \"admin:access\",\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 async (credentials: Credentials) => {\n const user = await sessionService.login(\n name,\n credentials.username,\n credentials.password,\n realm.name,\n );\n // Compose display name from first+last for OIDC `name` claim.\n // Without this, credentials-registered users appear as \"Anonymous User\".\n const composedName =\n [user.firstName, user.lastName]\n .filter((s): s is string => !!s?.trim())\n .join(\" \")\n .trim() || undefined;\n return { ...user, name: composedName };\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 realmRegistration.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 if (identities.apple) {\n auth.apple = $authApple(realm);\n }\n\n if (identities.facebook) {\n auth.facebook = $authFacebook(realm);\n }\n\n if (identities.microsoft) {\n auth.microsoft = $authMicrosoft(realm);\n }\n\n if (identities.franceconnect) {\n auth.franceconnect = $authFranceConnect(realm);\n }\n\n alepha.with(() => auth);\n }\n\n if (features.parameters) {\n alepha.with(AlephaApiParameters);\n const settingsParam = $parameter({\n name: `api.realms.settings.${name}`,\n description: `Authentication and registration settings for realm \"${name}\"`,\n schema: realmAuthSettingsAtom.schema,\n default: realmRegistration.settings,\n });\n realmRegistration.settingsParameter = settingsParam;\n alepha.with(() => ({ [`realmSettings_${name}`]: settingsParam }));\n }\n\n return realm;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmFeatures {\n /**\n * Will enable Job module.\n *\n * - Enable session purge functionality for cleaning up expired sessions.\n *\n * @default false\n */\n jobs?: boolean;\n\n /**\n * Enable notification system for password reset, verification emails, etc.\n *\n * @default false\n */\n notifications?: boolean;\n\n /**\n * Enable API key authentication for programmatic access.\n *\n * When enabled, users can create API keys to access protected endpoints\n * without using JWT tokens. API keys are useful for:\n * - Programmatic access (CLI tools, scripts)\n * - Long-lived authentication tokens\n * - Third-party integrations (MCP servers)\n *\n * API keys can be passed via:\n * - Query parameter: `?api_key=ak_xxx`\n * - Bearer header: `Authorization: Bearer ak_xxx`\n *\n * @default false\n */\n apiKeys?: boolean;\n\n /**\n * Enable runtime configuration management.\n *\n * Allows configuring realm settings at runtime with versioning and scheduled activation.\n *\n * @default false\n */\n parameters?: boolean;\n\n /**\n * Enable avatar uploads for user profiles.\n *\n * @default false\n */\n avatars?: boolean;\n\n /**\n * Enable audit trail for compliance and event logging.\n *\n * @default false\n */\n audits?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RealmOptions {\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 * Issuer configuration options.\n *\n * It's already pre-configured for user management with admin and user roles.\n */\n issuer?: Partial<IssuerPrimitiveOptions>;\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 apple?: true;\n facebook?: true;\n microsoft?: true;\n franceconnect?: true;\n };\n\n /**\n * Enable or disable realm features.\n *\n * Features control which modules are loaded with the realm.\n */\n features?: Partial<RealmFeatures>;\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: 8,\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 { UserAudits } from \"./audits/UserAudits.ts\";\nimport { UserBuckets } from \"./buckets/UserBuckets.ts\";\nimport { AdminIdentityController } from \"./controllers/AdminIdentityController.ts\";\nimport { AdminSessionController } from \"./controllers/AdminSessionController.ts\";\nimport { AdminUserController } from \"./controllers/AdminUserController.ts\";\nimport { RealmController } from \"./controllers/RealmController.ts\";\nimport { UserController } from \"./controllers/UserController.ts\";\nimport { UserJobs } from \"./jobs/UserJobs.ts\";\nimport { UserNotifications } from \"./notifications/UserNotifications.ts\";\nimport { RealmProvider } from \"./providers/RealmProvider.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 \"./audits/UserAudits.ts\";\nexport * from \"./buckets/UserBuckets.ts\";\nexport * from \"./controllers/AdminIdentityController.ts\";\nexport * from \"./controllers/AdminSessionController.ts\";\nexport * from \"./controllers/AdminUserController.ts\";\nexport * from \"./controllers/RealmController.ts\";\nexport * from \"./controllers/UserController.ts\";\nexport * from \"./entities/identities.ts\";\nexport * from \"./entities/sessions.ts\";\nexport * from \"./entities/users.ts\";\nexport * from \"./jobs/UserJobs.ts\";\nexport * from \"./notifications/UserNotifications.ts\";\nexport * from \"./primitives/$realm.ts\";\nexport * from \"./providers/RealmProvider.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/realmConfigSchema.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/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 * Complete user management with multi-realm support for multi-tenant applications.\n *\n * **Features:**\n * - User registration, login, and profile management\n * - Password reset workflows\n * - Email verification\n * - Session management with multiple devices\n * - Identity management (social logins, SSO)\n * - Multi-realm support for tenant isolation\n * - Credential management\n * - Entities: `users`, `identities`, `sessions`\n *\n * @module alepha.api.users\n */\nexport const AlephaApiUsers = $module({\n name: \"alepha.api.users\",\n services: [\n RealmProvider,\n SessionService,\n SessionCrudService,\n CredentialService,\n RegistrationService,\n UserService,\n IdentityService,\n UserController,\n AdminUserController,\n AdminSessionController,\n AdminIdentityController,\n RealmController,\n ],\n variants: [UserJobs, UserNotifications, UserAudits, UserBuckets],\n});\n"],"mappings":"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"}