alepha 0.19.3 → 0.19.5

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 (289) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/dist/api/invitations/index.d.ts +790 -0
  3. package/dist/api/invitations/index.d.ts.map +1 -0
  4. package/dist/api/invitations/index.js +665 -0
  5. package/dist/api/invitations/index.js.map +1 -0
  6. package/dist/api/issues/index.d.ts +810 -0
  7. package/dist/api/issues/index.d.ts.map +1 -0
  8. package/dist/api/issues/index.js +447 -0
  9. package/dist/api/issues/index.js.map +1 -0
  10. package/dist/api/jobs/index.browser.js +8 -9
  11. package/dist/api/jobs/index.browser.js.map +1 -1
  12. package/dist/api/jobs/index.d.ts +99 -43
  13. package/dist/api/jobs/index.d.ts.map +1 -1
  14. package/dist/api/jobs/index.js +257 -40
  15. package/dist/api/jobs/index.js.map +1 -1
  16. package/dist/api/notifications/index.browser.js +0 -1
  17. package/dist/api/notifications/index.browser.js.map +1 -1
  18. package/dist/api/notifications/index.d.ts +3 -3
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +0 -1
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.browser.js +112 -1
  23. package/dist/api/parameters/index.browser.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +90 -3
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +79 -12
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/{billing → api/payments}/index.d.ts +67 -49
  29. package/dist/api/payments/index.d.ts.map +1 -0
  30. package/dist/{billing → api/payments}/index.js +108 -74
  31. package/dist/api/payments/index.js.map +1 -0
  32. package/dist/api/subscriptions/index.d.ts +1692 -0
  33. package/dist/api/subscriptions/index.d.ts.map +1 -0
  34. package/dist/api/subscriptions/index.js +1870 -0
  35. package/dist/api/subscriptions/index.js.map +1 -0
  36. package/dist/api/users/index.d.ts +24 -2
  37. package/dist/api/users/index.d.ts.map +1 -1
  38. package/dist/api/users/index.js +176 -36
  39. package/dist/api/users/index.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +13 -13
  41. package/dist/api/workflows/index.browser.js +246 -0
  42. package/dist/api/workflows/index.browser.js.map +1 -0
  43. package/dist/api/workflows/index.d.ts +1618 -0
  44. package/dist/api/workflows/index.d.ts.map +1 -0
  45. package/dist/api/workflows/index.js +1504 -0
  46. package/dist/api/workflows/index.js.map +1 -0
  47. package/dist/captcha/index.d.ts +142 -0
  48. package/dist/captcha/index.d.ts.map +1 -0
  49. package/dist/captcha/index.js +177 -0
  50. package/dist/captcha/index.js.map +1 -0
  51. package/dist/cli/core/index.d.ts +126 -30
  52. package/dist/cli/core/index.d.ts.map +1 -1
  53. package/dist/cli/core/index.js +106 -67
  54. package/dist/cli/core/index.js.map +1 -1
  55. package/dist/cli/platform/index.d.ts +84 -10
  56. package/dist/cli/platform/index.d.ts.map +1 -1
  57. package/dist/cli/platform/index.js +92 -4
  58. package/dist/cli/platform/index.js.map +1 -1
  59. package/dist/cli/vendor/index.d.ts +60 -10
  60. package/dist/cli/vendor/index.d.ts.map +1 -1
  61. package/dist/cli/vendor/index.js +177 -45
  62. package/dist/cli/vendor/index.js.map +1 -1
  63. package/dist/command/index.d.ts.map +1 -1
  64. package/dist/command/index.js +2 -3
  65. package/dist/command/index.js.map +1 -1
  66. package/dist/core/index.browser.js +21 -2
  67. package/dist/core/index.browser.js.map +1 -1
  68. package/dist/core/index.d.ts +33 -2
  69. package/dist/core/index.d.ts.map +1 -1
  70. package/dist/core/index.js +21 -2
  71. package/dist/core/index.js.map +1 -1
  72. package/dist/core/index.native.js +21 -2
  73. package/dist/core/index.native.js.map +1 -1
  74. package/dist/core/index.workerd.js +21 -2
  75. package/dist/core/index.workerd.js.map +1 -1
  76. package/dist/email/smtp/index.js +24 -8
  77. package/dist/email/smtp/index.js.map +1 -1
  78. package/dist/orm/core/index.browser.js +0 -18
  79. package/dist/orm/core/index.browser.js.map +1 -1
  80. package/dist/orm/core/index.bun.js +6 -23
  81. package/dist/orm/core/index.bun.js.map +1 -1
  82. package/dist/orm/core/index.d.ts +1 -13
  83. package/dist/orm/core/index.d.ts.map +1 -1
  84. package/dist/orm/core/index.js +6 -23
  85. package/dist/orm/core/index.js.map +1 -1
  86. package/dist/orm/postgres/index.bun.js +3 -3
  87. package/dist/orm/postgres/index.bun.js.map +1 -1
  88. package/dist/orm/postgres/index.d.ts.map +1 -1
  89. package/dist/orm/postgres/index.js +3 -3
  90. package/dist/orm/postgres/index.js.map +1 -1
  91. package/dist/react/i18n/index.d.ts +1 -0
  92. package/dist/react/i18n/index.d.ts.map +1 -1
  93. package/dist/react/i18n/index.js +8 -4
  94. package/dist/react/i18n/index.js.map +1 -1
  95. package/dist/react/router/index.browser.js +25 -3
  96. package/dist/react/router/index.browser.js.map +1 -1
  97. package/dist/react/router/index.d.ts +16 -1
  98. package/dist/react/router/index.d.ts.map +1 -1
  99. package/dist/react/router/index.js +25 -3
  100. package/dist/react/router/index.js.map +1 -1
  101. package/dist/security/index.d.ts +28 -0
  102. package/dist/security/index.d.ts.map +1 -1
  103. package/dist/security/index.js +28 -0
  104. package/dist/security/index.js.map +1 -1
  105. package/dist/server/auth/index.d.ts +145 -2
  106. package/dist/server/auth/index.d.ts.map +1 -1
  107. package/dist/server/auth/index.js +364 -63
  108. package/dist/server/auth/index.js.map +1 -1
  109. package/dist/server/cookies/index.d.ts.map +1 -1
  110. package/dist/server/cookies/index.js.map +1 -1
  111. package/dist/websocket/index.d.ts.map +1 -1
  112. package/dist/websocket/index.js.map +1 -1
  113. package/package.json +47 -20
  114. package/src/api/invitations/__tests__/InvitationService.spec.ts +439 -0
  115. package/src/api/invitations/controllers/AdminInvitationController.ts +86 -0
  116. package/src/api/invitations/controllers/InvitationController.ts +84 -0
  117. package/src/api/invitations/entities/invitations.ts +33 -0
  118. package/src/api/invitations/index.ts +65 -0
  119. package/src/api/invitations/jobs/InvitationJobs.ts +37 -0
  120. package/src/api/invitations/providers/InvitationProvider.ts +45 -0
  121. package/src/api/invitations/schemas/createInvitationSchema.ts +12 -0
  122. package/src/api/invitations/schemas/invitationConfigAtom.ts +20 -0
  123. package/src/api/invitations/schemas/invitationQuerySchema.ts +15 -0
  124. package/src/api/invitations/schemas/invitationResourceSchema.ts +6 -0
  125. package/src/api/invitations/schemas/invitationWithResourceInfoSchema.ts +22 -0
  126. package/src/api/invitations/schemas/myInvitationsQuerySchema.ts +10 -0
  127. package/src/api/invitations/services/InvitationService.ts +556 -0
  128. package/src/api/issues/__tests__/IssueService.spec.ts +263 -0
  129. package/src/api/issues/controllers/AdminIssueController.ts +149 -0
  130. package/src/api/issues/controllers/IssueController.ts +44 -0
  131. package/src/api/issues/entities/issues.ts +49 -0
  132. package/src/api/issues/index.ts +53 -0
  133. package/src/api/issues/schemas/createIssueSchema.ts +13 -0
  134. package/src/api/issues/schemas/issueConfigAtom.ts +13 -0
  135. package/src/api/issues/schemas/issueQuerySchema.ts +18 -0
  136. package/src/api/issues/schemas/issueResourceSchema.ts +6 -0
  137. package/src/api/issues/schemas/myIssueQuerySchema.ts +10 -0
  138. package/src/api/issues/schemas/updateIssueSchema.ts +13 -0
  139. package/src/api/issues/services/IssueService.ts +264 -0
  140. package/src/api/jobs/__tests__/$job.spec.ts +876 -0
  141. package/src/api/jobs/controllers/AdminJobController.ts +44 -0
  142. package/src/api/jobs/entities/jobExecutionEntity.ts +0 -2
  143. package/src/api/jobs/index.ts +0 -3
  144. package/src/api/jobs/primitives/$job.ts +22 -11
  145. package/src/api/jobs/providers/JobProvider.ts +229 -19
  146. package/src/api/jobs/schemas/jobConfigAtom.ts +4 -0
  147. package/src/api/jobs/schemas/jobCronInfoSchema.ts +1 -0
  148. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +0 -1
  149. package/src/api/jobs/schemas/jobQueueDepthSchema.ts +1 -0
  150. package/src/api/jobs/schemas/jobRegistrationSchema.ts +1 -6
  151. package/src/api/jobs/services/JobService.ts +51 -12
  152. package/src/api/notifications/schemas/notificationQuerySchema.ts +0 -1
  153. package/src/api/parameters/__tests__/$parameter.spec.ts +327 -0
  154. package/src/api/parameters/controllers/AdminParameterController.ts +29 -3
  155. package/src/api/parameters/index.browser.ts +12 -0
  156. package/src/api/parameters/primitives/$parameter.ts +20 -3
  157. package/src/api/parameters/services/ParameterProvider.ts +48 -7
  158. package/src/{billing → api/payments}/__tests__/PaymentMethodService.spec.ts +32 -6
  159. package/src/api/payments/__tests__/PaymentService.spec.ts +279 -0
  160. package/src/{billing/controllers/AdminBillingController.ts → api/payments/controllers/AdminPaymentController.ts} +26 -21
  161. package/src/{billing/controllers/BillingController.ts → api/payments/controllers/PaymentController.ts} +23 -11
  162. package/src/{billing → api/payments}/entities/paymentIntents.ts +1 -0
  163. package/src/{billing/errors/BillingError.ts → api/payments/errors/PaymentError.ts} +1 -1
  164. package/src/{billing → api/payments}/index.ts +31 -25
  165. package/src/{billing/providers/MemoryBillingProvider.ts → api/payments/providers/MemoryPaymentProvider.ts} +4 -4
  166. package/src/{billing/providers/BillingProvider.ts → api/payments/providers/PaymentProvider.ts} +9 -2
  167. package/src/{billing → api/payments}/services/PaymentMethodService.ts +5 -5
  168. package/src/{billing/services/BillingService.ts → api/payments/services/PaymentService.ts} +94 -18
  169. package/src/api/subscriptions/__tests__/BillingService.spec.ts +218 -0
  170. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +278 -0
  171. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +212 -0
  172. package/src/api/subscriptions/controllers/SubscriptionController.ts +189 -0
  173. package/src/api/subscriptions/entities/subscriptionEvents.ts +54 -0
  174. package/src/api/subscriptions/entities/subscriptions.ts +68 -0
  175. package/src/api/subscriptions/index.ts +144 -0
  176. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +382 -0
  177. package/src/api/subscriptions/middleware/$requireLimit.ts +50 -0
  178. package/src/api/subscriptions/middleware/$requirePlan.ts +49 -0
  179. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +110 -0
  180. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +8 -0
  181. package/src/api/subscriptions/schemas/changePlanSchema.ts +9 -0
  182. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +11 -0
  183. package/src/api/subscriptions/schemas/entitlementsSchema.ts +21 -0
  184. package/src/api/subscriptions/schemas/mrrSchema.ts +13 -0
  185. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +71 -0
  186. package/src/api/subscriptions/schemas/planResourceSchema.ts +25 -0
  187. package/src/api/subscriptions/schemas/subscriptionEventResourceSchema.ts +8 -0
  188. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +19 -0
  189. package/src/api/subscriptions/schemas/subscriptionResourceSchema.ts +6 -0
  190. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +32 -0
  191. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +23 -0
  192. package/src/api/subscriptions/services/BillingService.ts +437 -0
  193. package/src/api/subscriptions/services/SubscriptionConfig.ts +56 -0
  194. package/src/api/subscriptions/services/SubscriptionService.ts +867 -0
  195. package/src/api/subscriptions/services/UsageService.ts +118 -0
  196. package/src/api/users/__tests__/AdminUserController.spec.ts +80 -1
  197. package/src/api/users/__tests__/CredentialService.spec.ts +177 -0
  198. package/src/api/users/__tests__/EmailVerification.spec.ts +29 -18
  199. package/src/api/users/__tests__/PasswordReset.spec.ts +3 -0
  200. package/src/api/users/__tests__/RegistrationService.spec.ts +148 -1
  201. package/src/api/users/__tests__/SessionService.spec.ts +142 -1
  202. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -1
  203. package/src/api/users/controllers/UserController.ts +3 -8
  204. package/src/api/users/notifications/UserNotifications.ts +23 -0
  205. package/src/api/users/primitives/$realm.ts +24 -0
  206. package/src/api/users/schemas/loginSchema.ts +1 -1
  207. package/src/api/users/services/CredentialService.ts +57 -7
  208. package/src/api/users/services/RegistrationService.ts +50 -11
  209. package/src/api/users/services/SessionService.ts +64 -9
  210. package/src/api/users/services/UserService.ts +21 -12
  211. package/src/api/workflows/__tests__/$workflow.spec.ts +616 -0
  212. package/src/api/workflows/controllers/AdminWorkflowController.ts +191 -0
  213. package/src/api/workflows/entities/workflowExecutions.ts +74 -0
  214. package/src/api/workflows/entities/workflowStepExecutions.ts +74 -0
  215. package/src/api/workflows/entities/workflowStepLogs.ts +13 -0
  216. package/src/api/workflows/index.browser.ts +22 -0
  217. package/src/api/workflows/index.ts +124 -0
  218. package/src/api/workflows/jobs/WorkflowJobs.ts +77 -0
  219. package/src/api/workflows/primitives/$workflow.ts +202 -0
  220. package/src/api/workflows/providers/WorkflowProvider.ts +1284 -0
  221. package/src/api/workflows/schemas/workflowActivitySchema.ts +15 -0
  222. package/src/api/workflows/schemas/workflowConfigAtom.ts +51 -0
  223. package/src/api/workflows/schemas/workflowExecutionDetailSchema.ts +18 -0
  224. package/src/api/workflows/schemas/workflowExecutionQuerySchema.ts +26 -0
  225. package/src/api/workflows/schemas/workflowExecutionResourceSchema.ts +30 -0
  226. package/src/api/workflows/schemas/workflowRegistrationSchema.ts +26 -0
  227. package/src/api/workflows/schemas/workflowStatsSchema.ts +16 -0
  228. package/src/api/workflows/schemas/workflowStepExecutionResourceSchema.ts +15 -0
  229. package/src/api/workflows/services/WorkflowService.ts +382 -0
  230. package/src/captcha/__tests__/MemoryCaptchaProvider.spec.ts +74 -0
  231. package/src/captcha/index.ts +33 -0
  232. package/src/captcha/providers/CaptchaProvider.ts +17 -0
  233. package/src/captcha/providers/MemoryCaptchaProvider.ts +65 -0
  234. package/src/captcha/providers/TurnstileCaptchaProvider.ts +125 -0
  235. package/src/cli/core/atoms/buildOptions.ts +57 -0
  236. package/src/cli/core/commands/build.ts +2 -0
  237. package/src/cli/core/providers/ViteDevServerProvider.ts +1 -1
  238. package/src/cli/core/services/ViteUtils.ts +5 -2
  239. package/src/cli/core/tasks/BuildClientTask.ts +3 -1
  240. package/src/cli/core/tasks/BuildCloudflareTask.ts +4 -0
  241. package/src/cli/core/tasks/BuildPwaTask.ts +81 -0
  242. package/src/cli/core/templates/webAppRouterTs.ts +5 -58
  243. package/src/cli/platform/adapters/CloudflareAdapter.ts +24 -0
  244. package/src/cli/platform/atoms/platformOptions.ts +19 -3
  245. package/src/cli/platform/hooks/PlatformHook.ts +51 -0
  246. package/src/cli/platform/index.ts +1 -0
  247. package/src/cli/platform/services/CloudflareApi.ts +22 -1
  248. package/src/cli/platform/services/PlatformOrchestrator.ts +67 -2
  249. package/src/cli/vendor/__tests__/VendorService.spec.ts +322 -178
  250. package/src/cli/vendor/commands/VendorCommand.ts +41 -38
  251. package/src/cli/vendor/services/VendorService.ts +234 -31
  252. package/src/command/__tests__/CliProvider.spec.ts +45 -0
  253. package/src/command/providers/CliProvider.ts +3 -4
  254. package/src/core/__tests__/TypeProvider.spec.ts +4 -2
  255. package/src/core/providers/SchemaValidator.ts +1 -1
  256. package/src/core/providers/TypeProvider.ts +46 -3
  257. package/src/orm/__tests__/enums.spec.ts +22 -29
  258. package/src/orm/__tests__/orm-showcase-tests.ts +430 -0
  259. package/src/orm/__tests__/orm-showcase.spec.ts +167 -0
  260. package/src/orm/core/providers/DatabaseTypeProvider.ts +0 -29
  261. package/src/orm/core/services/Repository.ts +20 -6
  262. package/src/orm/postgres/services/PostgresModelBuilder.ts +3 -6
  263. package/src/react/i18n/__tests__/I18nProvider.spec.ts +83 -0
  264. package/src/react/i18n/providers/I18nProvider.ts +12 -10
  265. package/src/react/router/__tests__/$page.browser.spec.tsx +157 -0
  266. package/src/react/router/providers/ReactBrowserProvider.ts +39 -0
  267. package/src/react/router/providers/ReactBrowserRouterProvider.ts +22 -0
  268. package/src/security/__tests__/$secure-combinations.spec.ts +945 -0
  269. package/src/security/primitives/$issuer.ts +3 -1
  270. package/src/security/primitives/$secure.ts +28 -0
  271. package/src/server/auth/index.ts +7 -0
  272. package/src/server/auth/primitives/$auth.ts +37 -3
  273. package/src/server/auth/primitives/$authApple.ts +114 -4
  274. package/src/server/auth/primitives/$authFacebook.ts +98 -0
  275. package/src/server/auth/primitives/$authFranceConnect.ts +105 -0
  276. package/src/server/auth/primitives/$authGithub.ts +22 -16
  277. package/src/server/auth/primitives/$authMicrosoft.ts +88 -0
  278. package/src/server/auth/providers/ServerAuthProvider.ts +197 -72
  279. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -0
  280. package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +1 -1
  281. package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -1
  282. package/dist/billing/index.d.ts.map +0 -1
  283. package/dist/billing/index.js.map +0 -1
  284. package/src/billing/__tests__/BillingService.spec.ts +0 -136
  285. /package/src/{billing → api/payments}/entities/paymentMethods.ts +0 -0
  286. /package/src/{billing → api/payments}/entities/refunds.ts +0 -0
  287. /package/src/{billing → api/payments}/schemas/intentSchemas.ts +0 -0
  288. /package/src/{billing → api/payments}/schemas/paymentMethodSchemas.ts +0 -0
  289. /package/src/{billing → api/payments}/schemas/refundSchemas.ts +0 -0
@@ -15,6 +15,7 @@ import { $client } from "alepha/server/links";
15
15
  import { FileSystemProvider } from "alepha/system";
16
16
  import { UserAudits } from "../audits/UserAudits.ts";
17
17
  import type { UserEntity } from "../entities/users.ts";
18
+ import { UserNotifications } from "../notifications/UserNotifications.ts";
18
19
  import { RealmProvider } from "../providers/RealmProvider.ts";
19
20
 
20
21
  export class SessionService {
@@ -35,6 +36,14 @@ export class SessionService {
35
36
  return undefined;
36
37
  }
37
38
 
39
+ protected userNotifications(realmName?: string) {
40
+ const realm = this.realmProvider.getRealm(realmName);
41
+ if (realm.features.notifications) {
42
+ return this.alepha.inject(UserNotifications);
43
+ }
44
+ return undefined;
45
+ }
46
+
38
47
  public users(userRealmName?: string) {
39
48
  return this.realmProvider.userRepository(userRealmName);
40
49
  }
@@ -141,16 +150,12 @@ export class SessionService {
141
150
  // Truncate to leave room for suffix
142
151
  candidate = candidate.slice(0, maxLength - 2);
143
152
 
144
- // Check uniqueness (case-insensitive)
153
+ // Check uniqueness (case-insensitive exact match)
145
154
  const isAvailable = async (name: string) => {
146
- const existing = await users.findMany({
147
- where: { username: { contains: name } },
148
- limit: 1,
155
+ const existing = await users.findOne({
156
+ where: { username: { ilike: name } },
149
157
  });
150
- // Case-insensitive check
151
- return !existing.some(
152
- (u: any) => u.username?.toLowerCase() === name.toLowerCase(),
153
- );
158
+ return !existing;
154
159
  };
155
160
 
156
161
  if (await isAvailable(candidate)) {
@@ -354,6 +359,23 @@ export class SessionService {
354
359
  throw new InvalidCredentialsError();
355
360
  }
356
361
 
362
+ // Check if user account is enabled
363
+ if (!user.enabled) {
364
+ this.log.warn("Login attempt for disabled account", {
365
+ userId: user.id,
366
+ realm: name,
367
+ });
368
+
369
+ await this.userAudits(userRealmName)?.recordAuth("login_failed", {
370
+ userRealm: name,
371
+ resourceId: user.id,
372
+ description: "Login attempt for disabled account",
373
+ metadata: { provider, username },
374
+ });
375
+
376
+ throw new InvalidCredentialsError();
377
+ }
378
+
357
379
  // Account rate limit check (per-realm)
358
380
  const accountKey = `login:account:${name}:${user.id}`;
359
381
  const accountLocked = await this.isLoginLocked(
@@ -445,6 +467,15 @@ export class SessionService {
445
467
  metadata: { userId: user.id },
446
468
  },
447
469
  );
470
+
471
+ // Notify user about account lockout
472
+ if (user.email) {
473
+ const lockoutMinutes = Math.round(loginRateLimit.windowMs / 60_000);
474
+ await this.userNotifications(userRealmName)?.accountLockout.push({
475
+ contact: user.email,
476
+ variables: { email: user.email, lockoutMinutes },
477
+ });
478
+ }
448
479
  }
449
480
 
450
481
  throw new InvalidCredentialsError();
@@ -512,6 +543,8 @@ export class SessionService {
512
543
  public async refreshSession(refreshToken: string, userRealmName?: string) {
513
544
  this.log.trace("Refreshing session");
514
545
 
546
+ // getOne() throws DbEntityNotFoundError if not found — never returns null.
547
+ // No null check needed here.
515
548
  const session = await this.sessions(userRealmName).getOne({
516
549
  where: {
517
550
  refreshToken: { eq: refreshToken },
@@ -536,6 +569,16 @@ export class SessionService {
536
569
  },
537
570
  });
538
571
 
572
+ // Check if user account is still enabled
573
+ if (!user.enabled) {
574
+ this.log.warn("Session refresh for disabled account", {
575
+ userId: user.id,
576
+ sessionId: session.id,
577
+ });
578
+ await this.sessions(userRealmName).deleteById(session.id);
579
+ throw new UnauthorizedError("Account disabled");
580
+ }
581
+
539
582
  // Auto-promote to admin if configured (handles "I promote you admin" case)
540
583
  await this.ensureAdminRole(user, userRealmName);
541
584
 
@@ -636,11 +679,23 @@ export class SessionService {
636
679
 
637
680
  const existing = await users.findOne({
638
681
  where: {
682
+ realm: realm.name,
639
683
  email: profile.email,
640
684
  },
641
685
  });
642
686
 
643
687
  if (existing) {
688
+ // Refuse auto-link if the OAuth provider explicitly says email is not verified
689
+ if (profile.email_verified === false) {
690
+ this.log.warn(
691
+ "OAuth2 profile email not verified by provider, refusing auto-link",
692
+ { provider, email: profile.email, userId: existing.id },
693
+ );
694
+ throw new BadRequestError(
695
+ "Cannot link account: email not verified by provider",
696
+ );
697
+ }
698
+
644
699
  this.log.debug("Linking OAuth2 profile to existing user by email", {
645
700
  provider,
646
701
  profileSub: profile.sub,
@@ -692,7 +747,7 @@ export class SessionService {
692
747
  email: profile.email,
693
748
  // we trust the OAuth2 provider
694
749
  emailVerified: true,
695
- roles: ["user"], // TODO: make default roles configurable via realm settings
750
+ roles: realmSettings.defaultRoles,
696
751
  });
697
752
 
698
753
  if (profile.picture) {
@@ -49,7 +49,6 @@ export class UserService {
49
49
  email: string,
50
50
  userRealmName?: string,
51
51
  method: "code" | "link" = "code",
52
- verifyUrl?: string,
53
52
  ): Promise<boolean> {
54
53
  this.log.trace("Requesting email verification", {
55
54
  email,
@@ -84,12 +83,15 @@ export class UserService {
84
83
  });
85
84
 
86
85
  if (method === "link") {
87
- // Build verification URL with token
88
- const url = new URL(verifyUrl || "/verify-email", "http://localhost");
86
+ // Build verification URL from realm settings (server-controlled, not user input)
87
+ const realm = this.realmProvider.getRealm(userRealmName);
88
+ const realmSettings = await realm.getSettings();
89
+ const baseUrl = realmSettings.verifyEmailUrl ?? "/verify-email";
90
+ const url = new URL(baseUrl, "http://localhost");
89
91
  url.searchParams.set("email", email);
90
92
  url.searchParams.set("token", verification.token);
91
- const fullVerifyUrl = verifyUrl
92
- ? `${verifyUrl}${url.search}`
93
+ const fullVerifyUrl = realmSettings.verifyEmailUrl
94
+ ? `${baseUrl}${url.search}`
93
95
  : url.pathname + url.search;
94
96
 
95
97
  await this.userNotifications(userRealmName)?.emailVerificationLink.push(
@@ -270,13 +272,12 @@ export class UserService {
270
272
  });
271
273
 
272
274
  const realm = this.realmProvider.getRealm(userRealmName);
275
+ const realmSettings = await realm.getSettings();
273
276
 
274
- // TODO: one query instead of 3
275
-
276
- // Check for existing user based on provided unique fields
277
+ // Check for existing user based on provided unique fields (scoped to realm)
277
278
  if (data.username) {
278
279
  const existingUser = await this.users(userRealmName).findOne({
279
- where: { username: { ilike: data.username } },
280
+ where: { realm: realm.name, username: { ilike: data.username } },
280
281
  });
281
282
 
282
283
  if (existingUser) {
@@ -287,7 +288,7 @@ export class UserService {
287
288
 
288
289
  if (data.email) {
289
290
  const existingUser = await this.users(userRealmName).findOne({
290
- where: { email: { eq: data.email } },
291
+ where: { realm: realm.name, email: { eq: data.email } },
291
292
  });
292
293
 
293
294
  if (existingUser) {
@@ -298,7 +299,7 @@ export class UserService {
298
299
 
299
300
  if (data.phoneNumber) {
300
301
  const existingUser = await this.users(userRealmName).findOne({
301
- where: { phoneNumber: { eq: data.phoneNumber } },
302
+ where: { realm: realm.name, phoneNumber: { eq: data.phoneNumber } },
302
303
  });
303
304
 
304
305
  if (existingUser) {
@@ -311,7 +312,7 @@ export class UserService {
311
312
 
312
313
  const user = await this.users(userRealmName).create({
313
314
  ...data,
314
- roles: data.roles ?? ["user"], // TODO: Default roles from realm settings
315
+ roles: data.roles ?? realmSettings.defaultRoles,
315
316
  realm: realm.name,
316
317
  });
317
318
 
@@ -386,6 +387,14 @@ export class UserService {
386
387
  this.log.trace("Deleting user", { id, userRealmName });
387
388
  const user = await this.getUserById(id, userRealmName);
388
389
 
390
+ // Clean up related sessions and identities before deleting the user
391
+ await this.realmProvider
392
+ .sessionRepository(userRealmName)
393
+ .deleteMany({ userId: { eq: id } });
394
+ await this.realmProvider
395
+ .identityRepository(userRealmName)
396
+ .deleteMany({ userId: { eq: id } });
397
+
389
398
  await this.users(userRealmName).deleteById(id);
390
399
  this.log.info("User deleted", { userId: id });
391
400