@authup/server-core 1.0.0-beta.55 → 1.0.0-beta.57

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 (410) hide show
  1. package/dist/adapters/database/domains/role/entity.d.ts.map +1 -1
  2. package/dist/adapters/database/domains/role/entity.mjs +2 -1
  3. package/dist/adapters/database/domains/role/entity.mjs.map +1 -1
  4. package/dist/adapters/database/migrations/mysql/1784970000000-RoleClientForeignKey.d.ts +25 -0
  5. package/dist/adapters/database/migrations/mysql/1784970000000-RoleClientForeignKey.d.ts.map +1 -0
  6. package/dist/adapters/database/migrations/mysql/1784970000000-RoleClientForeignKey.mjs +60 -0
  7. package/dist/adapters/database/migrations/mysql/1784970000000-RoleClientForeignKey.mjs.map +1 -0
  8. package/dist/adapters/database/migrations/postgres/1784970000000-RoleClientForeignKey.d.ts +25 -0
  9. package/dist/adapters/database/migrations/postgres/1784970000000-RoleClientForeignKey.d.ts.map +1 -0
  10. package/dist/adapters/database/migrations/postgres/1784970000000-RoleClientForeignKey.mjs +60 -0
  11. package/dist/adapters/database/migrations/postgres/1784970000000-RoleClientForeignKey.mjs.map +1 -0
  12. package/dist/adapters/http/adapters/oauth2/grant-types/authorize.mjs +1 -1
  13. package/dist/adapters/http/adapters/oauth2/grant-types/authorize.mjs.map +1 -1
  14. package/dist/adapters/http/adapters/oauth2/grant-types/client-credentials.mjs +1 -1
  15. package/dist/adapters/http/adapters/oauth2/grant-types/client-credentials.mjs.map +1 -1
  16. package/dist/adapters/http/adapters/oauth2/grant-types/identity.mjs +1 -1
  17. package/dist/adapters/http/adapters/oauth2/grant-types/identity.mjs.map +1 -1
  18. package/dist/adapters/http/adapters/oauth2/grant-types/password.mjs +1 -1
  19. package/dist/adapters/http/adapters/oauth2/grant-types/password.mjs.map +1 -1
  20. package/dist/adapters/http/adapters/oauth2/grant-types/refresh_token.mjs +1 -1
  21. package/dist/adapters/http/adapters/oauth2/grant-types/refresh_token.mjs.map +1 -1
  22. package/dist/adapters/http/controllers/entities/client/module.d.ts +6 -6
  23. package/dist/adapters/http/controllers/entities/client/module.d.ts.map +1 -1
  24. package/dist/adapters/http/controllers/entities/client/module.mjs +30 -7
  25. package/dist/adapters/http/controllers/entities/client/module.mjs.map +1 -1
  26. package/dist/adapters/http/controllers/entities/client-permission/module.d.ts +5 -5
  27. package/dist/adapters/http/controllers/entities/client-permission/module.d.ts.map +1 -1
  28. package/dist/adapters/http/controllers/entities/client-permission/module.mjs +23 -5
  29. package/dist/adapters/http/controllers/entities/client-permission/module.mjs.map +1 -1
  30. package/dist/adapters/http/controllers/entities/client-role/module.d.ts +4 -4
  31. package/dist/adapters/http/controllers/entities/client-role/module.d.ts.map +1 -1
  32. package/dist/adapters/http/controllers/entities/client-role/module.mjs +19 -4
  33. package/dist/adapters/http/controllers/entities/client-role/module.mjs.map +1 -1
  34. package/dist/adapters/http/controllers/entities/client-scope/module.d.ts +4 -4
  35. package/dist/adapters/http/controllers/entities/client-scope/module.d.ts.map +1 -1
  36. package/dist/adapters/http/controllers/entities/client-scope/module.mjs +19 -4
  37. package/dist/adapters/http/controllers/entities/client-scope/module.mjs.map +1 -1
  38. package/dist/adapters/http/controllers/entities/consent/module.d.ts +3 -3
  39. package/dist/adapters/http/controllers/entities/consent/module.d.ts.map +1 -1
  40. package/dist/adapters/http/controllers/entities/consent/module.mjs +15 -3
  41. package/dist/adapters/http/controllers/entities/consent/module.mjs.map +1 -1
  42. package/dist/adapters/http/controllers/entities/event/module.d.ts +2 -2
  43. package/dist/adapters/http/controllers/entities/event/module.d.ts.map +1 -1
  44. package/dist/adapters/http/controllers/entities/event/module.mjs +11 -2
  45. package/dist/adapters/http/controllers/entities/event/module.mjs.map +1 -1
  46. package/dist/adapters/http/controllers/entities/identity-provide-role/module.d.ts +5 -5
  47. package/dist/adapters/http/controllers/entities/identity-provide-role/module.d.ts.map +1 -1
  48. package/dist/adapters/http/controllers/entities/identity-provide-role/module.mjs +23 -5
  49. package/dist/adapters/http/controllers/entities/identity-provide-role/module.mjs.map +1 -1
  50. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +6 -6
  51. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
  52. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +23 -7
  53. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
  54. package/dist/adapters/http/controllers/entities/key/module.d.ts +5 -5
  55. package/dist/adapters/http/controllers/entities/key/module.d.ts.map +1 -1
  56. package/dist/adapters/http/controllers/entities/key/module.mjs +23 -5
  57. package/dist/adapters/http/controllers/entities/key/module.mjs.map +1 -1
  58. package/dist/adapters/http/controllers/entities/permission/module.d.ts +6 -6
  59. package/dist/adapters/http/controllers/entities/permission/module.d.ts.map +1 -1
  60. package/dist/adapters/http/controllers/entities/permission/module.mjs +27 -6
  61. package/dist/adapters/http/controllers/entities/permission/module.mjs.map +1 -1
  62. package/dist/adapters/http/controllers/entities/permission-policy/module.d.ts +4 -4
  63. package/dist/adapters/http/controllers/entities/permission-policy/module.d.ts.map +1 -1
  64. package/dist/adapters/http/controllers/entities/permission-policy/module.mjs +19 -4
  65. package/dist/adapters/http/controllers/entities/permission-policy/module.mjs.map +1 -1
  66. package/dist/adapters/http/controllers/entities/policy/module.d.ts +7 -7
  67. package/dist/adapters/http/controllers/entities/policy/module.d.ts.map +1 -1
  68. package/dist/adapters/http/controllers/entities/policy/module.mjs +27 -6
  69. package/dist/adapters/http/controllers/entities/policy/module.mjs.map +1 -1
  70. package/dist/adapters/http/controllers/entities/realm/module.d.ts +6 -6
  71. package/dist/adapters/http/controllers/entities/realm/module.d.ts.map +1 -1
  72. package/dist/adapters/http/controllers/entities/realm/module.mjs +29 -8
  73. package/dist/adapters/http/controllers/entities/realm/module.mjs.map +1 -1
  74. package/dist/adapters/http/controllers/entities/role/module.d.ts +6 -6
  75. package/dist/adapters/http/controllers/entities/role/module.d.ts.map +1 -1
  76. package/dist/adapters/http/controllers/entities/role/module.mjs +27 -6
  77. package/dist/adapters/http/controllers/entities/role/module.mjs.map +1 -1
  78. package/dist/adapters/http/controllers/entities/role-attribute/module.d.ts +5 -5
  79. package/dist/adapters/http/controllers/entities/role-attribute/module.d.ts.map +1 -1
  80. package/dist/adapters/http/controllers/entities/role-attribute/module.mjs +23 -5
  81. package/dist/adapters/http/controllers/entities/role-attribute/module.mjs.map +1 -1
  82. package/dist/adapters/http/controllers/entities/role-permission/module.d.ts +5 -5
  83. package/dist/adapters/http/controllers/entities/role-permission/module.d.ts.map +1 -1
  84. package/dist/adapters/http/controllers/entities/role-permission/module.mjs +23 -5
  85. package/dist/adapters/http/controllers/entities/role-permission/module.mjs.map +1 -1
  86. package/dist/adapters/http/controllers/entities/scope/module.d.ts +6 -6
  87. package/dist/adapters/http/controllers/entities/scope/module.d.ts.map +1 -1
  88. package/dist/adapters/http/controllers/entities/scope/module.mjs +27 -6
  89. package/dist/adapters/http/controllers/entities/scope/module.mjs.map +1 -1
  90. package/dist/adapters/http/controllers/entities/session/module.d.ts +3 -3
  91. package/dist/adapters/http/controllers/entities/session/module.d.ts.map +1 -1
  92. package/dist/adapters/http/controllers/entities/session/module.mjs +15 -3
  93. package/dist/adapters/http/controllers/entities/session/module.mjs.map +1 -1
  94. package/dist/adapters/http/controllers/entities/trust-anchor/module.d.ts +5 -5
  95. package/dist/adapters/http/controllers/entities/trust-anchor/module.d.ts.map +1 -1
  96. package/dist/adapters/http/controllers/entities/trust-anchor/module.mjs +23 -5
  97. package/dist/adapters/http/controllers/entities/trust-anchor/module.mjs.map +1 -1
  98. package/dist/adapters/http/controllers/entities/user/module.d.ts +6 -6
  99. package/dist/adapters/http/controllers/entities/user/module.d.ts.map +1 -1
  100. package/dist/adapters/http/controllers/entities/user/module.mjs +27 -6
  101. package/dist/adapters/http/controllers/entities/user/module.mjs.map +1 -1
  102. package/dist/adapters/http/controllers/entities/user-attribute/module.d.ts +5 -5
  103. package/dist/adapters/http/controllers/entities/user-attribute/module.d.ts.map +1 -1
  104. package/dist/adapters/http/controllers/entities/user-attribute/module.mjs +23 -5
  105. package/dist/adapters/http/controllers/entities/user-attribute/module.mjs.map +1 -1
  106. package/dist/adapters/http/controllers/entities/user-authenticator/module.d.ts +4 -4
  107. package/dist/adapters/http/controllers/entities/user-authenticator/module.d.ts.map +1 -1
  108. package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs +19 -4
  109. package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs.map +1 -1
  110. package/dist/adapters/http/controllers/entities/user-permission/module.d.ts +5 -5
  111. package/dist/adapters/http/controllers/entities/user-permission/module.d.ts.map +1 -1
  112. package/dist/adapters/http/controllers/entities/user-permission/module.mjs +23 -5
  113. package/dist/adapters/http/controllers/entities/user-permission/module.mjs.map +1 -1
  114. package/dist/adapters/http/controllers/entities/user-role/module.d.ts +4 -4
  115. package/dist/adapters/http/controllers/entities/user-role/module.d.ts.map +1 -1
  116. package/dist/adapters/http/controllers/entities/user-role/module.mjs +19 -4
  117. package/dist/adapters/http/controllers/entities/user-role/module.mjs.map +1 -1
  118. package/dist/adapters/http/controllers/index.mjs +2 -1
  119. package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.mjs +1 -1
  120. package/dist/adapters/http/controllers/workflows/authenticator-challenge/module.mjs.map +1 -1
  121. package/dist/adapters/http/controllers/workflows/index.d.ts +1 -0
  122. package/dist/adapters/http/controllers/workflows/index.d.ts.map +1 -1
  123. package/dist/adapters/http/controllers/workflows/index.mjs +3 -1
  124. package/dist/adapters/http/controllers/workflows/logout/module.d.ts.map +1 -1
  125. package/dist/adapters/http/controllers/workflows/logout/module.mjs +5 -1
  126. package/dist/adapters/http/controllers/workflows/logout/module.mjs.map +1 -1
  127. package/dist/adapters/http/controllers/workflows/userinfo/index.d.ts +2 -0
  128. package/dist/adapters/http/controllers/workflows/userinfo/index.d.ts.map +1 -0
  129. package/dist/adapters/http/controllers/workflows/userinfo/index.mjs +5 -0
  130. package/dist/adapters/http/controllers/workflows/userinfo/module.d.ts +18 -0
  131. package/dist/adapters/http/controllers/workflows/userinfo/module.d.ts.map +1 -0
  132. package/dist/adapters/http/controllers/workflows/userinfo/module.mjs +53 -0
  133. package/dist/adapters/http/controllers/workflows/userinfo/module.mjs.map +1 -0
  134. package/dist/adapters/http/index.mjs +2 -1
  135. package/dist/adapters/http/middleware/built-in/authorization/module.d.ts +1 -0
  136. package/dist/adapters/http/middleware/built-in/authorization/module.d.ts.map +1 -1
  137. package/dist/adapters/http/middleware/built-in/authorization/module.mjs +8 -0
  138. package/dist/adapters/http/middleware/built-in/authorization/module.mjs.map +1 -1
  139. package/dist/adapters/http/middleware/built-in/rate-limit.d.ts.map +1 -1
  140. package/dist/adapters/http/middleware/built-in/rate-limit.mjs +2 -0
  141. package/dist/adapters/http/middleware/built-in/rate-limit.mjs.map +1 -1
  142. package/dist/adapters/http/request/event-context.mjs +1 -1
  143. package/dist/adapters/http/request/event-context.mjs.map +1 -1
  144. package/dist/app/factory.d.ts +2 -1
  145. package/dist/app/factory.d.ts.map +1 -1
  146. package/dist/app/factory.mjs +2 -2
  147. package/dist/app/factory.mjs.map +1 -1
  148. package/dist/app/index.d.ts +1 -0
  149. package/dist/app/index.d.ts.map +1 -1
  150. package/dist/app/index.mjs +5 -1
  151. package/dist/app/modules/authentication/repositories/session.mjs +2 -2
  152. package/dist/app/modules/authentication/repositories/session.mjs.map +1 -1
  153. package/dist/app/modules/components/module.d.ts +3 -0
  154. package/dist/app/modules/components/module.d.ts.map +1 -1
  155. package/dist/app/modules/components/module.mjs +17 -2
  156. package/dist/app/modules/components/module.mjs.map +1 -1
  157. package/dist/app/modules/config/constants.d.ts +1 -0
  158. package/dist/app/modules/config/constants.d.ts.map +1 -1
  159. package/dist/app/modules/config/constants.mjs +1 -0
  160. package/dist/app/modules/config/constants.mjs.map +1 -1
  161. package/dist/app/modules/config/helpers.d.ts +9 -0
  162. package/dist/app/modules/config/helpers.d.ts.map +1 -0
  163. package/dist/app/modules/config/helpers.mjs +21 -0
  164. package/dist/app/modules/config/helpers.mjs.map +1 -0
  165. package/dist/app/modules/config/index.d.ts +2 -0
  166. package/dist/app/modules/config/index.d.ts.map +1 -1
  167. package/dist/app/modules/config/index.mjs +6 -1
  168. package/dist/app/modules/config/module.d.ts.map +1 -1
  169. package/dist/app/modules/config/module.mjs +2 -7
  170. package/dist/app/modules/config/module.mjs.map +1 -1
  171. package/dist/app/modules/config/normalize.d.ts.map +1 -1
  172. package/dist/app/modules/config/normalize.mjs +4 -0
  173. package/dist/app/modules/config/normalize.mjs.map +1 -1
  174. package/dist/app/modules/config/read/env.d.ts.map +1 -1
  175. package/dist/app/modules/config/read/env.mjs +2 -0
  176. package/dist/app/modules/config/read/env.mjs.map +1 -1
  177. package/dist/app/modules/config/read/fs.d.ts.map +1 -1
  178. package/dist/app/modules/config/read/fs.mjs +31 -8
  179. package/dist/app/modules/config/read/fs.mjs.map +1 -1
  180. package/dist/app/modules/config/trust-proxy.d.ts +28 -0
  181. package/dist/app/modules/config/trust-proxy.d.ts.map +1 -0
  182. package/dist/app/modules/config/trust-proxy.mjs +64 -0
  183. package/dist/app/modules/config/trust-proxy.mjs.map +1 -0
  184. package/dist/app/modules/config/types.d.ts +18 -0
  185. package/dist/app/modules/config/types.d.ts.map +1 -1
  186. package/dist/app/modules/config/validator.d.ts.map +1 -1
  187. package/dist/app/modules/config/validator.mjs +7 -0
  188. package/dist/app/modules/config/validator.mjs.map +1 -1
  189. package/dist/app/modules/database/repositories/client/repository.d.ts.map +1 -1
  190. package/dist/app/modules/database/repositories/client/repository.mjs +3 -4
  191. package/dist/app/modules/database/repositories/client/repository.mjs.map +1 -1
  192. package/dist/app/modules/database/repositories/client-permission/repository.mjs +2 -2
  193. package/dist/app/modules/database/repositories/client-permission/repository.mjs.map +1 -1
  194. package/dist/app/modules/database/repositories/client-role/repository.mjs +2 -2
  195. package/dist/app/modules/database/repositories/client-role/repository.mjs.map +1 -1
  196. package/dist/app/modules/database/repositories/client-scope/repository.mjs +2 -2
  197. package/dist/app/modules/database/repositories/client-scope/repository.mjs.map +1 -1
  198. package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
  199. package/dist/app/modules/database/repositories/event/repository.mjs +4 -3
  200. package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
  201. package/dist/app/modules/database/repositories/identity-provider/repository.mjs +2 -2
  202. package/dist/app/modules/database/repositories/identity-provider/repository.mjs.map +1 -1
  203. package/dist/app/modules/database/repositories/identity-provider-role-mapping/repository.mjs +2 -2
  204. package/dist/app/modules/database/repositories/identity-provider-role-mapping/repository.mjs.map +1 -1
  205. package/dist/app/modules/database/repositories/key/repository.mjs +2 -2
  206. package/dist/app/modules/database/repositories/key/repository.mjs.map +1 -1
  207. package/dist/app/modules/database/repositories/permission/repository.mjs +2 -2
  208. package/dist/app/modules/database/repositories/permission/repository.mjs.map +1 -1
  209. package/dist/app/modules/database/repositories/permission-policy/repository.mjs +2 -2
  210. package/dist/app/modules/database/repositories/permission-policy/repository.mjs.map +1 -1
  211. package/dist/app/modules/database/repositories/policy/repository.mjs +2 -2
  212. package/dist/app/modules/database/repositories/policy/repository.mjs.map +1 -1
  213. package/dist/app/modules/database/repositories/query.d.ts +20 -0
  214. package/dist/app/modules/database/repositories/query.d.ts.map +1 -1
  215. package/dist/app/modules/database/repositories/query.mjs +26 -3
  216. package/dist/app/modules/database/repositories/query.mjs.map +1 -1
  217. package/dist/app/modules/database/repositories/realm/repository.mjs +2 -2
  218. package/dist/app/modules/database/repositories/realm/repository.mjs.map +1 -1
  219. package/dist/app/modules/database/repositories/role/repository.mjs +2 -2
  220. package/dist/app/modules/database/repositories/role/repository.mjs.map +1 -1
  221. package/dist/app/modules/database/repositories/role-attribute/repository.mjs +2 -2
  222. package/dist/app/modules/database/repositories/role-attribute/repository.mjs.map +1 -1
  223. package/dist/app/modules/database/repositories/role-permission/repository.mjs +2 -2
  224. package/dist/app/modules/database/repositories/role-permission/repository.mjs.map +1 -1
  225. package/dist/app/modules/database/repositories/schema-validation.d.ts +17 -3
  226. package/dist/app/modules/database/repositories/schema-validation.d.ts.map +1 -1
  227. package/dist/app/modules/database/repositories/schema-validation.mjs +29 -5
  228. package/dist/app/modules/database/repositories/schema-validation.mjs.map +1 -1
  229. package/dist/app/modules/database/repositories/scope/repository.mjs +2 -2
  230. package/dist/app/modules/database/repositories/scope/repository.mjs.map +1 -1
  231. package/dist/app/modules/database/repositories/trust-anchor/repository.mjs +2 -2
  232. package/dist/app/modules/database/repositories/trust-anchor/repository.mjs.map +1 -1
  233. package/dist/app/modules/database/repositories/user/repository.mjs +2 -2
  234. package/dist/app/modules/database/repositories/user/repository.mjs.map +1 -1
  235. package/dist/app/modules/database/repositories/user-attribute/repository.mjs +2 -2
  236. package/dist/app/modules/database/repositories/user-attribute/repository.mjs.map +1 -1
  237. package/dist/app/modules/database/repositories/user-authenticator/repository.mjs +2 -2
  238. package/dist/app/modules/database/repositories/user-authenticator/repository.mjs.map +1 -1
  239. package/dist/app/modules/database/repositories/user-permission/repository.mjs +2 -2
  240. package/dist/app/modules/database/repositories/user-permission/repository.mjs.map +1 -1
  241. package/dist/app/modules/database/repositories/user-role/repository.mjs +2 -2
  242. package/dist/app/modules/database/repositories/user-role/repository.mjs.map +1 -1
  243. package/dist/app/modules/http/constants.d.ts +2 -13
  244. package/dist/app/modules/http/constants.d.ts.map +1 -1
  245. package/dist/app/modules/http/constants.mjs.map +1 -1
  246. package/dist/app/modules/http/module.d.ts.map +1 -1
  247. package/dist/app/modules/http/module.mjs +7 -1
  248. package/dist/app/modules/http/module.mjs.map +1 -1
  249. package/dist/app/modules/http/modules/controller.d.ts +4 -2
  250. package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
  251. package/dist/app/modules/http/modules/controller.mjs +16 -3
  252. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  253. package/dist/app/modules/index.mjs +5 -1
  254. package/dist/app/modules/oauth2/repositories/consent/repository.mjs +2 -2
  255. package/dist/app/modules/oauth2/repositories/consent/repository.mjs.map +1 -1
  256. package/dist/app/modules/provisioning/module.d.ts.map +1 -1
  257. package/dist/app/modules/provisioning/module.mjs +12 -4
  258. package/dist/app/modules/provisioning/module.mjs.map +1 -1
  259. package/dist/app/types.d.ts +5 -0
  260. package/dist/app/types.d.ts.map +1 -0
  261. package/dist/app/types.mjs +4 -0
  262. package/dist/cli/commands/healthcheck.d.ts +2 -1
  263. package/dist/cli/commands/healthcheck.d.ts.map +1 -1
  264. package/dist/cli/commands/healthcheck.mjs +3 -2
  265. package/dist/cli/commands/healthcheck.mjs.map +1 -1
  266. package/dist/cli/commands/index.d.ts +0 -1
  267. package/dist/cli/commands/index.d.ts.map +1 -1
  268. package/dist/cli/commands/index.mjs +1 -2
  269. package/dist/cli/commands/migration.d.ts +2 -1
  270. package/dist/cli/commands/migration.d.ts.map +1 -1
  271. package/dist/cli/commands/migration.mjs +3 -2
  272. package/dist/cli/commands/migration.mjs.map +1 -1
  273. package/dist/cli/commands/start.d.ts +2 -1
  274. package/dist/cli/commands/start.d.ts.map +1 -1
  275. package/dist/cli/commands/start.mjs +3 -2
  276. package/dist/cli/commands/start.mjs.map +1 -1
  277. package/dist/cli/config.d.ts +18 -0
  278. package/dist/cli/config.d.ts.map +1 -0
  279. package/dist/cli/config.mjs +34 -0
  280. package/dist/cli/config.mjs.map +1 -0
  281. package/dist/cli/module.d.ts +8 -8
  282. package/dist/cli/module.d.ts.map +1 -1
  283. package/dist/cli/module.mjs +8 -28
  284. package/dist/cli/module.mjs.map +1 -1
  285. package/dist/cli/types.d.ts +5 -0
  286. package/dist/cli/types.d.ts.map +1 -0
  287. package/dist/cli/types.mjs +4 -0
  288. package/dist/components/event-cleaner/module.d.ts.map +1 -1
  289. package/dist/components/event-cleaner/module.mjs +26 -14
  290. package/dist/components/event-cleaner/module.mjs.map +1 -1
  291. package/dist/components/index.d.ts +0 -1
  292. package/dist/components/index.d.ts.map +1 -1
  293. package/dist/components/index.mjs +1 -4
  294. package/dist/components/oauth2-cleaner/module.d.ts.map +1 -1
  295. package/dist/components/oauth2-cleaner/module.mjs +29 -17
  296. package/dist/components/oauth2-cleaner/module.mjs.map +1 -1
  297. package/dist/components/types.d.ts +1 -0
  298. package/dist/components/types.d.ts.map +1 -1
  299. package/dist/core/authentication/login-throttle/service.d.ts.map +1 -1
  300. package/dist/core/authentication/login-throttle/service.mjs +2 -1
  301. package/dist/core/authentication/login-throttle/service.mjs.map +1 -1
  302. package/dist/core/entities/client/constants.d.ts +10 -0
  303. package/dist/core/entities/client/constants.d.ts.map +1 -0
  304. package/dist/core/entities/client/constants.mjs +20 -0
  305. package/dist/core/entities/client/constants.mjs.map +1 -0
  306. package/dist/core/entities/client/index.d.ts +1 -0
  307. package/dist/core/entities/client/index.d.ts.map +1 -1
  308. package/dist/core/entities/client/index.mjs +3 -2
  309. package/dist/core/entities/client/schema.d.ts.map +1 -1
  310. package/dist/core/entities/client/schema.mjs +40 -3
  311. package/dist/core/entities/client/schema.mjs.map +1 -1
  312. package/dist/core/entities/client/service.d.ts.map +1 -1
  313. package/dist/core/entities/client/service.mjs +8 -60
  314. package/dist/core/entities/client/service.mjs.map +1 -1
  315. package/dist/core/entities/client/web-client.d.ts +39 -3
  316. package/dist/core/entities/client/web-client.d.ts.map +1 -1
  317. package/dist/core/entities/client/web-client.mjs +68 -12
  318. package/dist/core/entities/client/web-client.mjs.map +1 -1
  319. package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
  320. package/dist/core/entities/client-scope/schema.mjs +3 -1
  321. package/dist/core/entities/client-scope/schema.mjs.map +1 -1
  322. package/dist/core/entities/event/constants.d.ts +7 -0
  323. package/dist/core/entities/event/constants.d.ts.map +1 -1
  324. package/dist/core/entities/event/constants.mjs +7 -1
  325. package/dist/core/entities/event/constants.mjs.map +1 -1
  326. package/dist/core/entities/event/index.mjs +2 -2
  327. package/dist/core/entities/event/service.mjs +1 -1
  328. package/dist/core/entities/event/service.mjs.map +1 -1
  329. package/dist/core/entities/index.mjs +4 -3
  330. package/dist/core/entities/role/schema.d.ts.map +1 -1
  331. package/dist/core/entities/role/schema.mjs +4 -0
  332. package/dist/core/entities/role/schema.mjs.map +1 -1
  333. package/dist/core/entities/session/schema.d.ts.map +1 -1
  334. package/dist/core/entities/session/schema.mjs +2 -0
  335. package/dist/core/entities/session/schema.mjs.map +1 -1
  336. package/dist/core/entities/user/schema.d.ts.map +1 -1
  337. package/dist/core/entities/user/schema.mjs +2 -0
  338. package/dist/core/entities/user/schema.mjs.map +1 -1
  339. package/dist/core/identity/password-recovery/check.mjs +4 -4
  340. package/dist/core/identity/password-recovery/check.mjs.map +1 -1
  341. package/dist/core/identity/registration/check.mjs +2 -2
  342. package/dist/core/identity/registration/check.mjs.map +1 -1
  343. package/dist/core/index.mjs +5 -3
  344. package/dist/core/key/check.mjs +2 -2
  345. package/dist/core/key/check.mjs.map +1 -1
  346. package/dist/core/provisioning/entities/client/relations-validator.d.ts.map +1 -1
  347. package/dist/core/provisioning/entities/client/relations-validator.mjs +2 -0
  348. package/dist/core/provisioning/entities/client/relations-validator.mjs.map +1 -1
  349. package/dist/core/provisioning/entities/client/types.d.ts +8 -0
  350. package/dist/core/provisioning/entities/client/types.d.ts.map +1 -1
  351. package/dist/core/provisioning/synchronizer/client/module.d.ts +3 -1
  352. package/dist/core/provisioning/synchronizer/client/module.d.ts.map +1 -1
  353. package/dist/core/provisioning/synchronizer/client/module.mjs +10 -0
  354. package/dist/core/provisioning/synchronizer/client/module.mjs.map +1 -1
  355. package/dist/core/provisioning/synchronizer/client/types.d.ts +3 -1
  356. package/dist/core/provisioning/synchronizer/client/types.d.ts.map +1 -1
  357. package/dist/core/provisioning/synchronizer/entity-resolver.d.ts +4 -1
  358. package/dist/core/provisioning/synchronizer/entity-resolver.d.ts.map +1 -1
  359. package/dist/core/provisioning/synchronizer/entity-resolver.mjs +14 -29
  360. package/dist/core/provisioning/synchronizer/entity-resolver.mjs.map +1 -1
  361. package/dist/core/provisioning/synchronizer/index.d.ts +1 -0
  362. package/dist/core/provisioning/synchronizer/index.d.ts.map +1 -1
  363. package/dist/core/provisioning/synchronizer/realm/module.d.ts.map +1 -1
  364. package/dist/core/provisioning/synchronizer/realm/module.mjs +8 -8
  365. package/dist/core/provisioning/synchronizer/realm/module.mjs.map +1 -1
  366. package/dist/core/provisioning/synchronizer/types.d.ts +11 -0
  367. package/dist/core/provisioning/synchronizer/types.d.ts.map +1 -0
  368. package/dist/core/provisioning/synchronizer/types.mjs +4 -0
  369. package/dist/core/query/describe.d.ts +25 -0
  370. package/dist/core/query/describe.d.ts.map +1 -0
  371. package/dist/core/query/describe.mjs +50 -0
  372. package/dist/core/query/describe.mjs.map +1 -0
  373. package/dist/core/query/fields.d.ts +44 -0
  374. package/dist/core/query/fields.d.ts.map +1 -0
  375. package/dist/core/query/fields.mjs +46 -0
  376. package/dist/core/query/fields.mjs.map +1 -0
  377. package/dist/core/query/index.d.ts +1 -0
  378. package/dist/core/query/index.d.ts.map +1 -1
  379. package/dist/core/query/index.mjs +2 -1
  380. package/dist/core/query/module.d.ts +1 -1
  381. package/dist/core/query/module.mjs +1 -1
  382. package/dist/core/query/module.mjs.map +1 -1
  383. package/dist/index.mjs +6 -2
  384. package/dist/swagger.json +852 -145
  385. package/dist/ui/client/.vite/ssr-manifest.json +862 -754
  386. package/dist/ui/client/assets/index-Bk6-OZy9.css +2 -0
  387. package/dist/ui/client/assets/index-DO6_7NTJ.js +106 -0
  388. package/dist/ui/client/index.html +2 -2
  389. package/dist/ui/server/server.js +152569 -156411
  390. package/package.json +40 -40
  391. package/dist/cli/commands/reset.d.ts +0 -2
  392. package/dist/cli/commands/reset.d.ts.map +0 -1
  393. package/dist/cli/commands/reset.mjs +0 -23
  394. package/dist/cli/commands/reset.mjs.map +0 -1
  395. package/dist/components/database/index.d.ts +0 -2
  396. package/dist/components/database/index.d.ts.map +0 -1
  397. package/dist/components/database/index.mjs +0 -7
  398. package/dist/components/database/unique-entries/factory.d.ts +0 -4
  399. package/dist/components/database/unique-entries/factory.d.ts.map +0 -1
  400. package/dist/components/database/unique-entries/factory.mjs +0 -12
  401. package/dist/components/database/unique-entries/factory.mjs.map +0 -1
  402. package/dist/components/database/unique-entries/index.d.ts +0 -3
  403. package/dist/components/database/unique-entries/index.d.ts.map +0 -1
  404. package/dist/components/database/unique-entries/index.mjs +0 -6
  405. package/dist/components/database/unique-entries/module.d.ts +0 -15
  406. package/dist/components/database/unique-entries/module.d.ts.map +0 -1
  407. package/dist/components/database/unique-entries/module.mjs +0 -69
  408. package/dist/components/database/unique-entries/module.mjs.map +0 -1
  409. package/dist/ui/client/assets/index-CiHrf73J.css +0 -2
  410. package/dist/ui/client/assets/index-mtJngmng.js +0 -131
@@ -1,6 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import "../../entities/event/constants.mjs";
4
5
  import { EventName } from "@authup/core-kit";
5
6
  import { LoginThrottledError } from "@authup/errors";
6
7
  //#region src/core/authentication/login-throttle/service.ts
@@ -20,7 +21,7 @@ var LoginThrottleService = class {
20
21
  const windowSeconds = this.options.windowSeconds ?? DEFAULT_WINDOW_SECONDS;
21
22
  if (await this.repository.countRecent({
22
23
  name: EventName.LOGIN_FAILED,
23
- actorName: ctx.identifier,
24
+ actorName: ctx.identifier.slice(0, 128),
24
25
  requestIpAddress: ctx.ipAddress,
25
26
  realmId: ctx.realmId,
26
27
  since: (/* @__PURE__ */ new Date(Date.now() - windowSeconds * 1e3)).toISOString()
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":["EventName","LoginThrottledError","DEFAULT_THRESHOLD","DEFAULT_WINDOW_SECONDS","LoginThrottleService","repository","options","ctx","assertNotThrottled","enabled","ipAddress","threshold","windowSeconds","count","countRecent","name","LOGIN_FAILED","actorName","identifier","requestIpAddress","realmId","since","Date","now","toISOString","retryAfter"],"sources":["../../../../src/core/authentication/login-throttle/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { EventName } from '@authup/core-kit';\nimport { LoginThrottledError } from '@authup/errors';\nimport type { IEventRepository } from '../../entities/index.ts';\nimport type { ILoginThrottleService, LoginThrottleServiceContext, LoginThrottleServiceOptions } from './types.ts';\n\nconst DEFAULT_THRESHOLD = 5;\nconst DEFAULT_WINDOW_SECONDS = 900;\n\nexport class LoginThrottleService implements ILoginThrottleService {\n protected repository: IEventRepository;\n\n protected options: LoginThrottleServiceOptions;\n\n constructor(ctx: LoginThrottleServiceContext) {\n this.repository = ctx.repository;\n this.options = ctx.options ?? {};\n }\n\n async assertNotThrottled(\n ctx: {\n identifier: string, \n ipAddress?: string, \n realmId?: string | null \n },\n ): Promise<void> {\n if (!this.options.enabled) {\n return;\n }\n\n // The (identifier, ip) pair is the account-lockout-DoS mitigation: an\n // attacker on one IP can never lock the victim's other IPs. Without a\n // derivable IP the pair does not exist, so the throttle fails open\n // instead of degrading to a lockable per-identifier key.\n if (!ctx.ipAddress) {\n return;\n }\n\n const threshold = this.options.threshold ?? DEFAULT_THRESHOLD;\n const windowSeconds = this.options.windowSeconds ?? DEFAULT_WINDOW_SECONDS;\n\n const count = await this.repository.countRecent({\n name: EventName.LOGIN_FAILED,\n actorName: ctx.identifier,\n requestIpAddress: ctx.ipAddress,\n realmId: ctx.realmId,\n since: new Date(Date.now() - (windowSeconds * 1_000)).toISOString(),\n });\n\n if (count >= threshold) {\n throw new LoginThrottledError({ retryAfter: windowSeconds });\n }\n }\n}\n"],"mappings":";;;;;;AAYA,MAAME,oBAAoB;AAC1B,MAAMC,yBAAyB;AAE/B,IAAaC,uBAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYC,KAAkC;EAC1C,KAAKF,aAAaE,IAAIF;EACtB,KAAKC,UAAUC,IAAID,WAAW,CAAC;CACnC;CAEA,MAAME,mBACFD,KAKa;EACb,IAAI,CAAC,KAAKD,QAAQG,SACd;EAOJ,IAAI,CAACF,IAAIG,WACL;EAGJ,MAAMC,YAAY,KAAKL,QAAQK,aAAaT;EAC5C,MAAMU,gBAAgB,KAAKN,QAAQM,iBAAiBT;EAUpD,IAAIU,MARgB,KAAKR,WAAWS,YAAY;GAC5CC,MAAMf,UAAUgB;GAChBC,WAAWV,IAAIW;GACfC,kBAAkBZ,IAAIG;GACtBU,SAASb,IAAIa;GACbC,wBAAO,IAAIC,KAAKA,KAAKC,IAAG,IAAMX,gBAAgB,GAAA,EAAA,CAAQY,YAAW;EACrE,CAAA,KAEab,WACT,MAAM,IAAIV,oBAAoB,EAAEwB,YAAYb,cAAc,CAAA;CAElE;AACJ"}
1
+ {"version":3,"file":"service.mjs","names":["EventName","LoginThrottledError","EVENT_ACTOR_NAME_MAX_LENGTH","DEFAULT_THRESHOLD","DEFAULT_WINDOW_SECONDS","LoginThrottleService","repository","options","ctx","assertNotThrottled","enabled","ipAddress","threshold","windowSeconds","count","countRecent","name","LOGIN_FAILED","actorName","identifier","slice","requestIpAddress","realmId","since","Date","now","toISOString","retryAfter"],"sources":["../../../../src/core/authentication/login-throttle/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { EventName } from '@authup/core-kit';\nimport { LoginThrottledError } from '@authup/errors';\n// direct file import — the entities barrel reaches every entity service, and a\n// value import of it from here would pull that graph in at module init.\nimport { EVENT_ACTOR_NAME_MAX_LENGTH } from '../../entities/event/constants.ts';\nimport type { IEventRepository } from '../../entities/index.ts';\nimport type { ILoginThrottleService, LoginThrottleServiceContext, LoginThrottleServiceOptions } from './types.ts';\n\nconst DEFAULT_THRESHOLD = 5;\nconst DEFAULT_WINDOW_SECONDS = 900;\n\nexport class LoginThrottleService implements ILoginThrottleService {\n protected repository: IEventRepository;\n\n protected options: LoginThrottleServiceOptions;\n\n constructor(ctx: LoginThrottleServiceContext) {\n this.repository = ctx.repository;\n this.options = ctx.options ?? {};\n }\n\n async assertNotThrottled(\n ctx: {\n identifier: string, \n ipAddress?: string, \n realmId?: string | null \n },\n ): Promise<void> {\n if (!this.options.enabled) {\n return;\n }\n\n // The (identifier, ip) pair is the account-lockout-DoS mitigation: an\n // attacker on one IP can never lock the victim's other IPs. Without a\n // derivable IP the pair does not exist, so the throttle fails open\n // instead of degrading to a lockable per-identifier key.\n if (!ctx.ipAddress) {\n return;\n }\n\n const threshold = this.options.threshold ?? DEFAULT_THRESHOLD;\n const windowSeconds = this.options.windowSeconds ?? DEFAULT_WINDOW_SECONDS;\n\n const count = await this.repository.countRecent({\n name: EventName.LOGIN_FAILED,\n // the loginFailed rows are persisted with the actor name truncated\n // to the column bound — an untruncated key would never match its\n // own rows, silently failing open for over-long identifiers.\n actorName: ctx.identifier.slice(0, EVENT_ACTOR_NAME_MAX_LENGTH),\n requestIpAddress: ctx.ipAddress,\n realmId: ctx.realmId,\n since: new Date(Date.now() - (windowSeconds * 1_000)).toISOString(),\n });\n\n if (count >= threshold) {\n throw new LoginThrottledError({ retryAfter: windowSeconds });\n }\n }\n}\n"],"mappings":";;;;;;;AAeA,MAAMG,oBAAoB;AAC1B,MAAMC,yBAAyB;AAE/B,IAAaC,uBAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYC,KAAkC;EAC1C,KAAKF,aAAaE,IAAIF;EACtB,KAAKC,UAAUC,IAAID,WAAW,CAAC;CACnC;CAEA,MAAME,mBACFD,KAKa;EACb,IAAI,CAAC,KAAKD,QAAQG,SACd;EAOJ,IAAI,CAACF,IAAIG,WACL;EAGJ,MAAMC,YAAY,KAAKL,QAAQK,aAAaT;EAC5C,MAAMU,gBAAgB,KAAKN,QAAQM,iBAAiBT;EAapD,IAAIU,MAXgB,KAAKR,WAAWS,YAAY;GAC5CC,MAAMhB,UAAUiB;GAIhBC,WAAWV,IAAIW,WAAWC,MAAM,GAAA,GAAGlB;GACnCmB,kBAAkBb,IAAIG;GACtBW,SAASd,IAAIc;GACbC,wBAAO,IAAIC,KAAKA,KAAKC,IAAG,IAAMZ,gBAAgB,GAAA,EAAA,CAAQa,YAAW;EACrE,CAAA,KAEad,WACT,MAAM,IAAIX,oBAAoB,EAAE0B,YAAYd,cAAc,CAAA;CAElE;AACJ"}
@@ -0,0 +1,10 @@
1
+ import { PermissionName } from '@authup/core-kit';
2
+ /**
3
+ * The client read-permission disjunction: holding ANY of these grants
4
+ * read access to client rows. Shared between `ClientService`'s
5
+ * getMany/getOne pre-gates and the schema-level `secret` visibility
6
+ * gate (issue #3322) — the two MUST evaluate the same disjunction, or
7
+ * listability and secret visibility silently diverge.
8
+ */
9
+ export declare const CLIENT_READ_PERMISSIONS: PermissionName[];
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/constants.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAG,cAAc,EAIpD,CAAC"}
@@ -0,0 +1,20 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { PermissionName } from "@authup/core-kit";
5
+ //#region src/core/entities/client/constants.ts
6
+ /**
7
+ * The client read-permission disjunction: holding ANY of these grants
8
+ * read access to client rows. Shared between `ClientService`'s
9
+ * getMany/getOne pre-gates and the schema-level `secret` visibility
10
+ * gate (issue #3322) — the two MUST evaluate the same disjunction, or
11
+ * listability and secret visibility silently diverge.
12
+ */ const CLIENT_READ_PERMISSIONS = [
13
+ PermissionName.CLIENT_READ,
14
+ PermissionName.CLIENT_UPDATE,
15
+ PermissionName.CLIENT_DELETE
16
+ ];
17
+ //#endregion
18
+ export { CLIENT_READ_PERMISSIONS };
19
+
20
+ //# sourceMappingURL=constants.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.mjs","names":["PermissionName","CLIENT_READ_PERMISSIONS","CLIENT_READ","CLIENT_UPDATE","CLIENT_DELETE"],"sources":["../../../../src/core/entities/client/constants.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { PermissionName } from '@authup/core-kit';\n\n/**\n * The client read-permission disjunction: holding ANY of these grants\n * read access to client rows. Shared between `ClientService`'s\n * getMany/getOne pre-gates and the schema-level `secret` visibility\n * gate (issue #3322) — the two MUST evaluate the same disjunction, or\n * listability and secret visibility silently diverge.\n */\nexport const CLIENT_READ_PERMISSIONS : PermissionName[] = [\n PermissionName.CLIENT_READ,\n PermissionName.CLIENT_UPDATE,\n PermissionName.CLIENT_DELETE,\n];\n"],"mappings":";;;;;;;;;;;GAgBA,MAAaC,0BAA6C;CACtDD,eAAeE;CACfF,eAAeG;CACfH,eAAeI;AAClB"}
@@ -1,3 +1,4 @@
1
+ export * from './constants.ts';
1
2
  export * from './service.ts';
2
3
  export * from './types.ts';
3
4
  export * from './web-client.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/index.ts"],"names":[],"mappings":"AAOA,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/index.ts"],"names":[],"mappings":"AAOA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { CLIENT_READ_PERMISSIONS } from "./constants.mjs";
4
5
  import { clientSchema } from "./schema.mjs";
5
6
  import { ClientService } from "./service.mjs";
6
- import { WebClientProvisioner, buildWebClientAttributes } from "./web-client.mjs";
7
- export { ClientService, WebClientProvisioner, buildWebClientAttributes, clientSchema };
7
+ import { WEB_CLIENT_SCOPE_NAMES, WebClientProvisioner, buildWebClientAttributes } from "./web-client.mjs";
8
+ export { CLIENT_READ_PERMISSIONS, ClientService, WEB_CLIENT_SCOPE_NAMES, WebClientProvisioner, buildWebClientAttributes, clientSchema };
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM/C,eAAO,MAAM,YAAY,2CAgCvB,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/schema.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2D/C,eAAO,MAAM,YAAY,2CAiCvB,CAAC"}
@@ -1,14 +1,50 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { CLIENT_READ_PERMISSIONS } from "./constants.mjs";
5
+ import { createFieldsReadGate } from "../../query/fields.mjs";
4
6
  import { createRelationsReadGate } from "../../query/relations.mjs";
5
- import { EntityType } from "@authup/core-kit";
6
- import { defineSchema } from "@rapiq/core";
7
+ import { EntityType, IdentityType } from "@authup/core-kit";
8
+ import { and, defineSchema, eq, ne, or } from "@rapiq/core";
7
9
  //#region src/core/entities/client/schema.ts
8
10
  const schemaMapping = {
9
11
  realm: EntityType.REALM,
10
12
  accessPolicy: EntityType.POLICY
11
13
  };
14
+ /**
15
+ * Per-row visibility gate for the `secret` column (issue #3322) —
16
+ * schema-level, so it applies wherever the client schema governs a
17
+ * projection: the `/clients` root AND the `include=client` paths of
18
+ * client-permission / client-role / client-scope, which never run
19
+ * `ClientService`'s read path.
20
+ *
21
+ * Only rows exposing a PLAINTEXT secret are guarded: `null`, hashed
22
+ * and encrypted values stay visible to any actor that passed the read
23
+ * pre-gate (the historical service semantics). A plaintext value is
24
+ * additionally visible on the actor's OWN client row (the `getOne`
25
+ * isMe bypass, list-shaped) and on rows the compiled permission
26
+ * condition covers. A non-expressible (`post`) or settled-deny compile
27
+ * yields no permission leg — fail closed for plaintext rows rather
28
+ * than falling back to per-row evaluation.
29
+ *
30
+ * The outer `ne('realmId', null)` guard is semantically inert (clients
31
+ * are realm-bound) but makes the condition fail closed on rows whose
32
+ * gate columns were not fetched — an `ownOrNull` reach compiles to a
33
+ * null-inclusive realm leg that would otherwise MATCH a missing
34
+ * column. The SQL adapter force-selects every column the condition
35
+ * reads (rapiq#830's operand projection), so this is defense in depth.
36
+ */ async function secretReadGate(actor) {
37
+ const compiled = await actor.permissionEvaluator.compile({ name: CLIENT_READ_PERMISSIONS });
38
+ if (compiled.verdict === "allow") return true;
39
+ const visible = [
40
+ eq("secret", null),
41
+ eq("secretHashed", true),
42
+ eq("secretEncrypted", true)
43
+ ];
44
+ if (compiled.verdict === "conditional") visible.push(compiled.condition);
45
+ if (actor.identity && actor.identity.type === IdentityType.CLIENT && actor.identity.data.id) visible.push(eq("id", actor.identity.data.id));
46
+ return and(ne("realmId", null), or(...visible));
47
+ }
12
48
  const clientSchema = defineSchema({
13
49
  name: EntityType.CLIENT,
14
50
  fields: {
@@ -34,7 +70,8 @@ const clientSchema = defineSchema({
34
70
  "updatedAt",
35
71
  "createdAt"
36
72
  ],
37
- allowed: ["secret"]
73
+ allowed: ["secret"],
74
+ validateMany: createFieldsReadGate({ secret: secretReadGate })
38
75
  },
39
76
  filters: { allowed: [
40
77
  "id",
@@ -1 +1 @@
1
- {"version":3,"file":"schema.mjs","names":["defineSchema","EntityType","createRelationsReadGate","schemaMapping","realm","REALM","accessPolicy","POLICY","clientSchema","name","CLIENT","fields","default","allowed","filters","relations","validate","sort","pagination","maxLimit"],"sources":["../../../../src/core/entities/client/schema.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { defineSchema } from '@rapiq/core';\nimport type { Client } from '@authup/core-kit';\nimport { EntityType } from '@authup/core-kit';\nimport { createRelationsReadGate } from '../../query/relations.ts';\n\nconst schemaMapping = { realm: EntityType.REALM, accessPolicy: EntityType.POLICY };\n\nexport const clientSchema = defineSchema<Client>({\n name: EntityType.CLIENT,\n fields: {\n default: [\n 'id',\n 'active',\n 'builtIn',\n 'name',\n 'displayName',\n 'description',\n 'secretHashed',\n 'secretEncrypted',\n 'baseUrl',\n 'rootUrl',\n 'redirectUri',\n 'postLogoutRedirectUri',\n 'grantTypes',\n 'scope',\n 'authMethod',\n 'tokenBindingMethod',\n 'accessPolicyId',\n 'realmId',\n 'updatedAt',\n 'createdAt',\n ],\n allowed: ['secret'],\n },\n filters: { allowed: ['id', 'name', 'realmId'] },\n relations: { allowed: ['realm', 'accessPolicy'], validate: createRelationsReadGate(schemaMapping) },\n sort: { allowed: ['id', 'createdAt', 'updatedAt'] },\n pagination: { maxLimit: 50 },\n schemaMapping,\n});\n"],"mappings":";;;;;;;AAYA,MAAMG,gBAAgB;CAAEC,OAAOH,WAAWI;CAAOC,cAAcL,WAAWM;AAAO;AAEjF,MAAaC,eAAeR,aAAqB;CAC7CS,MAAMR,WAAWS;CACjBC,QAAQ;EACJC,SAAS;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACH;EACDC,SAAS,CAAC,QAAS;CACvB;CACAC,SAAS,EAAED,SAAS;EAAC;EAAM;EAAQ;CAAU,EAAC;CAC9CE,WAAW;EAAEF,SAAS,CAAC,SAAS,cAAe;EAAEG,UAAUd,wBAAwBC,aAAAA;CAAe;CAClGc,MAAM,EAAEJ,SAAS;EAAC;EAAM;EAAa;CAAY,EAAC;CAClDK,YAAY,EAAEC,UAAU,GAAG;CAC3BhB;AACJ,CAAA"}
1
+ {"version":3,"file":"schema.mjs","names":["and","defineSchema","eq","ne","or","EntityType","IdentityType","createFieldsReadGate","createRelationsReadGate","CLIENT_READ_PERMISSIONS","schemaMapping","realm","REALM","accessPolicy","POLICY","secretReadGate","actor","compiled","permissionEvaluator","compile","name","verdict","visible","push","condition","identity","type","CLIENT","data","id","clientSchema","fields","default","allowed","validateMany","secret","filters","relations","validate","sort","pagination","maxLimit"],"sources":["../../../../src/core/entities/client/schema.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ICondition, KeyValidationVerdict } from '@rapiq/core';\nimport {\n and,\n defineSchema,\n eq,\n ne,\n or,\n} from '@rapiq/core';\nimport type { Client } from '@authup/core-kit';\nimport { EntityType, IdentityType } from '@authup/core-kit';\nimport type { ActorContext } from '@authup/server-kit';\nimport { createFieldsReadGate } from '../../query/fields.ts';\nimport { createRelationsReadGate } from '../../query/relations.ts';\nimport { CLIENT_READ_PERMISSIONS } from './constants.ts';\n\nconst schemaMapping = { realm: EntityType.REALM, accessPolicy: EntityType.POLICY };\n\n/**\n * Per-row visibility gate for the `secret` column (issue #3322) —\n * schema-level, so it applies wherever the client schema governs a\n * projection: the `/clients` root AND the `include=client` paths of\n * client-permission / client-role / client-scope, which never run\n * `ClientService`'s read path.\n *\n * Only rows exposing a PLAINTEXT secret are guarded: `null`, hashed\n * and encrypted values stay visible to any actor that passed the read\n * pre-gate (the historical service semantics). A plaintext value is\n * additionally visible on the actor's OWN client row (the `getOne`\n * isMe bypass, list-shaped) and on rows the compiled permission\n * condition covers. A non-expressible (`post`) or settled-deny compile\n * yields no permission leg — fail closed for plaintext rows rather\n * than falling back to per-row evaluation.\n *\n * The outer `ne('realmId', null)` guard is semantically inert (clients\n * are realm-bound) but makes the condition fail closed on rows whose\n * gate columns were not fetched — an `ownOrNull` reach compiles to a\n * null-inclusive realm leg that would otherwise MATCH a missing\n * column. The SQL adapter force-selects every column the condition\n * reads (rapiq#830's operand projection), so this is defense in depth.\n */\nasync function secretReadGate(actor: ActorContext) : Promise<KeyValidationVerdict> {\n const compiled = await actor.permissionEvaluator.compile({ name: CLIENT_READ_PERMISSIONS });\n if (compiled.verdict === 'allow') {\n return true;\n }\n\n const visible : ICondition[] = [\n eq('secret', null),\n eq('secretHashed', true),\n eq('secretEncrypted', true),\n ];\n\n if (compiled.verdict === 'conditional') {\n visible.push(compiled.condition);\n }\n\n if (\n actor.identity &&\n actor.identity.type === IdentityType.CLIENT &&\n actor.identity.data.id\n ) {\n visible.push(eq('id', actor.identity.data.id));\n }\n\n return and(ne('realmId', null), or(...visible));\n}\n\nexport const clientSchema = defineSchema<Client>({\n name: EntityType.CLIENT,\n fields: {\n default: [\n 'id',\n 'active',\n 'builtIn',\n 'name',\n 'displayName',\n 'description',\n 'secretHashed',\n 'secretEncrypted',\n 'baseUrl',\n 'rootUrl',\n 'redirectUri',\n 'postLogoutRedirectUri',\n 'grantTypes',\n 'scope',\n 'authMethod',\n 'tokenBindingMethod',\n 'accessPolicyId',\n 'realmId',\n 'updatedAt',\n 'createdAt',\n ],\n allowed: ['secret'],\n validateMany: createFieldsReadGate({ secret: secretReadGate }),\n },\n filters: { allowed: ['id', 'name', 'realmId'] },\n relations: { allowed: ['realm', 'accessPolicy'], validate: createRelationsReadGate(schemaMapping) },\n sort: { allowed: ['id', 'createdAt', 'updatedAt'] },\n pagination: { maxLimit: 50 },\n schemaMapping,\n});\n"],"mappings":";;;;;;;;;AAsBA,MAAMU,gBAAgB;CAAEC,OAAON,WAAWO;CAAOC,cAAcR,WAAWS;AAAO;;;;;;;;;;;;;;;;;;;;;;;GAyBjF,eAAeC,eAAeC,OAAmB;CAC7C,MAAMC,WAAW,MAAMD,MAAME,oBAAoBC,QAAQ,EAAEC,MAAMX,wBAAwB,CAAA;CACzF,IAAIQ,SAASI,YAAY,SACrB,OAAO;CAGX,MAAMC,UAAyB;EAC3BpB,GAAG,UAAU,IAAA;EACbA,GAAG,gBAAgB,IAAA;EACnBA,GAAG,mBAAmB,IAAA;CACzB;CAED,IAAIe,SAASI,YAAY,eACrBC,QAAQC,KAAKN,SAASO,SAAS;CAGnC,IACIR,MAAMS,YACNT,MAAMS,SAASC,SAASpB,aAAaqB,UACrCX,MAAMS,SAASG,KAAKC,IAEpBP,QAAQC,KAAKrB,GAAG,MAAMc,MAAMS,SAASG,KAAKC,EAAE,CAAA;CAGhD,OAAO7B,IAAIG,GAAG,WAAW,IAAA,GAAOC,GAAAA,GAAMkB,OAAAA,CAAAA;AAC1C;AAEA,MAAaQ,eAAe7B,aAAqB;CAC7CmB,MAAMf,WAAWsB;CACjBI,QAAQ;EACJC,SAAS;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACH;EACDC,SAAS,CAAC,QAAS;EACnBC,cAAc3B,qBAAqB,EAAE4B,QAAQpB,eAAe,CAAA;CAChE;CACAqB,SAAS,EAAEH,SAAS;EAAC;EAAM;EAAQ;CAAU,EAAC;CAC9CI,WAAW;EAAEJ,SAAS,CAAC,SAAS,cAAe;EAAEK,UAAU9B,wBAAwBE,aAAAA;CAAe;CAClG6B,MAAM,EAAEN,SAAS;EAAC;EAAM;EAAa;CAAY,EAAC;CAClDO,YAAY,EAAEC,UAAU,GAAG;CAC3B/B;AACJ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/service.ts"],"names":[],"mappings":"AAiBA,OAAO,EAGH,eAAe,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAG,MAAM,oBAAoB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIpE,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,eAAe,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,SAAQ,qBAAsB,YAAW,cAAc;IAC9E,SAAS,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAExC,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,SAAS,EAAE,eAAe,CAAC;gBAEzB,GAAG,EAAE,oBAAoB;IAO/B,OAAO,CACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IAmF5C,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IAmDZ,MAAM,CACR,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,IAAI,CACN,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC;IA2JI,MAAM,CACR,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;CAmBrB"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/service.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,eAAe,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAG,MAAM,oBAAoB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKpE,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,eAAe,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,SAAQ,qBAAsB,YAAW,cAAc;IAC9E,SAAS,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAExC,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,SAAS,EAAE,eAAe,CAAC;gBAEzB,GAAG,EAAE,oBAAoB;IAO/B,OAAO,CACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IAY5C,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA6CZ,MAAM,CACR,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,IAAI,CACN,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC;IA2JI,MAAM,CACR,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;CAmBrB"}
@@ -2,15 +2,15 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { ClientCredentialsService } from "../../authentication/credential/entities/client/module.mjs";
5
+ import { CLIENT_READ_PERMISSIONS } from "./constants.mjs";
5
6
  import { clientSchema } from "./schema.mjs";
6
- import { appendQueryConditions, decodeQuery } from "../../query/module.mjs";
7
+ import { decodeQuery } from "../../query/module.mjs";
7
8
  import "../../query/index.mjs";
8
9
  import { ValidatorGroup, isUUID } from "@authup/kit";
9
10
  import { CLIENT_RESERVED_NAMES, ClientAuthMethod, ClientValidator, PermissionName } from "@authup/core-kit";
10
11
  import { AbstractEntityService } from "@authup/server-kit";
11
12
  import { BuiltInPolicyType, definePolicyData } from "@authup/access";
12
13
  import { EntityNotFoundError, ValidationError } from "@authup/errors";
13
- import { and, eq, ne, not, or } from "@rapiq/core";
14
14
  //#region src/core/entities/client/service.ts
15
15
  var ClientService = class extends AbstractEntityService {
16
16
  repository;
@@ -23,67 +23,15 @@ var ClientService = class extends AbstractEntityService {
23
23
  this.validator = new ClientValidator();
24
24
  }
25
25
  async getMany(query, actor) {
26
- const permissionNames = [
27
- PermissionName.CLIENT_READ,
28
- PermissionName.CLIENT_UPDATE,
29
- PermissionName.CLIENT_DELETE
30
- ];
31
- await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });
32
- const parsed = await decodeQuery(query, {
26
+ await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });
27
+ return this.repository.findMany(await decodeQuery(query, {
33
28
  schema: clientSchema,
34
29
  actor
35
- });
36
- const secretSelected = parsed.fields.value.some((field) => field.name === "secret" && field.operator !== "-");
37
- const compiled = await actor.permissionEvaluator.compile({ name: permissionNames });
38
- if (compiled.verdict !== "post") {
39
- let scoped = parsed;
40
- if (secretSelected && compiled.verdict !== "allow") {
41
- const plaintextSecret = and(ne("secret", null), eq("secretHashed", false), eq("secretEncrypted", false));
42
- scoped = appendQueryConditions(parsed, compiled.verdict === "conditional" ? or(not(plaintextSecret), compiled.condition) : not(plaintextSecret));
43
- }
44
- return this.repository.findMany(scoped);
45
- }
46
- const { data: entities, meta } = await this.repository.findMany(parsed);
47
- let { total } = meta;
48
- const data = [];
49
- for (const entity of entities) {
50
- if (entity.secret && !entity.secretEncrypted && !entity.secretHashed) {
51
- try {
52
- await actor.permissionEvaluator.evaluateOneOf({
53
- name: [
54
- PermissionName.CLIENT_READ,
55
- PermissionName.CLIENT_UPDATE,
56
- PermissionName.CLIENT_DELETE
57
- ],
58
- data: definePolicyData({
59
- [BuiltInPolicyType.ATTRIBUTES]: entity,
60
- ...this.resourceRealmMatch(entity)
61
- })
62
- });
63
- data.push(entity);
64
- } catch {
65
- total -= 1;
66
- }
67
- continue;
68
- }
69
- data.push(entity);
70
- }
71
- return {
72
- data,
73
- meta: {
74
- ...meta,
75
- total
76
- }
77
- };
30
+ }));
78
31
  }
79
32
  async getOne(idOrName, actor, query, realmId) {
80
- const permissionNames = [
81
- PermissionName.CLIENT_READ,
82
- PermissionName.CLIENT_UPDATE,
83
- PermissionName.CLIENT_DELETE
84
- ];
85
33
  let isMe = !!actor.identity && actor.identity.type === "client" && (actor.identity.data.id === idOrName || actor.identity.data.name === idOrName);
86
- if (!isMe) await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });
34
+ if (!isMe) await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });
87
35
  const entity = await this.repository.findOne(idOrName, await decodeQuery(query, {
88
36
  schema: clientSchema,
89
37
  parameters: ["fields", "relations"],
@@ -92,10 +40,10 @@ var ClientService = class extends AbstractEntityService {
92
40
  if (!entity) throw new EntityNotFoundError();
93
41
  if (isMe && actor.identity.data.id !== entity.id) {
94
42
  isMe = false;
95
- await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });
43
+ await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });
96
44
  }
97
45
  if (!isMe && entity.secret && !entity.secretEncrypted && !entity.secretHashed) await actor.permissionEvaluator.evaluateOneOf({
98
- name: permissionNames,
46
+ name: CLIENT_READ_PERMISSIONS,
99
47
  data: definePolicyData({
100
48
  [BuiltInPolicyType.ATTRIBUTES]: entity,
101
49
  ...this.resourceRealmMatch(entity)
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":["BuiltInPolicyType","definePolicyData","and","eq","ne","not","or","ValidatorGroup","isUUID","EntityNotFoundError","ValidationError","CLIENT_RESERVED_NAMES","ClientAuthMethod","ClientValidator","PermissionName","AbstractEntityService","ClientCredentialsService","appendQueryConditions","decodeQuery","clientSchema","ClientService","repository","realmRepository","validator","ctx","getMany","query","actor","permissionNames","CLIENT_READ","CLIENT_UPDATE","CLIENT_DELETE","permissionEvaluator","preEvaluateOneOf","name","parsed","schema","secretSelected","fields","value","some","field","operator","compiled","compile","verdict","scoped","plaintextSecret","condition","findMany","data","entities","meta","total","entity","secret","secretEncrypted","secretHashed","evaluateOneOf","ATTRIBUTES","resourceRealmMatch","push","getOne","idOrName","realmId","isMe","identity","type","id","findOne","parameters","create","save","undefined","update","updateOnly","options","group","realm","resolve","where","findOneWithSecret","isSelfEdit","preEvaluate","e","CLIENT_SELF_MANAGE","UPDATE","CLIENT_CREATE","CREATE","validated","run","includes","builtIn","validateJoinColumns","checkUniqueness","credentialsService","actorRealmId","getActorRealmId","evaluate","merge","authMethod","SECRET","generateSecret","protect","created","delete","findOneBy","entityId","remove"],"sources":["../../../../src/core/entities/client/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport {\n and, \n eq, \n ne, \n not, \n or,\n} from '@rapiq/core';\nimport { ValidatorGroup, isUUID } from '@authup/kit';\nimport { EntityNotFoundError, ValidationError } from '@authup/errors';\nimport {\n CLIENT_RESERVED_NAMES,\n ClientAuthMethod,\n ClientValidator,\n PermissionName,\n} from '@authup/core-kit';\nimport type { Client } from '@authup/core-kit';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { IRealmRepository } from '../realm/types.ts';\nimport { AbstractEntityService } from '@authup/server-kit';\nimport { ClientCredentialsService } from '../../authentication/credential/entities/client/module.ts';\nimport type { IClientRepository, IClientService } from './types.ts';\nimport { appendQueryConditions, decodeQuery } from '../../query/index.ts';\nimport { clientSchema } from './schema.ts';\n\nexport type ClientServiceContext = {\n repository: IClientRepository;\n realmRepository: IRealmRepository;\n};\n\nexport class ClientService extends AbstractEntityService implements IClientService {\n protected repository: IClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected validator: ClientValidator;\n\n constructor(ctx: ClientServiceContext) {\n super();\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.validator = new ClientValidator();\n }\n\n async getMany(\n query: Record<string, any>,\n actor: ActorContext,\n ): Promise<EntityRepositoryFindManyResult<Client>> {\n const permissionNames = [\n PermissionName.CLIENT_READ,\n PermissionName.CLIENT_UPDATE,\n PermissionName.CLIENT_DELETE,\n ];\n\n await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });\n\n const parsed = await decodeQuery(query, { schema: clientSchema, actor });\n\n // The per-row gate below only fires for rows exposing a PLAINTEXT secret,\n // and only when the projection actually selects the (non-default) `secret`\n // field — everything else lists freely. Compile the gate into WHERE\n // (#3286 phase 3): a plaintext-secret row is visible iff the compiled\n // permission condition covers it; rows without a plaintext secret always\n // are. A non-expressible policy falls back to the per-row loop below.\n const secretSelected = parsed.fields.value.some(\n (field) => field.name === 'secret' && field.operator !== '-',\n );\n const compiled = await actor.permissionEvaluator.compile({ name: permissionNames });\n if (compiled.verdict !== 'post') {\n let scoped = parsed;\n if (secretSelected && compiled.verdict !== 'allow') {\n const plaintextSecret = and(\n ne('secret', null),\n eq('secretHashed', false),\n eq('secretEncrypted', false),\n );\n\n scoped = appendQueryConditions(\n parsed,\n compiled.verdict === 'conditional' ?\n or(not(plaintextSecret), compiled.condition) :\n not(plaintextSecret),\n );\n }\n\n return this.repository.findMany(scoped);\n }\n\n const {\n data: entities,\n meta,\n } = await this.repository.findMany(parsed);\n let { total } = meta;\n\n const data: Client[] = [];\n for (const entity of entities) {\n if (\n entity.secret &&\n !entity.secretEncrypted &&\n !entity.secretHashed\n ) {\n try {\n await actor.permissionEvaluator.evaluateOneOf({\n name: [\n PermissionName.CLIENT_READ,\n PermissionName.CLIENT_UPDATE,\n PermissionName.CLIENT_DELETE,\n ],\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n data.push(entity);\n } catch {\n total -= 1;\n }\n\n continue;\n }\n\n data.push(entity);\n }\n\n return {\n data,\n meta: {\n ...meta,\n total, \n }, \n };\n }\n\n async getOne(\n idOrName: string,\n actor: ActorContext,\n query?: Record<string, any>,\n realmId?: string,\n ): Promise<Client> {\n const permissionNames = [\n PermissionName.CLIENT_READ,\n PermissionName.CLIENT_UPDATE,\n PermissionName.CLIENT_DELETE,\n ];\n\n let isMe = !!actor.identity &&\n actor.identity.type === 'client' &&\n (\n actor.identity.data.id === idOrName ||\n actor.identity.data.name === idOrName\n );\n\n if (!isMe) {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });\n }\n\n const entity = await this.repository.findOne(\n idOrName,\n await decodeQuery(query, {\n schema: clientSchema, \n parameters: ['fields', 'relations'], \n actor, \n }),\n realmId,\n );\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n if (isMe && actor.identity!.data.id !== entity.id) {\n isMe = false;\n await actor.permissionEvaluator.preEvaluateOneOf({ name: permissionNames });\n }\n\n if (\n !isMe &&\n entity.secret &&\n !entity.secretEncrypted &&\n !entity.secretHashed\n ) {\n await actor.permissionEvaluator.evaluateOneOf({\n name: permissionNames,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n return entity;\n }\n\n async create(\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(undefined, data, actor);\n return entity;\n }\n\n async update(\n idOrName: string,\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(idOrName, data, actor, { updateOnly: true });\n return entity;\n }\n\n async save(\n idOrName: string | undefined,\n data: Record<string, any>,\n actor: ActorContext,\n options: { updateOnly?: boolean } = {},\n ): Promise<{\n entity: Client,\n created: boolean \n }> {\n let group: string;\n\n const realm = typeof data.realmId === 'string' ?\n await this.realmRepository.resolve(data.realmId) :\n undefined;\n\n let entity: Client | null | undefined;\n if (idOrName) {\n const where: Record<string, any> = {};\n if (isUUID(idOrName)) {\n where.id = idOrName;\n } else {\n where.name = idOrName;\n }\n\n if (realm) {\n where.realmId = realm.id;\n }\n\n entity = await this.repository.findOneWithSecret(where);\n if (!entity && options.updateOnly) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n let isSelfEdit = false;\n if (entity) {\n try {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_UPDATE });\n } catch (e) {\n if (\n !actor.identity ||\n actor.identity.type !== 'client' ||\n actor.identity.data.id !== entity.id\n ) {\n throw e;\n }\n isSelfEdit = true;\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_SELF_MANAGE });\n }\n group = ValidatorGroup.UPDATE;\n } else {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_CREATE });\n group = ValidatorGroup.CREATE;\n }\n\n const validated = await this.validator.run(data, { group });\n\n // Reserve the system-provisioned client names (`system`, `web`) so an\n // API caller can't create or rename a client onto them — that would\n // collide on unique(name, realmId) or shadow the builtIn client.\n // Provisioning and runtime hooks bypass this service, so they remain\n // free to manage the reserved clients. builtIn clients are exempt\n // (they ARE the provisioned ones) but API callers can never produce a\n // builtIn client since the validator strips the flag.\n if (\n typeof validated.name === 'string' &&\n CLIENT_RESERVED_NAMES.includes(validated.name) &&\n !(entity && entity.builtIn && entity.name === validated.name)\n ) {\n throw new ValidationError(`The client name '${validated.name}' is reserved.`);\n }\n\n await this.repository.validateJoinColumns(validated);\n await this.repository.checkUniqueness(validated, entity || undefined);\n\n const credentialsService = new ClientCredentialsService();\n\n if (entity) {\n if (\n !isSelfEdit &&\n !validated.realmId &&\n !entity.realmId\n ) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n if (isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_SELF_MANAGE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n }\n\n entity = this.repository.merge(entity, validated);\n\n if (!isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_UPDATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret && !entity.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n if (validated.secret) {\n entity.secret = await credentialsService.protect(validated.secret, entity);\n }\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: false, \n };\n }\n\n if (!validated.realmId) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n\n entity = this.repository.create(validated);\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n entity.secret = await credentialsService.protect(validated.secret, validated);\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: true, \n };\n }\n\n async delete(\n id: string,\n actor: ActorContext,\n ): Promise<Client> {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_DELETE });\n\n const entity = await this.repository.findOneBy({ id });\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n\n const { id: entityId } = entity;\n await this.repository.remove(entity);\n entity.id = entityId;\n\n return entity;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAqCA,IAAaoB,gBAAb,cAAmCL,sBAAAA;CACrBM;CAEAC;CAEAC;CAEV,YAAYC,KAA2B;EACnC,MAAK;EACL,KAAKH,aAAaG,IAAIH;EACtB,KAAKC,kBAAkBE,IAAIF;EAC3B,KAAKC,YAAY,IAAIV,gBAAAA;CACzB;CAEA,MAAMY,QACFC,OACAC,OAC+C;EAC/C,MAAMC,kBAAkB;GACpBd,eAAee;GACff,eAAegB;GACfhB,eAAeiB;EAClB;EAED,MAAMJ,MAAMK,oBAAoBC,iBAAiB,EAAEC,MAAMN,gBAAgB,CAAA;EAEzE,MAAMO,SAAS,MAAMjB,YAAYQ,OAAO;GAAEU,QAAQjB;GAAcQ;EAAM,CAAA;EAQtE,MAAMU,iBAAiBF,OAAOG,OAAOC,MAAMC,MACtCC,UAAUA,MAAMP,SAAS,YAAYO,MAAMC,aAAa,GAAA;EAE7D,MAAMC,WAAW,MAAMhB,MAAMK,oBAAoBY,QAAQ,EAAEV,MAAMN,gBAAgB,CAAA;EACjF,IAAIe,SAASE,YAAY,QAAQ;GAC7B,IAAIC,SAASX;GACb,IAAIE,kBAAkBM,SAASE,YAAY,SAAS;IAChD,MAAME,kBAAkB7C,IACpBE,GAAG,UAAU,IAAA,GACbD,GAAG,gBAAgB,KAAA,GACnBA,GAAG,mBAAmB,KAAA,CAAA;IAG1B2C,SAAS7B,sBACLkB,QACAQ,SAASE,YAAY,gBACjBvC,GAAGD,IAAI0C,eAAAA,GAAkBJ,SAASK,SAAS,IAC3C3C,IAAI0C,eAAAA,CAAAA;GAEhB;GAEA,OAAO,KAAK1B,WAAW4B,SAASH,MAAAA;EACpC;EAEA,MAAM,EACFI,MAAMC,UACNC,SACA,MAAM,KAAK/B,WAAW4B,SAASd,MAAAA;EACnC,IAAI,EAAEkB,UAAUD;EAEhB,MAAMF,OAAiB,CAAA;EACvB,KAAK,MAAMI,UAAUH,UAAU;GAC3B,IACIG,OAAOC,UACP,CAACD,OAAOE,mBACR,CAACF,OAAOG,cACV;IACE,IAAI;KACA,MAAM9B,MAAMK,oBAAoB0B,cAAc;MAC1CxB,MAAM;OACFpB,eAAee;OACff,eAAegB;OACfhB,eAAeiB;MAClB;MACDmB,MAAMjD,iBAAiB;QAAGD,kBAAkB2D,aAAaL;OAAQ,GAAG,KAAKM,mBAAmBN,MAAAA;MAAQ,CAAA;KACxG,CAAA;KACAJ,KAAKW,KAAKP,MAAAA;IACd,QAAQ;KACJD,SAAS;IACb;IAEA;GACJ;GAEAH,KAAKW,KAAKP,MAAAA;EACd;EAEA,OAAO;GACHJ;GACAE,MAAM;IACF,GAAGA;IACHC;GACJ;EACJ;CACJ;CAEA,MAAMS,OACFC,UACApC,OACAD,OACAsC,SACe;EACf,MAAMpC,kBAAkB;GACpBd,eAAee;GACff,eAAegB;GACfhB,eAAeiB;EAClB;EAED,IAAIkC,OAAO,CAAC,CAACtC,MAAMuC,YACfvC,MAAMuC,SAASC,SAAS,aAEpBxC,MAAMuC,SAAShB,KAAKkB,OAAOL,YAC3BpC,MAAMuC,SAAShB,KAAKhB,SAAS6B;EAGrC,IAAI,CAACE,MACD,MAAMtC,MAAMK,oBAAoBC,iBAAiB,EAAEC,MAAMN,gBAAgB,CAAA;EAG7E,MAAM0B,SAAS,MAAM,KAAKjC,WAAWgD,QACjCN,UACA,MAAM7C,YAAYQ,OAAO;GACrBU,QAAQjB;GACRmD,YAAY,CAAC,UAAU,WAAY;GACnC3C;EACJ,CAAA,GACAqC,OAAAA;EAEJ,IAAI,CAACV,QACD,MAAM,IAAI7C,oBAAAA;EAGd,IAAIwD,QAAQtC,MAAMuC,SAAUhB,KAAKkB,OAAOd,OAAOc,IAAI;GAC/CH,OAAO;GACP,MAAMtC,MAAMK,oBAAoBC,iBAAiB,EAAEC,MAAMN,gBAAgB,CAAA;EAC7E;EAEA,IACI,CAACqC,QACDX,OAAOC,UACP,CAACD,OAAOE,mBACR,CAACF,OAAOG,cAER,MAAM9B,MAAMK,oBAAoB0B,cAAc;GAC1CxB,MAAMN;GACNsB,MAAMjD,iBAAiB;KAAGD,kBAAkB2D,aAAaL;IAAQ,GAAG,KAAKM,mBAAmBN,MAAAA;GAAQ,CAAA;EACxG,CAAA;EAGJ,OAAOA;CACX;CAEA,MAAMiB,OACFrB,MACAvB,OACe;EACf,MAAM,EAAE2B,WAAW,MAAM,KAAKkB,KAAKC,KAAAA,GAAWvB,MAAMvB,KAAAA;EACpD,OAAO2B;CACX;CAEA,MAAMoB,OACFX,UACAb,MACAvB,OACe;EACf,MAAM,EAAE2B,WAAW,MAAM,KAAKkB,KAAKT,UAAUb,MAAMvB,OAAO,EAAEgD,YAAY,KAAK,CAAA;EAC7E,OAAOrB;CACX;CAEA,MAAMkB,KACFT,UACAb,MACAvB,OACAiD,UAAoC,CAAC,GAItC;EACC,IAAIC;EAEJ,MAAMC,QAAQ,OAAO5B,KAAKc,YAAY,WAClC,MAAM,KAAK1C,gBAAgByD,QAAQ7B,KAAKc,OAAO,IAC/CS,KAAAA;EAEJ,IAAInB;EACJ,IAAIS,UAAU;GACV,MAAMiB,QAA6B,CAAC;GACpC,IAAIxE,OAAOuD,QAAAA,GACPiB,MAAMZ,KAAKL;QAEXiB,MAAM9C,OAAO6B;GAGjB,IAAIe,OACAE,MAAMhB,UAAUc,MAAMV;GAG1Bd,SAAS,MAAM,KAAKjC,WAAW4D,kBAAkBD,KAAAA;GACjD,IAAI,CAAC1B,UAAUsB,QAAQD,YACnB,MAAM,IAAIlE,oBAAAA;EAElB,OAAO,IAAImE,QAAQD,YACf,MAAM,IAAIlE,oBAAAA;EAGd,IAAIyE,aAAa;EACjB,IAAI5B,QAAQ;GACR,IAAI;IACA,MAAM3B,MAAMK,oBAAoBmD,YAAY,EAAEjD,MAAMpB,eAAegB,cAAc,CAAA;GACrF,SAASsD,GAAG;IACR,IACI,CAACzD,MAAMuC,YACPvC,MAAMuC,SAASC,SAAS,YACxBxC,MAAMuC,SAAShB,KAAKkB,OAAOd,OAAOc,IAElC,MAAMgB;IAEVF,aAAa;IACb,MAAMvD,MAAMK,oBAAoBmD,YAAY,EAAEjD,MAAMpB,eAAeuE,mBAAmB,CAAA;GAC1F;GACAR,QAAQtE,eAAe+E;EAC3B,OAAO;GACH,MAAM3D,MAAMK,oBAAoBmD,YAAY,EAAEjD,MAAMpB,eAAeyE,cAAc,CAAA;GACjFV,QAAQtE,eAAeiF;EAC3B;EAEA,MAAMC,YAAY,MAAM,KAAKlE,UAAUmE,IAAIxC,MAAM,EAAE2B,MAAM,CAAA;EASzD,IACI,OAAOY,UAAUvD,SAAS,YAC1BvB,sBAAsBgF,SAASF,UAAUvD,IAAI,KAC7C,EAAEoB,UAAUA,OAAOsC,WAAWtC,OAAOpB,SAASuD,UAAUvD,OAExD,MAAM,IAAIxB,gBAAgB,oBAAoB+E,UAAUvD,KAAK,eAAe;EAGhF,MAAM,KAAKb,WAAWwE,oBAAoBJ,SAAAA;EAC1C,MAAM,KAAKpE,WAAWyE,gBAAgBL,WAAWnC,UAAUmB,KAAAA,CAAAA;EAE3D,MAAMsB,qBAAqB,IAAI/E,yBAAAA;EAE/B,IAAIsC,QAAQ;GACR,IACI,CAAC4B,cACD,CAACO,UAAUzB,WACX,CAACV,OAAOU,SACV;IACE,MAAMgC,eAAe,KAAKC,gBAAgBtE,KAAAA;IAC1C,IAAIqE,cACAP,UAAUzB,UAAUgC;GAE5B;GAEA,IAAId,YACA,MAAMvD,MAAMK,oBAAoBkE,SAAS;IACrChE,MAAMpB,eAAeuE;IACrBnC,MAAMjD,iBAAiB;MAAGD,kBAAkB2D,aAAa8B;KAAW,GAAG,KAAK7B,mBAAmB6B,SAAAA;IAAW,CAAA;GAC9G,CAAA;GAGJnC,SAAS,KAAKjC,WAAW8E,MAAM7C,QAAQmC,SAAAA;GAEvC,IAAI,CAACP,YACD,MAAMvD,MAAMK,oBAAoBkE,SAAS;IACrChE,MAAMpB,eAAegB;IACrBoB,MAAMjD,iBAAiB;MAAGD,kBAAkB2D,aAAaL;KAAQ,GAAG,KAAKM,mBAAmBN,MAAAA;IAAQ,CAAA;GACxG,CAAA;GAGJ,IAAIA,OAAO8C,eAAexF,iBAAiByF,QAAQ;IAC/C,IAAI,CAACZ,UAAUlC,UAAU,CAACD,OAAOC,QAC7BkC,UAAUlC,SAASwC,mBAAmBO,eAAc;IAGxD,IAAIb,UAAUlC,QACVD,OAAOC,SAAS,MAAMwC,mBAAmBQ,QAAQd,UAAUlC,QAAQD,MAAAA;GAE3E,OAAO;IACHA,OAAOC,SAAS;IAChBD,OAAOG,eAAe;IACtBH,OAAOE,kBAAkB;GAC7B;GAEA,MAAM,KAAKnC,WAAWmD,KAAKlB,MAAAA;GAE3B,OAAO;IACHA;IACAkD,SAAS;GACb;EACJ;EAEA,IAAI,CAACf,UAAUzB,SAAS;GACpB,MAAMgC,eAAe,KAAKC,gBAAgBtE,KAAAA;GAC1C,IAAIqE,cACAP,UAAUzB,UAAUgC;EAE5B;EAEA,MAAMrE,MAAMK,oBAAoBkE,SAAS;GACrChE,MAAMpB,eAAeyE;GACrBrC,MAAMjD,iBAAiB;KAAGD,kBAAkB2D,aAAa8B;IAAW,GAAG,KAAK7B,mBAAmB6B,SAAAA;GAAW,CAAA;EAC9G,CAAA;EAEAnC,SAAS,KAAKjC,WAAWkD,OAAOkB,SAAAA;EAEhC,IAAInC,OAAO8C,eAAexF,iBAAiByF,QAAQ;GAC/C,IAAI,CAACZ,UAAUlC,QACXkC,UAAUlC,SAASwC,mBAAmBO,eAAc;GAGxDhD,OAAOC,SAAS,MAAMwC,mBAAmBQ,QAAQd,UAAUlC,QAAQkC,SAAAA;EACvE,OAAO;GACHnC,OAAOC,SAAS;GAChBD,OAAOG,eAAe;GACtBH,OAAOE,kBAAkB;EAC7B;EAEA,MAAM,KAAKnC,WAAWmD,KAAKlB,MAAAA;EAE3B,OAAO;GACHA;GACAkD,SAAS;EACb;CACJ;CAEA,MAAMC,OACFrC,IACAzC,OACe;EACf,MAAMA,MAAMK,oBAAoBmD,YAAY,EAAEjD,MAAMpB,eAAeiB,cAAc,CAAA;EAEjF,MAAMuB,SAAS,MAAM,KAAKjC,WAAWqF,UAAU,EAAEtC,GAAG,CAAA;EACpD,IAAI,CAACd,QACD,MAAM,IAAI7C,oBAAAA;EAGd,MAAMkB,MAAMK,oBAAoBkE,SAAS;GACrChE,MAAMpB,eAAeiB;GACrBmB,MAAMjD,iBAAiB;KAAGD,kBAAkB2D,aAAaL;IAAQ,GAAG,KAAKM,mBAAmBN,MAAAA;GAAQ,CAAA;EACxG,CAAA;EAEA,MAAM,EAAEc,IAAIuC,aAAarD;EACzB,MAAM,KAAKjC,WAAWuF,OAAOtD,MAAAA;EAC7BA,OAAOc,KAAKuC;EAEZ,OAAOrD;CACX;AACJ"}
1
+ {"version":3,"file":"service.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","isUUID","EntityNotFoundError","ValidationError","CLIENT_RESERVED_NAMES","ClientAuthMethod","ClientValidator","PermissionName","AbstractEntityService","ClientCredentialsService","CLIENT_READ_PERMISSIONS","decodeQuery","clientSchema","ClientService","repository","realmRepository","validator","ctx","getMany","query","actor","permissionEvaluator","preEvaluateOneOf","name","findMany","schema","getOne","idOrName","realmId","isMe","identity","type","data","id","entity","findOne","parameters","secret","secretEncrypted","secretHashed","evaluateOneOf","ATTRIBUTES","resourceRealmMatch","create","save","undefined","update","updateOnly","options","group","realm","resolve","where","findOneWithSecret","isSelfEdit","preEvaluate","CLIENT_UPDATE","e","CLIENT_SELF_MANAGE","UPDATE","CLIENT_CREATE","CREATE","validated","run","includes","builtIn","validateJoinColumns","checkUniqueness","credentialsService","actorRealmId","getActorRealmId","evaluate","merge","authMethod","SECRET","generateSecret","protect","created","delete","CLIENT_DELETE","findOneBy","entityId","remove"],"sources":["../../../../src/core/entities/client/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { ValidatorGroup, isUUID } from '@authup/kit';\nimport { EntityNotFoundError, ValidationError } from '@authup/errors';\nimport {\n CLIENT_RESERVED_NAMES,\n ClientAuthMethod,\n ClientValidator,\n PermissionName,\n} from '@authup/core-kit';\nimport type { Client } from '@authup/core-kit';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { IRealmRepository } from '../realm/types.ts';\nimport { AbstractEntityService } from '@authup/server-kit';\nimport { ClientCredentialsService } from '../../authentication/credential/entities/client/module.ts';\nimport type { IClientRepository, IClientService } from './types.ts';\nimport { CLIENT_READ_PERMISSIONS } from './constants.ts';\nimport { decodeQuery } from '../../query/index.ts';\nimport { clientSchema } from './schema.ts';\n\nexport type ClientServiceContext = {\n repository: IClientRepository;\n realmRepository: IRealmRepository;\n};\n\nexport class ClientService extends AbstractEntityService implements IClientService {\n protected repository: IClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected validator: ClientValidator;\n\n constructor(ctx: ClientServiceContext) {\n super();\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.validator = new ClientValidator();\n }\n\n async getMany(\n query: Record<string, any>,\n actor: ActorContext,\n ): Promise<EntityRepositoryFindManyResult<Client>> {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n\n // The per-row `secret` visibility gate lives on the client SCHEMA\n // (`fields.validateMany`, issue #3322), so it also covers the\n // `include=client` paths served by other services; the repository\n // layer redacts unauthorized values without dropping rows.\n return this.repository.findMany(\n await decodeQuery(query, { schema: clientSchema, actor }),\n );\n }\n\n async getOne(\n idOrName: string,\n actor: ActorContext,\n query?: Record<string, any>,\n realmId?: string,\n ): Promise<Client> {\n let isMe = !!actor.identity &&\n actor.identity.type === 'client' &&\n (\n actor.identity.data.id === idOrName ||\n actor.identity.data.name === idOrName\n );\n\n if (!isMe) {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n const entity = await this.repository.findOne(\n idOrName,\n await decodeQuery(query, {\n schema: clientSchema, \n parameters: ['fields', 'relations'], \n actor, \n }),\n realmId,\n );\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n if (isMe && actor.identity!.data.id !== entity.id) {\n isMe = false;\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n if (\n !isMe &&\n entity.secret &&\n !entity.secretEncrypted &&\n !entity.secretHashed\n ) {\n await actor.permissionEvaluator.evaluateOneOf({\n name: CLIENT_READ_PERMISSIONS,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n return entity;\n }\n\n async create(\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(undefined, data, actor);\n return entity;\n }\n\n async update(\n idOrName: string,\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(idOrName, data, actor, { updateOnly: true });\n return entity;\n }\n\n async save(\n idOrName: string | undefined,\n data: Record<string, any>,\n actor: ActorContext,\n options: { updateOnly?: boolean } = {},\n ): Promise<{\n entity: Client,\n created: boolean \n }> {\n let group: string;\n\n const realm = typeof data.realmId === 'string' ?\n await this.realmRepository.resolve(data.realmId) :\n undefined;\n\n let entity: Client | null | undefined;\n if (idOrName) {\n const where: Record<string, any> = {};\n if (isUUID(idOrName)) {\n where.id = idOrName;\n } else {\n where.name = idOrName;\n }\n\n if (realm) {\n where.realmId = realm.id;\n }\n\n entity = await this.repository.findOneWithSecret(where);\n if (!entity && options.updateOnly) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n let isSelfEdit = false;\n if (entity) {\n try {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_UPDATE });\n } catch (e) {\n if (\n !actor.identity ||\n actor.identity.type !== 'client' ||\n actor.identity.data.id !== entity.id\n ) {\n throw e;\n }\n isSelfEdit = true;\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_SELF_MANAGE });\n }\n group = ValidatorGroup.UPDATE;\n } else {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_CREATE });\n group = ValidatorGroup.CREATE;\n }\n\n const validated = await this.validator.run(data, { group });\n\n // Reserve the system-provisioned client names (`system`, `web`) so an\n // API caller can't create or rename a client onto them — that would\n // collide on unique(name, realmId) or shadow the builtIn client.\n // Provisioning and runtime hooks bypass this service, so they remain\n // free to manage the reserved clients. builtIn clients are exempt\n // (they ARE the provisioned ones) but API callers can never produce a\n // builtIn client since the validator strips the flag.\n if (\n typeof validated.name === 'string' &&\n CLIENT_RESERVED_NAMES.includes(validated.name) &&\n !(entity && entity.builtIn && entity.name === validated.name)\n ) {\n throw new ValidationError(`The client name '${validated.name}' is reserved.`);\n }\n\n await this.repository.validateJoinColumns(validated);\n await this.repository.checkUniqueness(validated, entity || undefined);\n\n const credentialsService = new ClientCredentialsService();\n\n if (entity) {\n if (\n !isSelfEdit &&\n !validated.realmId &&\n !entity.realmId\n ) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n if (isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_SELF_MANAGE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n }\n\n entity = this.repository.merge(entity, validated);\n\n if (!isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_UPDATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret && !entity.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n if (validated.secret) {\n entity.secret = await credentialsService.protect(validated.secret, entity);\n }\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: false, \n };\n }\n\n if (!validated.realmId) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n\n entity = this.repository.create(validated);\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n entity.secret = await credentialsService.protect(validated.secret, validated);\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: true, \n };\n }\n\n async delete(\n id: string,\n actor: ActorContext,\n ): Promise<Client> {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_DELETE });\n\n const entity = await this.repository.findOneBy({ id });\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n\n const { id: entityId } = entity;\n await this.repository.remove(entity);\n entity.id = entityId;\n\n return entity;\n }\n}\n"],"mappings":"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"}
@@ -1,36 +1,72 @@
1
1
  import type { Client, Realm } from '@authup/core-kit';
2
2
  import type { Logger } from '@authup/server-kit';
3
+ import type { IClientScopeRepository } from '../client-scope/types.ts';
4
+ import type { IScopeRepository } from '../scope/types.ts';
3
5
  import type { IClientRepository, IWebClientProvisioner } from './types.ts';
4
6
  export type WebClientProvisionerContext = {
5
7
  clientRepository: IClientRepository;
8
+ scopeRepository: IScopeRepository;
9
+ clientScopeRepository: IClientScopeRepository;
6
10
  appOrigins: string[];
7
11
  logger?: Logger;
8
12
  };
13
+ /**
14
+ * The scopes a realm's public `web` client is granted.
15
+ *
16
+ * `global` lets the issued token drive the management API (parity with the
17
+ * legacy password-grant admin login); `openid` yields the id-token.
18
+ */
19
+ export declare const WEB_CLIENT_SCOPE_NAMES: string[];
9
20
  /**
10
21
  * Build the attribute set for a realm's public `web` client.
11
22
  *
12
23
  * Public (PKCE) client used by authup's own client-web and any downstream
13
24
  * UI embedding client-web-kit. `builtIn` makes it auto-consent in the
14
- * /authorize flow and protects it from the API reserved-name guard. The
15
- * `global` scope lets the issued token drive the management API (parity
16
- * with the legacy password-grant admin login); `openid` for the id-token.
25
+ * /authorize flow and protects it from the API reserved-name guard.
17
26
  *
18
27
  * `grantTypes` is an enforced allowlist (`assertClientGrantAllowed` at the
19
28
  * /token grants and the /authorize code-request verifier; null = allow-all),
20
29
  * so it must list every flow the client uses. `redirectUri` and
21
30
  * `postLogoutRedirectUri` are each one `<origin>/**` wildcard per trusted
22
31
  * origin (matched by isSimpleMatch).
32
+ *
33
+ * The `scope` column is descriptive only. Scope authorization resolves
34
+ * through the `auth_client_scopes` junction, which
35
+ * `WebClientProvisioner.ensureForRealm` fills from
36
+ * {@see WEB_CLIENT_SCOPE_NAMES}.
23
37
  */
24
38
  export declare function buildWebClientAttributes(realm: Realm | {
25
39
  id: string;
26
40
  }, appOrigins: string[]): Partial<Client>;
27
41
  export declare class WebClientProvisioner implements IWebClientProvisioner {
28
42
  protected clientRepository: IClientRepository;
43
+ protected scopeRepository: IScopeRepository;
44
+ protected clientScopeRepository: IClientScopeRepository;
29
45
  protected appOrigins: string[];
30
46
  protected logger?: Logger;
31
47
  constructor(ctx: WebClientProvisionerContext);
32
48
  ensureForRealm(realm: Realm | {
33
49
  id: string;
34
50
  }): Promise<void>;
51
+ /**
52
+ * Upsert the client row itself.
53
+ *
54
+ * `web` is a reserved client name (`CLIENT_RESERVED_NAMES`), so the row
55
+ * belongs to the system whatever state it is in. A non-built-in one
56
+ * predates the reservation and is taken over rather than left to shadow
57
+ * the realm's login client.
58
+ */
59
+ protected ensureClient(realm: Realm | {
60
+ id: string;
61
+ }): Promise<Client>;
62
+ /**
63
+ * Bind the built-in global scopes through `auth_client_scopes`. That
64
+ * junction is the only source the /authorize code-request verifier reads
65
+ * scopes from.
66
+ *
67
+ * Additive like the attribute MERGE above: an extra scope an admin bound
68
+ * by hand is left in place.
69
+ */
70
+ protected ensureScopes(client: Client): Promise<void>;
35
71
  }
36
72
  //# sourceMappingURL=web-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-client.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/web-client.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,MAAM,2BAA2B,GAAG;IACtC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC7B,UAAU,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,qBAAa,oBAAqB,YAAW,qBAAqB;IAC9D,SAAS,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;IAE9C,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAE/B,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEd,GAAG,EAAE,2BAA2B;IAMtC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAsCrE"}
1
+ {"version":3,"file":"web-client.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/web-client.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE3E,MAAM,MAAM,2BAA2B,GAAG;IACtC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,eAAe,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,EAAE,sBAAsB,CAAC;IAC9C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,EAG1C,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC7B,UAAU,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,qBAAa,oBAAqB,YAAW,qBAAqB;IAC9D,SAAS,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;IAE9C,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;IAExD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAE/B,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEd,GAAG,EAAE,2BAA2B;IAQtC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IASlE;;;;;;;OAOG;cACa,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAsD5E;;;;;;;OAOG;cACa,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiC9D"}
@@ -4,19 +4,28 @@ import.meta.url;
4
4
  import { CLIENT_WEB_NAME, ClientAuthMethod, ClientTokenBindingMethod, ScopeName } from "@authup/core-kit";
5
5
  //#region src/core/entities/client/web-client.ts
6
6
  /**
7
+ * The scopes a realm's public `web` client is granted.
8
+ *
9
+ * `global` lets the issued token drive the management API (parity with the
10
+ * legacy password-grant admin login); `openid` yields the id-token.
11
+ */ const WEB_CLIENT_SCOPE_NAMES = [ScopeName.GLOBAL, ScopeName.OPEN_ID];
12
+ /**
7
13
  * Build the attribute set for a realm's public `web` client.
8
14
  *
9
15
  * Public (PKCE) client used by authup's own client-web and any downstream
10
16
  * UI embedding client-web-kit. `builtIn` makes it auto-consent in the
11
- * /authorize flow and protects it from the API reserved-name guard. The
12
- * `global` scope lets the issued token drive the management API (parity
13
- * with the legacy password-grant admin login); `openid` for the id-token.
17
+ * /authorize flow and protects it from the API reserved-name guard.
14
18
  *
15
19
  * `grantTypes` is an enforced allowlist (`assertClientGrantAllowed` at the
16
20
  * /token grants and the /authorize code-request verifier; null = allow-all),
17
21
  * so it must list every flow the client uses. `redirectUri` and
18
22
  * `postLogoutRedirectUri` are each one `<origin>/**` wildcard per trusted
19
23
  * origin (matched by isSimpleMatch).
24
+ *
25
+ * The `scope` column is descriptive only. Scope authorization resolves
26
+ * through the `auth_client_scopes` junction, which
27
+ * `WebClientProvisioner.ensureForRealm` fills from
28
+ * {@see WEB_CLIENT_SCOPE_NAMES}.
20
29
  */ function buildWebClientAttributes(realm, appOrigins) {
21
30
  const originPatterns = appOrigins.map((origin) => `${origin}/**`).join(",");
22
31
  return {
@@ -27,41 +36,88 @@ import { CLIENT_WEB_NAME, ClientAuthMethod, ClientTokenBindingMethod, ScopeName
27
36
  builtIn: true,
28
37
  active: true,
29
38
  grantTypes: "authorization_code refresh_token",
30
- scope: `${ScopeName.GLOBAL} ${ScopeName.OPEN_ID}`,
39
+ scope: WEB_CLIENT_SCOPE_NAMES.join(" "),
31
40
  redirectUri: originPatterns,
32
41
  postLogoutRedirectUri: originPatterns
33
42
  };
34
43
  }
35
44
  var WebClientProvisioner = class {
36
45
  clientRepository;
46
+ scopeRepository;
47
+ clientScopeRepository;
37
48
  appOrigins;
38
49
  logger;
39
50
  constructor(ctx) {
40
51
  this.clientRepository = ctx.clientRepository;
52
+ this.scopeRepository = ctx.scopeRepository;
53
+ this.clientScopeRepository = ctx.clientScopeRepository;
41
54
  this.appOrigins = ctx.appOrigins;
42
55
  this.logger = ctx.logger;
43
56
  }
44
57
  async ensureForRealm(realm) {
58
+ const client = await this.ensureClient(realm);
59
+ await this.ensureScopes(client);
60
+ }
61
+ /**
62
+ * Upsert the client row itself.
63
+ *
64
+ * `web` is a reserved client name (`CLIENT_RESERVED_NAMES`), so the row
65
+ * belongs to the system whatever state it is in. A non-built-in one
66
+ * predates the reservation and is taken over rather than left to shadow
67
+ * the realm's login client.
68
+ */ async ensureClient(realm) {
45
69
  const attributes = buildWebClientAttributes(realm, this.appOrigins);
46
70
  const existing = await this.clientRepository.findOneBy({
47
71
  name: CLIENT_WEB_NAME,
48
72
  realmId: realm.id
49
73
  });
50
74
  if (existing) {
75
+ if (!Object.keys(attributes).some((key) => existing[key] !== attributes[key])) return existing;
76
+ let target = existing;
51
77
  if (!existing.builtIn) {
52
- if (this.logger) this.logger.warn(`Skipping web client provisioning for realm ${realm.id}: a non-built-in client named '${CLIENT_WEB_NAME}' already exists.`);
53
- return;
78
+ if (this.logger) this.logger.warn(`Taking over the non-built-in client named '${CLIENT_WEB_NAME}' in realm ${realm.id}: the name is reserved for the built-in web client.`);
79
+ target = await this.clientRepository.findOneWithSecret({ id: existing.id }) || existing;
80
+ target.secret = null;
81
+ target.secretHashed = false;
82
+ target.secretEncrypted = false;
54
83
  }
55
- if (!Object.keys(attributes).some((key) => existing[key] !== attributes[key])) return;
56
- const merged = this.clientRepository.merge(existing, attributes);
57
- await this.clientRepository.save(merged);
58
- return;
84
+ const merged = this.clientRepository.merge(target, attributes);
85
+ return this.clientRepository.save(merged);
59
86
  }
60
87
  const entity = this.clientRepository.create(attributes);
61
- await this.clientRepository.save(entity);
88
+ return this.clientRepository.save(entity);
89
+ }
90
+ /**
91
+ * Bind the built-in global scopes through `auth_client_scopes`. That
92
+ * junction is the only source the /authorize code-request verifier reads
93
+ * scopes from.
94
+ *
95
+ * Additive like the attribute MERGE above: an extra scope an admin bound
96
+ * by hand is left in place.
97
+ */ async ensureScopes(client) {
98
+ for (const name of WEB_CLIENT_SCOPE_NAMES) {
99
+ const scope = await this.scopeRepository.findOneBy({
100
+ name,
101
+ realmId: null
102
+ });
103
+ if (!scope) {
104
+ if (this.logger) this.logger.warn(`Skipping scope '${name}' for the web client of realm ${client.realmId}: the scope is not provisioned.`);
105
+ continue;
106
+ }
107
+ if (await this.clientScopeRepository.findOneBy({
108
+ clientId: client.id,
109
+ scopeId: scope.id
110
+ })) continue;
111
+ await this.clientScopeRepository.save(this.clientScopeRepository.create({
112
+ clientId: client.id,
113
+ clientRealmId: client.realmId,
114
+ scopeId: scope.id,
115
+ scopeRealmId: scope.realmId
116
+ }));
117
+ }
62
118
  }
63
119
  };
64
120
  //#endregion
65
- export { WebClientProvisioner, buildWebClientAttributes };
121
+ export { WEB_CLIENT_SCOPE_NAMES, WebClientProvisioner, buildWebClientAttributes };
66
122
 
67
123
  //# sourceMappingURL=web-client.mjs.map