@contember/engine-tenant-api 2.2.0-alpha.3 → 2.2.0-alpha.6

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 (1316) hide show
  1. package/CLAUDE.md +36 -2
  2. package/dist/development/index.cjs +32 -0
  3. package/dist/development/index.cjs.map +1 -1
  4. package/dist/development/index.js +33 -1
  5. package/dist/development/index.js.map +1 -1
  6. package/dist/development/src/TenantContainer.cjs +15 -3
  7. package/dist/development/src/TenantContainer.cjs.map +1 -1
  8. package/dist/development/src/TenantContainer.js +15 -3
  9. package/dist/development/src/TenantContainer.js.map +1 -1
  10. package/dist/development/src/bridges/system/IdentityFetcher.cjs +2 -1
  11. package/dist/development/src/bridges/system/IdentityFetcher.cjs.map +1 -1
  12. package/dist/development/src/bridges/system/IdentityFetcher.js +2 -1
  13. package/dist/development/src/bridges/system/IdentityFetcher.js.map +1 -1
  14. package/dist/development/src/migrations/2026-06-18-120000-idp-claim-mapping.cjs +10 -0
  15. package/dist/development/src/migrations/2026-06-18-120000-idp-claim-mapping.cjs.map +1 -0
  16. package/dist/development/src/migrations/2026-06-18-120000-idp-claim-mapping.js +11 -0
  17. package/dist/development/src/migrations/2026-06-18-120000-idp-claim-mapping.js.map +1 -0
  18. package/dist/development/src/migrations/2026-08-03-120000-person-enable-auth-log.cjs +9 -0
  19. package/dist/development/src/migrations/2026-08-03-120000-person-enable-auth-log.cjs.map +1 -0
  20. package/dist/development/src/migrations/2026-08-03-120000-person-enable-auth-log.js +10 -0
  21. package/dist/development/src/migrations/2026-08-03-120000-person-enable-auth-log.js.map +1 -0
  22. package/dist/development/src/migrations/2026-08-04-120000-config-panel.cjs +11 -0
  23. package/dist/development/src/migrations/2026-08-04-120000-config-panel.cjs.map +1 -0
  24. package/dist/development/src/migrations/2026-08-04-120000-config-panel.js +12 -0
  25. package/dist/development/src/migrations/2026-08-04-120000-config-panel.js.map +1 -0
  26. package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs +18 -0
  27. package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs.map +1 -0
  28. package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js +19 -0
  29. package/dist/development/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js.map +1 -0
  30. package/dist/development/src/migrations/runner.cjs +11 -2
  31. package/dist/development/src/migrations/runner.cjs.map +1 -1
  32. package/dist/development/src/migrations/runner.js +11 -2
  33. package/dist/development/src/migrations/runner.js.map +1 -1
  34. package/dist/development/src/migrations/snapshot.cjs +8 -2
  35. package/dist/development/src/migrations/snapshot.cjs.map +1 -1
  36. package/dist/development/src/migrations/snapshot.js +8 -2
  37. package/dist/development/src/migrations/snapshot.js.map +1 -1
  38. package/dist/development/src/model/commands/configuration/UpdateConfigurationCommand.cjs +5 -3
  39. package/dist/development/src/model/commands/configuration/UpdateConfigurationCommand.cjs.map +1 -1
  40. package/dist/development/src/model/commands/configuration/UpdateConfigurationCommand.js +5 -3
  41. package/dist/development/src/model/commands/configuration/UpdateConfigurationCommand.js.map +1 -1
  42. package/dist/development/src/model/commands/idp/CreateIdpCommand.cjs +8 -2
  43. package/dist/development/src/model/commands/idp/CreateIdpCommand.cjs.map +1 -1
  44. package/dist/development/src/model/commands/idp/CreateIdpCommand.js +8 -2
  45. package/dist/development/src/model/commands/idp/CreateIdpCommand.js.map +1 -1
  46. package/dist/development/src/model/commands/idp/UpdateIdpCommand.cjs +6 -2
  47. package/dist/development/src/model/commands/idp/UpdateIdpCommand.cjs.map +1 -1
  48. package/dist/development/src/model/commands/idp/UpdateIdpCommand.js +6 -2
  49. package/dist/development/src/model/commands/idp/UpdateIdpCommand.js.map +1 -1
  50. package/dist/development/src/model/commands/membership/{RemoveProjectMembershipsCommand.cjs → RemoveProjectMembershipCommand.cjs} +1 -1
  51. package/dist/development/src/model/commands/membership/RemoveProjectMembershipCommand.cjs.map +1 -0
  52. package/dist/development/src/model/commands/membership/{RemoveProjectMembershipsCommand.js → RemoveProjectMembershipCommand.js} +1 -1
  53. package/dist/development/src/model/commands/membership/RemoveProjectMembershipCommand.js.map +1 -0
  54. package/dist/development/src/model/commands/membership/UpdateProjectMemberCommand.cjs +2 -2
  55. package/dist/development/src/model/commands/membership/UpdateProjectMemberCommand.cjs.map +1 -1
  56. package/dist/development/src/model/commands/membership/UpdateProjectMemberCommand.js +1 -1
  57. package/dist/development/src/model/commands/membership/UpdateProjectMemberCommand.js.map +1 -1
  58. package/dist/development/src/model/commands/person/EnablePersonCommand.cjs +17 -0
  59. package/dist/development/src/model/commands/person/EnablePersonCommand.cjs.map +1 -0
  60. package/dist/development/src/model/commands/person/EnablePersonCommand.js +17 -0
  61. package/dist/development/src/model/commands/person/EnablePersonCommand.js.map +1 -0
  62. package/dist/development/src/model/queries/configuration/ConfigurationQuery.cjs +4 -0
  63. package/dist/development/src/model/queries/configuration/ConfigurationQuery.cjs.map +1 -1
  64. package/dist/development/src/model/queries/configuration/ConfigurationQuery.js +4 -0
  65. package/dist/development/src/model/queries/configuration/ConfigurationQuery.js.map +1 -1
  66. package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs +21 -0
  67. package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs.map +1 -0
  68. package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js +21 -0
  69. package/dist/development/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js.map +1 -0
  70. package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.cjs +1 -1
  71. package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.cjs.map +1 -1
  72. package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.js +1 -1
  73. package/dist/development/src/model/queries/idp/PersonIdentityProvidersQuery.js.map +1 -1
  74. package/dist/development/src/model/queries/idp/helpers.cjs +3 -3
  75. package/dist/development/src/model/queries/idp/helpers.cjs.map +1 -1
  76. package/dist/development/src/model/queries/idp/helpers.js +3 -3
  77. package/dist/development/src/model/queries/idp/helpers.js.map +1 -1
  78. package/dist/development/src/model/queries/membership/ProjectMembersQuery.cjs +1 -1
  79. package/dist/development/src/model/queries/membership/ProjectMembersQuery.cjs.map +1 -1
  80. package/dist/development/src/model/queries/membership/ProjectMembersQuery.js +1 -1
  81. package/dist/development/src/model/queries/membership/ProjectMembersQuery.js.map +1 -1
  82. package/dist/development/src/model/queries/person/PersonQueryBuilderFactory.cjs +1 -1
  83. package/dist/development/src/model/queries/person/PersonQueryBuilderFactory.cjs.map +1 -1
  84. package/dist/development/src/model/queries/person/PersonQueryBuilderFactory.js +1 -1
  85. package/dist/development/src/model/queries/person/PersonQueryBuilderFactory.js.map +1 -1
  86. package/dist/development/src/model/service/AuthLogService.cjs +8 -2
  87. package/dist/development/src/model/service/AuthLogService.cjs.map +1 -1
  88. package/dist/development/src/model/service/AuthLogService.js +8 -2
  89. package/dist/development/src/model/service/AuthLogService.js.map +1 -1
  90. package/dist/development/src/model/service/IDPSignInManager.cjs +29 -2
  91. package/dist/development/src/model/service/IDPSignInManager.cjs.map +1 -1
  92. package/dist/development/src/model/service/IDPSignInManager.js +29 -2
  93. package/dist/development/src/model/service/IDPSignInManager.js.map +1 -1
  94. package/dist/development/src/model/service/PanelAccessResolver.cjs +31 -0
  95. package/dist/development/src/model/service/PanelAccessResolver.cjs.map +1 -0
  96. package/dist/development/src/model/service/PanelAccessResolver.js +31 -0
  97. package/dist/development/src/model/service/PanelAccessResolver.js.map +1 -0
  98. package/dist/development/src/model/service/PasswordResetManager.cjs +5 -0
  99. package/dist/development/src/model/service/PasswordResetManager.cjs.map +1 -1
  100. package/dist/development/src/model/service/PasswordResetManager.js +5 -0
  101. package/dist/development/src/model/service/PasswordResetManager.js.map +1 -1
  102. package/dist/development/src/model/service/PasswordlessSignInManager.cjs +19 -0
  103. package/dist/development/src/model/service/PasswordlessSignInManager.cjs.map +1 -1
  104. package/dist/development/src/model/service/PasswordlessSignInManager.js +19 -0
  105. package/dist/development/src/model/service/PasswordlessSignInManager.js.map +1 -1
  106. package/dist/development/src/model/service/PersonAccessManager.cjs +10 -2
  107. package/dist/development/src/model/service/PersonAccessManager.cjs.map +1 -1
  108. package/dist/development/src/model/service/PersonAccessManager.js +10 -2
  109. package/dist/development/src/model/service/PersonAccessManager.js.map +1 -1
  110. package/dist/development/src/model/service/SignInManager.cjs +5 -0
  111. package/dist/development/src/model/service/SignInManager.cjs.map +1 -1
  112. package/dist/development/src/model/service/SignInManager.js +5 -0
  113. package/dist/development/src/model/service/SignInManager.js.map +1 -1
  114. package/dist/development/src/model/service/apiKey/ApiKeyManager.cjs +4 -3
  115. package/dist/development/src/model/service/apiKey/ApiKeyManager.cjs.map +1 -1
  116. package/dist/development/src/model/service/apiKey/ApiKeyManager.js +4 -3
  117. package/dist/development/src/model/service/apiKey/ApiKeyManager.js.map +1 -1
  118. package/dist/development/src/model/service/apiKey/UnpersistedApiKeyManager.cjs +7 -0
  119. package/dist/development/src/model/service/apiKey/UnpersistedApiKeyManager.cjs.map +1 -1
  120. package/dist/development/src/model/service/apiKey/UnpersistedApiKeyManager.js +8 -1
  121. package/dist/development/src/model/service/apiKey/UnpersistedApiKeyManager.js.map +1 -1
  122. package/dist/development/src/model/service/idp/ClaimMapping.cjs +187 -0
  123. package/dist/development/src/model/service/idp/ClaimMapping.cjs.map +1 -0
  124. package/dist/development/src/model/service/idp/ClaimMapping.js +170 -0
  125. package/dist/development/src/model/service/idp/ClaimMapping.js.map +1 -0
  126. package/dist/development/src/model/service/idp/ClaimMappingEvaluator.cjs +155 -0
  127. package/dist/development/src/model/service/idp/ClaimMappingEvaluator.cjs.map +1 -0
  128. package/dist/development/src/model/service/idp/ClaimMappingEvaluator.js +155 -0
  129. package/dist/development/src/model/service/idp/ClaimMappingEvaluator.js.map +1 -0
  130. package/dist/development/src/model/service/idp/ClaimMappingValidation.cjs +83 -0
  131. package/dist/development/src/model/service/idp/ClaimMappingValidation.cjs.map +1 -0
  132. package/dist/development/src/model/service/idp/ClaimMappingValidation.js +83 -0
  133. package/dist/development/src/model/service/idp/ClaimMappingValidation.js.map +1 -0
  134. package/dist/development/src/model/service/idp/IDPClaimSyncService.cjs +260 -0
  135. package/dist/development/src/model/service/idp/IDPClaimSyncService.cjs.map +1 -0
  136. package/dist/development/src/model/service/idp/IDPClaimSyncService.js +260 -0
  137. package/dist/development/src/model/service/idp/IDPClaimSyncService.js.map +1 -0
  138. package/dist/development/src/model/service/idp/IDPManager.cjs +96 -20
  139. package/dist/development/src/model/service/idp/IDPManager.cjs.map +1 -1
  140. package/dist/development/src/model/service/idp/IDPManager.js +96 -20
  141. package/dist/development/src/model/service/idp/IDPManager.js.map +1 -1
  142. package/dist/development/src/model/service/idp/IdpSessionRevalidator.cjs +55 -1
  143. package/dist/development/src/model/service/idp/IdpSessionRevalidator.cjs.map +1 -1
  144. package/dist/development/src/model/service/idp/IdpSessionRevalidator.js +55 -1
  145. package/dist/development/src/model/service/idp/IdpSessionRevalidator.js.map +1 -1
  146. package/dist/development/src/model/service/idp/PersonIdentityProviderManager.cjs +6 -0
  147. package/dist/development/src/model/service/idp/PersonIdentityProviderManager.cjs.map +1 -1
  148. package/dist/development/src/model/service/idp/PersonIdentityProviderManager.js +6 -0
  149. package/dist/development/src/model/service/idp/PersonIdentityProviderManager.js.map +1 -1
  150. package/dist/development/src/model/service/idp/providers/OIDCHelpers.cjs +69 -33
  151. package/dist/development/src/model/service/idp/providers/OIDCHelpers.cjs.map +1 -1
  152. package/dist/development/src/model/service/idp/providers/OIDCHelpers.js +69 -33
  153. package/dist/development/src/model/service/idp/providers/OIDCHelpers.js.map +1 -1
  154. package/dist/development/src/model/service/idp/providers/OIDCProvider.cjs +4 -1
  155. package/dist/development/src/model/service/idp/providers/OIDCProvider.cjs.map +1 -1
  156. package/dist/development/src/model/service/idp/providers/OIDCProvider.js +4 -1
  157. package/dist/development/src/model/service/idp/providers/OIDCProvider.js.map +1 -1
  158. package/dist/development/src/model/utils/DatabaseContext.cjs +7 -2
  159. package/dist/development/src/model/utils/DatabaseContext.cjs.map +1 -1
  160. package/dist/development/src/model/utils/DatabaseContext.js +8 -3
  161. package/dist/development/src/model/utils/DatabaseContext.js.map +1 -1
  162. package/dist/development/src/resolvers/ResolverFactory.cjs +6 -2
  163. package/dist/development/src/resolvers/ResolverFactory.cjs.map +1 -1
  164. package/dist/development/src/resolvers/ResolverFactory.js +6 -2
  165. package/dist/development/src/resolvers/ResolverFactory.js.map +1 -1
  166. package/dist/development/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.cjs +2 -1
  167. package/dist/development/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.cjs.map +1 -1
  168. package/dist/development/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.js +2 -1
  169. package/dist/development/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.js.map +1 -1
  170. package/dist/development/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.cjs +2 -1
  171. package/dist/development/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.cjs.map +1 -1
  172. package/dist/development/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.js +2 -1
  173. package/dist/development/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.js.map +1 -1
  174. package/dist/development/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.cjs +2 -1
  175. package/dist/development/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.cjs.map +1 -1
  176. package/dist/development/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.js +2 -1
  177. package/dist/development/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.js.map +1 -1
  178. package/dist/development/src/resolvers/mutation/configuration/ConfigurationMutationResolver.cjs +2 -1
  179. package/dist/development/src/resolvers/mutation/configuration/ConfigurationMutationResolver.cjs.map +1 -1
  180. package/dist/development/src/resolvers/mutation/configuration/ConfigurationMutationResolver.js +2 -1
  181. package/dist/development/src/resolvers/mutation/configuration/ConfigurationMutationResolver.js.map +1 -1
  182. package/dist/development/src/resolvers/mutation/identity/GlobalRolesMutationResolver.cjs +2 -1
  183. package/dist/development/src/resolvers/mutation/identity/GlobalRolesMutationResolver.cjs.map +1 -1
  184. package/dist/development/src/resolvers/mutation/identity/GlobalRolesMutationResolver.js +2 -1
  185. package/dist/development/src/resolvers/mutation/identity/GlobalRolesMutationResolver.js.map +1 -1
  186. package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs +4 -2
  187. package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs.map +1 -1
  188. package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.js +4 -2
  189. package/dist/development/src/resolvers/mutation/idp/AddIDPMutationResolver.js.map +1 -1
  190. package/dist/development/src/resolvers/mutation/idp/DisableIDPMutationResolver.cjs +2 -1
  191. package/dist/development/src/resolvers/mutation/idp/DisableIDPMutationResolver.cjs.map +1 -1
  192. package/dist/development/src/resolvers/mutation/idp/DisableIDPMutationResolver.js +2 -1
  193. package/dist/development/src/resolvers/mutation/idp/DisableIDPMutationResolver.js.map +1 -1
  194. package/dist/development/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.cjs +2 -1
  195. package/dist/development/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.cjs.map +1 -1
  196. package/dist/development/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.js +2 -1
  197. package/dist/development/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.js.map +1 -1
  198. package/dist/development/src/resolvers/mutation/idp/EnableIDPMutationResolver.cjs +2 -1
  199. package/dist/development/src/resolvers/mutation/idp/EnableIDPMutationResolver.cjs.map +1 -1
  200. package/dist/development/src/resolvers/mutation/idp/EnableIDPMutationResolver.js +2 -1
  201. package/dist/development/src/resolvers/mutation/idp/EnableIDPMutationResolver.js.map +1 -1
  202. package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs +4 -2
  203. package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs.map +1 -1
  204. package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js +4 -2
  205. package/dist/development/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js.map +1 -1
  206. package/dist/development/src/resolvers/mutation/idp/audit.cjs +1 -0
  207. package/dist/development/src/resolvers/mutation/idp/audit.cjs.map +1 -1
  208. package/dist/development/src/resolvers/mutation/idp/audit.js +1 -0
  209. package/dist/development/src/resolvers/mutation/idp/audit.js.map +1 -1
  210. package/dist/development/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.cjs +2 -1
  211. package/dist/development/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.cjs.map +1 -1
  212. package/dist/development/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.js +2 -1
  213. package/dist/development/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.js.map +1 -1
  214. package/dist/development/src/resolvers/mutation/person/ChangePasswordMutationResolver.cjs +2 -1
  215. package/dist/development/src/resolvers/mutation/person/ChangePasswordMutationResolver.cjs.map +1 -1
  216. package/dist/development/src/resolvers/mutation/person/ChangePasswordMutationResolver.js +2 -1
  217. package/dist/development/src/resolvers/mutation/person/ChangePasswordMutationResolver.js.map +1 -1
  218. package/dist/development/src/resolvers/mutation/person/ChangeProfileMutationResolver.cjs +2 -1
  219. package/dist/development/src/resolvers/mutation/person/ChangeProfileMutationResolver.cjs.map +1 -1
  220. package/dist/development/src/resolvers/mutation/person/ChangeProfileMutationResolver.js +2 -1
  221. package/dist/development/src/resolvers/mutation/person/ChangeProfileMutationResolver.js.map +1 -1
  222. package/dist/development/src/resolvers/mutation/person/DisablePersonMutationResolver.cjs +4 -3
  223. package/dist/development/src/resolvers/mutation/person/DisablePersonMutationResolver.cjs.map +1 -1
  224. package/dist/development/src/resolvers/mutation/person/DisablePersonMutationResolver.js +4 -3
  225. package/dist/development/src/resolvers/mutation/person/DisablePersonMutationResolver.js.map +1 -1
  226. package/dist/development/src/resolvers/mutation/person/EmailOtpMutationResolver.cjs +2 -1
  227. package/dist/development/src/resolvers/mutation/person/EmailOtpMutationResolver.cjs.map +1 -1
  228. package/dist/development/src/resolvers/mutation/person/EmailOtpMutationResolver.js +2 -1
  229. package/dist/development/src/resolvers/mutation/person/EmailOtpMutationResolver.js.map +1 -1
  230. package/dist/development/src/resolvers/mutation/person/EmailVerificationMutationResolver.cjs +2 -1
  231. package/dist/development/src/resolvers/mutation/person/EmailVerificationMutationResolver.cjs.map +1 -1
  232. package/dist/development/src/resolvers/mutation/person/EmailVerificationMutationResolver.js +2 -1
  233. package/dist/development/src/resolvers/mutation/person/EmailVerificationMutationResolver.js.map +1 -1
  234. package/dist/development/src/resolvers/mutation/person/EnablePersonMutationResolver.cjs +60 -0
  235. package/dist/development/src/resolvers/mutation/person/EnablePersonMutationResolver.cjs.map +1 -0
  236. package/dist/development/src/resolvers/mutation/person/EnablePersonMutationResolver.js +60 -0
  237. package/dist/development/src/resolvers/mutation/person/EnablePersonMutationResolver.js.map +1 -0
  238. package/dist/development/src/resolvers/mutation/person/ForceSignOutMutationResolver.cjs +2 -1
  239. package/dist/development/src/resolvers/mutation/person/ForceSignOutMutationResolver.cjs.map +1 -1
  240. package/dist/development/src/resolvers/mutation/person/ForceSignOutMutationResolver.js +2 -1
  241. package/dist/development/src/resolvers/mutation/person/ForceSignOutMutationResolver.js.map +1 -1
  242. package/dist/development/src/resolvers/mutation/person/IDPMutationResolver.cjs +27 -1
  243. package/dist/development/src/resolvers/mutation/person/IDPMutationResolver.cjs.map +1 -1
  244. package/dist/development/src/resolvers/mutation/person/IDPMutationResolver.js +27 -1
  245. package/dist/development/src/resolvers/mutation/person/IDPMutationResolver.js.map +1 -1
  246. package/dist/development/src/resolvers/mutation/person/InviteMutationResolver.cjs +2 -1
  247. package/dist/development/src/resolvers/mutation/person/InviteMutationResolver.cjs.map +1 -1
  248. package/dist/development/src/resolvers/mutation/person/InviteMutationResolver.js +2 -1
  249. package/dist/development/src/resolvers/mutation/person/InviteMutationResolver.js.map +1 -1
  250. package/dist/development/src/resolvers/mutation/person/OtpMutationResolver.cjs +2 -1
  251. package/dist/development/src/resolvers/mutation/person/OtpMutationResolver.cjs.map +1 -1
  252. package/dist/development/src/resolvers/mutation/person/OtpMutationResolver.js +2 -1
  253. package/dist/development/src/resolvers/mutation/person/OtpMutationResolver.js.map +1 -1
  254. package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs +6 -1
  255. package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs.map +1 -1
  256. package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.js +6 -1
  257. package/dist/development/src/resolvers/mutation/person/PasswordlessMutationResolver.js.map +1 -1
  258. package/dist/development/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.cjs +2 -1
  259. package/dist/development/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.cjs.map +1 -1
  260. package/dist/development/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.js +2 -1
  261. package/dist/development/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.js.map +1 -1
  262. package/dist/development/src/resolvers/mutation/person/ResetPasswordMutationResolver.cjs +2 -1
  263. package/dist/development/src/resolvers/mutation/person/ResetPasswordMutationResolver.cjs.map +1 -1
  264. package/dist/development/src/resolvers/mutation/person/ResetPasswordMutationResolver.js +2 -1
  265. package/dist/development/src/resolvers/mutation/person/ResetPasswordMutationResolver.js.map +1 -1
  266. package/dist/development/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.cjs +2 -1
  267. package/dist/development/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.cjs.map +1 -1
  268. package/dist/development/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.js +2 -1
  269. package/dist/development/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.js.map +1 -1
  270. package/dist/development/src/resolvers/mutation/person/RevokeSessionMutationResolver.cjs +2 -1
  271. package/dist/development/src/resolvers/mutation/person/RevokeSessionMutationResolver.cjs.map +1 -1
  272. package/dist/development/src/resolvers/mutation/person/RevokeSessionMutationResolver.js +2 -1
  273. package/dist/development/src/resolvers/mutation/person/RevokeSessionMutationResolver.js.map +1 -1
  274. package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.cjs +4 -3
  275. package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.cjs.map +1 -1
  276. package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.js +4 -3
  277. package/dist/development/src/resolvers/mutation/person/SignInMutationResolver.js.map +1 -1
  278. package/dist/development/src/resolvers/mutation/person/SignOutMutationResolver.cjs +2 -1
  279. package/dist/development/src/resolvers/mutation/person/SignOutMutationResolver.cjs.map +1 -1
  280. package/dist/development/src/resolvers/mutation/person/SignOutMutationResolver.js +2 -1
  281. package/dist/development/src/resolvers/mutation/person/SignOutMutationResolver.js.map +1 -1
  282. package/dist/development/src/resolvers/mutation/person/SignUpMutationResolver.cjs +2 -1
  283. package/dist/development/src/resolvers/mutation/person/SignUpMutationResolver.cjs.map +1 -1
  284. package/dist/development/src/resolvers/mutation/person/SignUpMutationResolver.js +2 -1
  285. package/dist/development/src/resolvers/mutation/person/SignUpMutationResolver.js.map +1 -1
  286. package/dist/development/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.cjs +2 -1
  287. package/dist/development/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.cjs.map +1 -1
  288. package/dist/development/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.js +2 -1
  289. package/dist/development/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.js.map +1 -1
  290. package/dist/development/src/resolvers/mutation/project/CreateProjectMutationResolver.cjs +2 -1
  291. package/dist/development/src/resolvers/mutation/project/CreateProjectMutationResolver.cjs.map +1 -1
  292. package/dist/development/src/resolvers/mutation/project/CreateProjectMutationResolver.js +2 -1
  293. package/dist/development/src/resolvers/mutation/project/CreateProjectMutationResolver.js.map +1 -1
  294. package/dist/development/src/resolvers/mutation/project/SetProjectSecretMutationResolver.cjs +2 -1
  295. package/dist/development/src/resolvers/mutation/project/SetProjectSecretMutationResolver.cjs.map +1 -1
  296. package/dist/development/src/resolvers/mutation/project/SetProjectSecretMutationResolver.js +2 -1
  297. package/dist/development/src/resolvers/mutation/project/SetProjectSecretMutationResolver.js.map +1 -1
  298. package/dist/development/src/resolvers/mutation/project/UpdateProjectMutationResolver.cjs +2 -1
  299. package/dist/development/src/resolvers/mutation/project/UpdateProjectMutationResolver.cjs.map +1 -1
  300. package/dist/development/src/resolvers/mutation/project/UpdateProjectMutationResolver.js +2 -1
  301. package/dist/development/src/resolvers/mutation/project/UpdateProjectMutationResolver.js.map +1 -1
  302. package/dist/development/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.cjs +2 -1
  303. package/dist/development/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.cjs.map +1 -1
  304. package/dist/development/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.js +2 -1
  305. package/dist/development/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.js.map +1 -1
  306. package/dist/development/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.cjs +2 -1
  307. package/dist/development/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.cjs.map +1 -1
  308. package/dist/development/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.js +2 -1
  309. package/dist/development/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.js.map +1 -1
  310. package/dist/development/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.cjs +2 -1
  311. package/dist/development/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.cjs.map +1 -1
  312. package/dist/development/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.js +2 -1
  313. package/dist/development/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.js.map +1 -1
  314. package/dist/development/src/resolvers/query/ApiKeyQueryResolver.cjs +2 -1
  315. package/dist/development/src/resolvers/query/ApiKeyQueryResolver.cjs.map +1 -1
  316. package/dist/development/src/resolvers/query/ApiKeyQueryResolver.js +2 -1
  317. package/dist/development/src/resolvers/query/ApiKeyQueryResolver.js.map +1 -1
  318. package/dist/development/src/resolvers/query/AuthLogQueryResolver.cjs +2 -1
  319. package/dist/development/src/resolvers/query/AuthLogQueryResolver.cjs.map +1 -1
  320. package/dist/development/src/resolvers/query/AuthLogQueryResolver.js +2 -1
  321. package/dist/development/src/resolvers/query/AuthLogQueryResolver.js.map +1 -1
  322. package/dist/development/src/resolvers/query/AuthPolicyQueryResolver.cjs +2 -1
  323. package/dist/development/src/resolvers/query/AuthPolicyQueryResolver.cjs.map +1 -1
  324. package/dist/development/src/resolvers/query/AuthPolicyQueryResolver.js +2 -1
  325. package/dist/development/src/resolvers/query/AuthPolicyQueryResolver.js.map +1 -1
  326. package/dist/development/src/resolvers/query/ConfigurationQueryResolver.cjs +2 -1
  327. package/dist/development/src/resolvers/query/ConfigurationQueryResolver.cjs.map +1 -1
  328. package/dist/development/src/resolvers/query/ConfigurationQueryResolver.js +2 -1
  329. package/dist/development/src/resolvers/query/ConfigurationQueryResolver.js.map +1 -1
  330. package/dist/development/src/resolvers/query/IDPQueryResolver.cjs +2 -1
  331. package/dist/development/src/resolvers/query/IDPQueryResolver.cjs.map +1 -1
  332. package/dist/development/src/resolvers/query/IDPQueryResolver.js +2 -1
  333. package/dist/development/src/resolvers/query/IDPQueryResolver.js.map +1 -1
  334. package/dist/development/src/resolvers/query/MailTemplateQueryResolver.cjs +2 -1
  335. package/dist/development/src/resolvers/query/MailTemplateQueryResolver.cjs.map +1 -1
  336. package/dist/development/src/resolvers/query/MailTemplateQueryResolver.js +2 -1
  337. package/dist/development/src/resolvers/query/MailTemplateQueryResolver.js.map +1 -1
  338. package/dist/development/src/resolvers/query/PersonQueryResolver.cjs +2 -1
  339. package/dist/development/src/resolvers/query/PersonQueryResolver.cjs.map +1 -1
  340. package/dist/development/src/resolvers/query/PersonQueryResolver.js +2 -1
  341. package/dist/development/src/resolvers/query/PersonQueryResolver.js.map +1 -1
  342. package/dist/development/src/resolvers/query/ProjectMembersQueryResolver.cjs +2 -1
  343. package/dist/development/src/resolvers/query/ProjectMembersQueryResolver.cjs.map +1 -1
  344. package/dist/development/src/resolvers/query/ProjectMembersQueryResolver.js +2 -1
  345. package/dist/development/src/resolvers/query/ProjectMembersQueryResolver.js.map +1 -1
  346. package/dist/development/src/resolvers/query/ProjectQueryResolver.cjs +5 -3
  347. package/dist/development/src/resolvers/query/ProjectQueryResolver.cjs.map +1 -1
  348. package/dist/development/src/resolvers/query/ProjectQueryResolver.js +5 -3
  349. package/dist/development/src/resolvers/query/ProjectQueryResolver.js.map +1 -1
  350. package/dist/development/src/resolvers/responseHelpers/ApiKeyResponseFactory.cjs +2 -1
  351. package/dist/development/src/resolvers/responseHelpers/ApiKeyResponseFactory.cjs.map +1 -1
  352. package/dist/development/src/resolvers/responseHelpers/ApiKeyResponseFactory.js +2 -1
  353. package/dist/development/src/resolvers/responseHelpers/ApiKeyResponseFactory.js.map +1 -1
  354. package/dist/development/src/resolvers/responseHelpers/PersonResponseFactory.cjs +5 -0
  355. package/dist/development/src/resolvers/responseHelpers/PersonResponseFactory.cjs.map +1 -1
  356. package/dist/development/src/resolvers/responseHelpers/PersonResponseFactory.js +5 -0
  357. package/dist/development/src/resolvers/responseHelpers/PersonResponseFactory.js.map +1 -1
  358. package/dist/development/src/resolvers/responseHelpers/ProjectResponseFactory.cjs +23 -0
  359. package/dist/development/src/resolvers/responseHelpers/ProjectResponseFactory.cjs.map +1 -0
  360. package/dist/development/src/resolvers/responseHelpers/ProjectResponseFactory.js +23 -0
  361. package/dist/development/src/resolvers/responseHelpers/ProjectResponseFactory.js.map +1 -0
  362. package/dist/development/src/resolvers/responseHelpers/SignInResponseFactory.cjs +2 -1
  363. package/dist/development/src/resolvers/responseHelpers/SignInResponseFactory.cjs.map +1 -1
  364. package/dist/development/src/resolvers/responseHelpers/SignInResponseFactory.js +2 -1
  365. package/dist/development/src/resolvers/responseHelpers/SignInResponseFactory.js.map +1 -1
  366. package/dist/development/src/resolvers/types/IdentityTypeResolver.cjs +37 -4
  367. package/dist/development/src/resolvers/types/IdentityTypeResolver.cjs.map +1 -1
  368. package/dist/development/src/resolvers/types/IdentityTypeResolver.js +37 -4
  369. package/dist/development/src/resolvers/types/IdentityTypeResolver.js.map +1 -1
  370. package/dist/development/src/resolvers/types/PersonTypeResolver.cjs +30 -1
  371. package/dist/development/src/resolvers/types/PersonTypeResolver.cjs.map +1 -1
  372. package/dist/development/src/resolvers/types/PersonTypeResolver.js +30 -1
  373. package/dist/development/src/resolvers/types/PersonTypeResolver.js.map +1 -1
  374. package/dist/development/src/resolvers/types/ProjectTypeResolver.cjs +25 -1
  375. package/dist/development/src/resolvers/types/ProjectTypeResolver.cjs.map +1 -1
  376. package/dist/development/src/resolvers/types/ProjectTypeResolver.js +25 -1
  377. package/dist/development/src/resolvers/types/ProjectTypeResolver.js.map +1 -1
  378. package/dist/development/src/schema/tenant.graphql.cjs +144 -0
  379. package/dist/development/src/schema/tenant.graphql.cjs.map +1 -1
  380. package/dist/development/src/schema/tenant.graphql.js +144 -0
  381. package/dist/development/src/schema/tenant.graphql.js.map +1 -1
  382. package/dist/production/index.cjs +32 -0
  383. package/dist/production/index.cjs.map +1 -1
  384. package/dist/production/index.js +33 -1
  385. package/dist/production/index.js.map +1 -1
  386. package/dist/production/src/TenantContainer.cjs +15 -3
  387. package/dist/production/src/TenantContainer.cjs.map +1 -1
  388. package/dist/production/src/TenantContainer.js +15 -3
  389. package/dist/production/src/TenantContainer.js.map +1 -1
  390. package/dist/production/src/bridges/system/IdentityFetcher.cjs +2 -1
  391. package/dist/production/src/bridges/system/IdentityFetcher.cjs.map +1 -1
  392. package/dist/production/src/bridges/system/IdentityFetcher.js +2 -1
  393. package/dist/production/src/bridges/system/IdentityFetcher.js.map +1 -1
  394. package/dist/production/src/migrations/2026-06-18-120000-idp-claim-mapping.cjs +10 -0
  395. package/dist/production/src/migrations/2026-06-18-120000-idp-claim-mapping.cjs.map +1 -0
  396. package/dist/production/src/migrations/2026-06-18-120000-idp-claim-mapping.js +11 -0
  397. package/dist/production/src/migrations/2026-06-18-120000-idp-claim-mapping.js.map +1 -0
  398. package/dist/production/src/migrations/2026-08-03-120000-person-enable-auth-log.cjs +9 -0
  399. package/dist/production/src/migrations/2026-08-03-120000-person-enable-auth-log.cjs.map +1 -0
  400. package/dist/production/src/migrations/2026-08-03-120000-person-enable-auth-log.js +10 -0
  401. package/dist/production/src/migrations/2026-08-03-120000-person-enable-auth-log.js.map +1 -0
  402. package/dist/production/src/migrations/2026-08-04-120000-config-panel.cjs +11 -0
  403. package/dist/production/src/migrations/2026-08-04-120000-config-panel.cjs.map +1 -0
  404. package/dist/production/src/migrations/2026-08-04-120000-config-panel.js +12 -0
  405. package/dist/production/src/migrations/2026-08-04-120000-config-panel.js.map +1 -0
  406. package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs +18 -0
  407. package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.cjs.map +1 -0
  408. package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js +19 -0
  409. package/dist/production/src/migrations/2026-09-04-120000-idp-disable-local-authentication.js.map +1 -0
  410. package/dist/production/src/migrations/runner.cjs +11 -2
  411. package/dist/production/src/migrations/runner.cjs.map +1 -1
  412. package/dist/production/src/migrations/runner.js +11 -2
  413. package/dist/production/src/migrations/runner.js.map +1 -1
  414. package/dist/production/src/migrations/snapshot.cjs +8 -2
  415. package/dist/production/src/migrations/snapshot.cjs.map +1 -1
  416. package/dist/production/src/migrations/snapshot.js +8 -2
  417. package/dist/production/src/migrations/snapshot.js.map +1 -1
  418. package/dist/production/src/model/commands/configuration/UpdateConfigurationCommand.cjs +5 -3
  419. package/dist/production/src/model/commands/configuration/UpdateConfigurationCommand.cjs.map +1 -1
  420. package/dist/production/src/model/commands/configuration/UpdateConfigurationCommand.js +5 -3
  421. package/dist/production/src/model/commands/configuration/UpdateConfigurationCommand.js.map +1 -1
  422. package/dist/production/src/model/commands/idp/CreateIdpCommand.cjs +8 -2
  423. package/dist/production/src/model/commands/idp/CreateIdpCommand.cjs.map +1 -1
  424. package/dist/production/src/model/commands/idp/CreateIdpCommand.js +8 -2
  425. package/dist/production/src/model/commands/idp/CreateIdpCommand.js.map +1 -1
  426. package/dist/production/src/model/commands/idp/UpdateIdpCommand.cjs +6 -2
  427. package/dist/production/src/model/commands/idp/UpdateIdpCommand.cjs.map +1 -1
  428. package/dist/production/src/model/commands/idp/UpdateIdpCommand.js +6 -2
  429. package/dist/production/src/model/commands/idp/UpdateIdpCommand.js.map +1 -1
  430. package/dist/production/src/model/commands/membership/{RemoveProjectMembershipsCommand.cjs → RemoveProjectMembershipCommand.cjs} +1 -1
  431. package/dist/production/src/model/commands/membership/RemoveProjectMembershipCommand.cjs.map +1 -0
  432. package/dist/production/src/model/commands/membership/{RemoveProjectMembershipsCommand.js → RemoveProjectMembershipCommand.js} +1 -1
  433. package/dist/production/src/model/commands/membership/RemoveProjectMembershipCommand.js.map +1 -0
  434. package/dist/production/src/model/commands/membership/UpdateProjectMemberCommand.cjs +2 -2
  435. package/dist/production/src/model/commands/membership/UpdateProjectMemberCommand.cjs.map +1 -1
  436. package/dist/production/src/model/commands/membership/UpdateProjectMemberCommand.js +1 -1
  437. package/dist/production/src/model/commands/membership/UpdateProjectMemberCommand.js.map +1 -1
  438. package/dist/production/src/model/commands/person/EnablePersonCommand.cjs +17 -0
  439. package/dist/production/src/model/commands/person/EnablePersonCommand.cjs.map +1 -0
  440. package/dist/production/src/model/commands/person/EnablePersonCommand.js +17 -0
  441. package/dist/production/src/model/commands/person/EnablePersonCommand.js.map +1 -0
  442. package/dist/production/src/model/queries/configuration/ConfigurationQuery.cjs +4 -0
  443. package/dist/production/src/model/queries/configuration/ConfigurationQuery.cjs.map +1 -1
  444. package/dist/production/src/model/queries/configuration/ConfigurationQuery.js +4 -0
  445. package/dist/production/src/model/queries/configuration/ConfigurationQuery.js.map +1 -1
  446. package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs +21 -0
  447. package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.cjs.map +1 -0
  448. package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js +21 -0
  449. package/dist/production/src/model/queries/idp/LocalAuthDisablingIdpsQuery.js.map +1 -0
  450. package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.cjs +1 -1
  451. package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.cjs.map +1 -1
  452. package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.js +1 -1
  453. package/dist/production/src/model/queries/idp/PersonIdentityProvidersQuery.js.map +1 -1
  454. package/dist/production/src/model/queries/idp/helpers.cjs +3 -3
  455. package/dist/production/src/model/queries/idp/helpers.cjs.map +1 -1
  456. package/dist/production/src/model/queries/idp/helpers.js +3 -3
  457. package/dist/production/src/model/queries/idp/helpers.js.map +1 -1
  458. package/dist/production/src/model/queries/membership/ProjectMembersQuery.cjs +1 -1
  459. package/dist/production/src/model/queries/membership/ProjectMembersQuery.cjs.map +1 -1
  460. package/dist/production/src/model/queries/membership/ProjectMembersQuery.js +1 -1
  461. package/dist/production/src/model/queries/membership/ProjectMembersQuery.js.map +1 -1
  462. package/dist/production/src/model/queries/person/PersonQueryBuilderFactory.cjs +1 -1
  463. package/dist/production/src/model/queries/person/PersonQueryBuilderFactory.cjs.map +1 -1
  464. package/dist/production/src/model/queries/person/PersonQueryBuilderFactory.js +1 -1
  465. package/dist/production/src/model/queries/person/PersonQueryBuilderFactory.js.map +1 -1
  466. package/dist/production/src/model/service/AuthLogService.cjs +8 -2
  467. package/dist/production/src/model/service/AuthLogService.cjs.map +1 -1
  468. package/dist/production/src/model/service/AuthLogService.js +8 -2
  469. package/dist/production/src/model/service/AuthLogService.js.map +1 -1
  470. package/dist/production/src/model/service/IDPSignInManager.cjs +29 -2
  471. package/dist/production/src/model/service/IDPSignInManager.cjs.map +1 -1
  472. package/dist/production/src/model/service/IDPSignInManager.js +29 -2
  473. package/dist/production/src/model/service/IDPSignInManager.js.map +1 -1
  474. package/dist/production/src/model/service/PanelAccessResolver.cjs +31 -0
  475. package/dist/production/src/model/service/PanelAccessResolver.cjs.map +1 -0
  476. package/dist/production/src/model/service/PanelAccessResolver.js +31 -0
  477. package/dist/production/src/model/service/PanelAccessResolver.js.map +1 -0
  478. package/dist/production/src/model/service/PasswordResetManager.cjs +5 -0
  479. package/dist/production/src/model/service/PasswordResetManager.cjs.map +1 -1
  480. package/dist/production/src/model/service/PasswordResetManager.js +5 -0
  481. package/dist/production/src/model/service/PasswordResetManager.js.map +1 -1
  482. package/dist/production/src/model/service/PasswordlessSignInManager.cjs +19 -0
  483. package/dist/production/src/model/service/PasswordlessSignInManager.cjs.map +1 -1
  484. package/dist/production/src/model/service/PasswordlessSignInManager.js +19 -0
  485. package/dist/production/src/model/service/PasswordlessSignInManager.js.map +1 -1
  486. package/dist/production/src/model/service/PersonAccessManager.cjs +10 -2
  487. package/dist/production/src/model/service/PersonAccessManager.cjs.map +1 -1
  488. package/dist/production/src/model/service/PersonAccessManager.js +10 -2
  489. package/dist/production/src/model/service/PersonAccessManager.js.map +1 -1
  490. package/dist/production/src/model/service/SignInManager.cjs +5 -0
  491. package/dist/production/src/model/service/SignInManager.cjs.map +1 -1
  492. package/dist/production/src/model/service/SignInManager.js +5 -0
  493. package/dist/production/src/model/service/SignInManager.js.map +1 -1
  494. package/dist/production/src/model/service/apiKey/ApiKeyManager.cjs +4 -3
  495. package/dist/production/src/model/service/apiKey/ApiKeyManager.cjs.map +1 -1
  496. package/dist/production/src/model/service/apiKey/ApiKeyManager.js +4 -3
  497. package/dist/production/src/model/service/apiKey/ApiKeyManager.js.map +1 -1
  498. package/dist/production/src/model/service/apiKey/UnpersistedApiKeyManager.cjs +7 -0
  499. package/dist/production/src/model/service/apiKey/UnpersistedApiKeyManager.cjs.map +1 -1
  500. package/dist/production/src/model/service/apiKey/UnpersistedApiKeyManager.js +8 -1
  501. package/dist/production/src/model/service/apiKey/UnpersistedApiKeyManager.js.map +1 -1
  502. package/dist/production/src/model/service/idp/ClaimMapping.cjs +187 -0
  503. package/dist/production/src/model/service/idp/ClaimMapping.cjs.map +1 -0
  504. package/dist/production/src/model/service/idp/ClaimMapping.js +170 -0
  505. package/dist/production/src/model/service/idp/ClaimMapping.js.map +1 -0
  506. package/dist/production/src/model/service/idp/ClaimMappingEvaluator.cjs +155 -0
  507. package/dist/production/src/model/service/idp/ClaimMappingEvaluator.cjs.map +1 -0
  508. package/dist/production/src/model/service/idp/ClaimMappingEvaluator.js +155 -0
  509. package/dist/production/src/model/service/idp/ClaimMappingEvaluator.js.map +1 -0
  510. package/dist/production/src/model/service/idp/ClaimMappingValidation.cjs +83 -0
  511. package/dist/production/src/model/service/idp/ClaimMappingValidation.cjs.map +1 -0
  512. package/dist/production/src/model/service/idp/ClaimMappingValidation.js +83 -0
  513. package/dist/production/src/model/service/idp/ClaimMappingValidation.js.map +1 -0
  514. package/dist/production/src/model/service/idp/IDPClaimSyncService.cjs +260 -0
  515. package/dist/production/src/model/service/idp/IDPClaimSyncService.cjs.map +1 -0
  516. package/dist/production/src/model/service/idp/IDPClaimSyncService.js +260 -0
  517. package/dist/production/src/model/service/idp/IDPClaimSyncService.js.map +1 -0
  518. package/dist/production/src/model/service/idp/IDPManager.cjs +96 -20
  519. package/dist/production/src/model/service/idp/IDPManager.cjs.map +1 -1
  520. package/dist/production/src/model/service/idp/IDPManager.js +96 -20
  521. package/dist/production/src/model/service/idp/IDPManager.js.map +1 -1
  522. package/dist/production/src/model/service/idp/IdpSessionRevalidator.cjs +55 -1
  523. package/dist/production/src/model/service/idp/IdpSessionRevalidator.cjs.map +1 -1
  524. package/dist/production/src/model/service/idp/IdpSessionRevalidator.js +55 -1
  525. package/dist/production/src/model/service/idp/IdpSessionRevalidator.js.map +1 -1
  526. package/dist/production/src/model/service/idp/PersonIdentityProviderManager.cjs +6 -0
  527. package/dist/production/src/model/service/idp/PersonIdentityProviderManager.cjs.map +1 -1
  528. package/dist/production/src/model/service/idp/PersonIdentityProviderManager.js +6 -0
  529. package/dist/production/src/model/service/idp/PersonIdentityProviderManager.js.map +1 -1
  530. package/dist/production/src/model/service/idp/providers/OIDCHelpers.cjs +69 -33
  531. package/dist/production/src/model/service/idp/providers/OIDCHelpers.cjs.map +1 -1
  532. package/dist/production/src/model/service/idp/providers/OIDCHelpers.js +69 -33
  533. package/dist/production/src/model/service/idp/providers/OIDCHelpers.js.map +1 -1
  534. package/dist/production/src/model/service/idp/providers/OIDCProvider.cjs +4 -1
  535. package/dist/production/src/model/service/idp/providers/OIDCProvider.cjs.map +1 -1
  536. package/dist/production/src/model/service/idp/providers/OIDCProvider.js +4 -1
  537. package/dist/production/src/model/service/idp/providers/OIDCProvider.js.map +1 -1
  538. package/dist/production/src/model/utils/DatabaseContext.cjs +7 -2
  539. package/dist/production/src/model/utils/DatabaseContext.cjs.map +1 -1
  540. package/dist/production/src/model/utils/DatabaseContext.js +8 -3
  541. package/dist/production/src/model/utils/DatabaseContext.js.map +1 -1
  542. package/dist/production/src/resolvers/ResolverFactory.cjs +6 -2
  543. package/dist/production/src/resolvers/ResolverFactory.cjs.map +1 -1
  544. package/dist/production/src/resolvers/ResolverFactory.js +6 -2
  545. package/dist/production/src/resolvers/ResolverFactory.js.map +1 -1
  546. package/dist/production/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.cjs +2 -1
  547. package/dist/production/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.cjs.map +1 -1
  548. package/dist/production/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.js +2 -1
  549. package/dist/production/src/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.js.map +1 -1
  550. package/dist/production/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.cjs +2 -1
  551. package/dist/production/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.cjs.map +1 -1
  552. package/dist/production/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.js +2 -1
  553. package/dist/production/src/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.js.map +1 -1
  554. package/dist/production/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.cjs +2 -1
  555. package/dist/production/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.cjs.map +1 -1
  556. package/dist/production/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.js +2 -1
  557. package/dist/production/src/resolvers/mutation/configuration/AuthPolicyMutationResolver.js.map +1 -1
  558. package/dist/production/src/resolvers/mutation/configuration/ConfigurationMutationResolver.cjs +2 -1
  559. package/dist/production/src/resolvers/mutation/configuration/ConfigurationMutationResolver.cjs.map +1 -1
  560. package/dist/production/src/resolvers/mutation/configuration/ConfigurationMutationResolver.js +2 -1
  561. package/dist/production/src/resolvers/mutation/configuration/ConfigurationMutationResolver.js.map +1 -1
  562. package/dist/production/src/resolvers/mutation/identity/GlobalRolesMutationResolver.cjs +2 -1
  563. package/dist/production/src/resolvers/mutation/identity/GlobalRolesMutationResolver.cjs.map +1 -1
  564. package/dist/production/src/resolvers/mutation/identity/GlobalRolesMutationResolver.js +2 -1
  565. package/dist/production/src/resolvers/mutation/identity/GlobalRolesMutationResolver.js.map +1 -1
  566. package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs +4 -2
  567. package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.cjs.map +1 -1
  568. package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.js +4 -2
  569. package/dist/production/src/resolvers/mutation/idp/AddIDPMutationResolver.js.map +1 -1
  570. package/dist/production/src/resolvers/mutation/idp/DisableIDPMutationResolver.cjs +2 -1
  571. package/dist/production/src/resolvers/mutation/idp/DisableIDPMutationResolver.cjs.map +1 -1
  572. package/dist/production/src/resolvers/mutation/idp/DisableIDPMutationResolver.js +2 -1
  573. package/dist/production/src/resolvers/mutation/idp/DisableIDPMutationResolver.js.map +1 -1
  574. package/dist/production/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.cjs +2 -1
  575. package/dist/production/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.cjs.map +1 -1
  576. package/dist/production/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.js +2 -1
  577. package/dist/production/src/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.js.map +1 -1
  578. package/dist/production/src/resolvers/mutation/idp/EnableIDPMutationResolver.cjs +2 -1
  579. package/dist/production/src/resolvers/mutation/idp/EnableIDPMutationResolver.cjs.map +1 -1
  580. package/dist/production/src/resolvers/mutation/idp/EnableIDPMutationResolver.js +2 -1
  581. package/dist/production/src/resolvers/mutation/idp/EnableIDPMutationResolver.js.map +1 -1
  582. package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs +4 -2
  583. package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.cjs.map +1 -1
  584. package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js +4 -2
  585. package/dist/production/src/resolvers/mutation/idp/UpdateIDPMutationResolver.js.map +1 -1
  586. package/dist/production/src/resolvers/mutation/idp/audit.cjs +1 -0
  587. package/dist/production/src/resolvers/mutation/idp/audit.cjs.map +1 -1
  588. package/dist/production/src/resolvers/mutation/idp/audit.js +1 -0
  589. package/dist/production/src/resolvers/mutation/idp/audit.js.map +1 -1
  590. package/dist/production/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.cjs +2 -1
  591. package/dist/production/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.cjs.map +1 -1
  592. package/dist/production/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.js +2 -1
  593. package/dist/production/src/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.js.map +1 -1
  594. package/dist/production/src/resolvers/mutation/person/ChangePasswordMutationResolver.cjs +2 -1
  595. package/dist/production/src/resolvers/mutation/person/ChangePasswordMutationResolver.cjs.map +1 -1
  596. package/dist/production/src/resolvers/mutation/person/ChangePasswordMutationResolver.js +2 -1
  597. package/dist/production/src/resolvers/mutation/person/ChangePasswordMutationResolver.js.map +1 -1
  598. package/dist/production/src/resolvers/mutation/person/ChangeProfileMutationResolver.cjs +2 -1
  599. package/dist/production/src/resolvers/mutation/person/ChangeProfileMutationResolver.cjs.map +1 -1
  600. package/dist/production/src/resolvers/mutation/person/ChangeProfileMutationResolver.js +2 -1
  601. package/dist/production/src/resolvers/mutation/person/ChangeProfileMutationResolver.js.map +1 -1
  602. package/dist/production/src/resolvers/mutation/person/DisablePersonMutationResolver.cjs +4 -3
  603. package/dist/production/src/resolvers/mutation/person/DisablePersonMutationResolver.cjs.map +1 -1
  604. package/dist/production/src/resolvers/mutation/person/DisablePersonMutationResolver.js +4 -3
  605. package/dist/production/src/resolvers/mutation/person/DisablePersonMutationResolver.js.map +1 -1
  606. package/dist/production/src/resolvers/mutation/person/EmailOtpMutationResolver.cjs +2 -1
  607. package/dist/production/src/resolvers/mutation/person/EmailOtpMutationResolver.cjs.map +1 -1
  608. package/dist/production/src/resolvers/mutation/person/EmailOtpMutationResolver.js +2 -1
  609. package/dist/production/src/resolvers/mutation/person/EmailOtpMutationResolver.js.map +1 -1
  610. package/dist/production/src/resolvers/mutation/person/EmailVerificationMutationResolver.cjs +2 -1
  611. package/dist/production/src/resolvers/mutation/person/EmailVerificationMutationResolver.cjs.map +1 -1
  612. package/dist/production/src/resolvers/mutation/person/EmailVerificationMutationResolver.js +2 -1
  613. package/dist/production/src/resolvers/mutation/person/EmailVerificationMutationResolver.js.map +1 -1
  614. package/dist/production/src/resolvers/mutation/person/EnablePersonMutationResolver.cjs +60 -0
  615. package/dist/production/src/resolvers/mutation/person/EnablePersonMutationResolver.cjs.map +1 -0
  616. package/dist/production/src/resolvers/mutation/person/EnablePersonMutationResolver.js +60 -0
  617. package/dist/production/src/resolvers/mutation/person/EnablePersonMutationResolver.js.map +1 -0
  618. package/dist/production/src/resolvers/mutation/person/ForceSignOutMutationResolver.cjs +2 -1
  619. package/dist/production/src/resolvers/mutation/person/ForceSignOutMutationResolver.cjs.map +1 -1
  620. package/dist/production/src/resolvers/mutation/person/ForceSignOutMutationResolver.js +2 -1
  621. package/dist/production/src/resolvers/mutation/person/ForceSignOutMutationResolver.js.map +1 -1
  622. package/dist/production/src/resolvers/mutation/person/IDPMutationResolver.cjs +27 -1
  623. package/dist/production/src/resolvers/mutation/person/IDPMutationResolver.cjs.map +1 -1
  624. package/dist/production/src/resolvers/mutation/person/IDPMutationResolver.js +27 -1
  625. package/dist/production/src/resolvers/mutation/person/IDPMutationResolver.js.map +1 -1
  626. package/dist/production/src/resolvers/mutation/person/InviteMutationResolver.cjs +2 -1
  627. package/dist/production/src/resolvers/mutation/person/InviteMutationResolver.cjs.map +1 -1
  628. package/dist/production/src/resolvers/mutation/person/InviteMutationResolver.js +2 -1
  629. package/dist/production/src/resolvers/mutation/person/InviteMutationResolver.js.map +1 -1
  630. package/dist/production/src/resolvers/mutation/person/OtpMutationResolver.cjs +2 -1
  631. package/dist/production/src/resolvers/mutation/person/OtpMutationResolver.cjs.map +1 -1
  632. package/dist/production/src/resolvers/mutation/person/OtpMutationResolver.js +2 -1
  633. package/dist/production/src/resolvers/mutation/person/OtpMutationResolver.js.map +1 -1
  634. package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs +6 -1
  635. package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.cjs.map +1 -1
  636. package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.js +6 -1
  637. package/dist/production/src/resolvers/mutation/person/PasswordlessMutationResolver.js.map +1 -1
  638. package/dist/production/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.cjs +2 -1
  639. package/dist/production/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.cjs.map +1 -1
  640. package/dist/production/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.js +2 -1
  641. package/dist/production/src/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.js.map +1 -1
  642. package/dist/production/src/resolvers/mutation/person/ResetPasswordMutationResolver.cjs +2 -1
  643. package/dist/production/src/resolvers/mutation/person/ResetPasswordMutationResolver.cjs.map +1 -1
  644. package/dist/production/src/resolvers/mutation/person/ResetPasswordMutationResolver.js +2 -1
  645. package/dist/production/src/resolvers/mutation/person/ResetPasswordMutationResolver.js.map +1 -1
  646. package/dist/production/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.cjs +2 -1
  647. package/dist/production/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.cjs.map +1 -1
  648. package/dist/production/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.js +2 -1
  649. package/dist/production/src/resolvers/mutation/person/ResetPersonMfaMutationResolver.js.map +1 -1
  650. package/dist/production/src/resolvers/mutation/person/RevokeSessionMutationResolver.cjs +2 -1
  651. package/dist/production/src/resolvers/mutation/person/RevokeSessionMutationResolver.cjs.map +1 -1
  652. package/dist/production/src/resolvers/mutation/person/RevokeSessionMutationResolver.js +2 -1
  653. package/dist/production/src/resolvers/mutation/person/RevokeSessionMutationResolver.js.map +1 -1
  654. package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.cjs +4 -3
  655. package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.cjs.map +1 -1
  656. package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.js +4 -3
  657. package/dist/production/src/resolvers/mutation/person/SignInMutationResolver.js.map +1 -1
  658. package/dist/production/src/resolvers/mutation/person/SignOutMutationResolver.cjs +2 -1
  659. package/dist/production/src/resolvers/mutation/person/SignOutMutationResolver.cjs.map +1 -1
  660. package/dist/production/src/resolvers/mutation/person/SignOutMutationResolver.js +2 -1
  661. package/dist/production/src/resolvers/mutation/person/SignOutMutationResolver.js.map +1 -1
  662. package/dist/production/src/resolvers/mutation/person/SignUpMutationResolver.cjs +2 -1
  663. package/dist/production/src/resolvers/mutation/person/SignUpMutationResolver.cjs.map +1 -1
  664. package/dist/production/src/resolvers/mutation/person/SignUpMutationResolver.js +2 -1
  665. package/dist/production/src/resolvers/mutation/person/SignUpMutationResolver.js.map +1 -1
  666. package/dist/production/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.cjs +2 -1
  667. package/dist/production/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.cjs.map +1 -1
  668. package/dist/production/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.js +2 -1
  669. package/dist/production/src/resolvers/mutation/person/TogglePasswordlessMutationResolver.js.map +1 -1
  670. package/dist/production/src/resolvers/mutation/project/CreateProjectMutationResolver.cjs +2 -1
  671. package/dist/production/src/resolvers/mutation/project/CreateProjectMutationResolver.cjs.map +1 -1
  672. package/dist/production/src/resolvers/mutation/project/CreateProjectMutationResolver.js +2 -1
  673. package/dist/production/src/resolvers/mutation/project/CreateProjectMutationResolver.js.map +1 -1
  674. package/dist/production/src/resolvers/mutation/project/SetProjectSecretMutationResolver.cjs +2 -1
  675. package/dist/production/src/resolvers/mutation/project/SetProjectSecretMutationResolver.cjs.map +1 -1
  676. package/dist/production/src/resolvers/mutation/project/SetProjectSecretMutationResolver.js +2 -1
  677. package/dist/production/src/resolvers/mutation/project/SetProjectSecretMutationResolver.js.map +1 -1
  678. package/dist/production/src/resolvers/mutation/project/UpdateProjectMutationResolver.cjs +2 -1
  679. package/dist/production/src/resolvers/mutation/project/UpdateProjectMutationResolver.cjs.map +1 -1
  680. package/dist/production/src/resolvers/mutation/project/UpdateProjectMutationResolver.js +2 -1
  681. package/dist/production/src/resolvers/mutation/project/UpdateProjectMutationResolver.js.map +1 -1
  682. package/dist/production/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.cjs +2 -1
  683. package/dist/production/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.cjs.map +1 -1
  684. package/dist/production/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.js +2 -1
  685. package/dist/production/src/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.js.map +1 -1
  686. package/dist/production/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.cjs +2 -1
  687. package/dist/production/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.cjs.map +1 -1
  688. package/dist/production/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.js +2 -1
  689. package/dist/production/src/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.js.map +1 -1
  690. package/dist/production/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.cjs +2 -1
  691. package/dist/production/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.cjs.map +1 -1
  692. package/dist/production/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.js +2 -1
  693. package/dist/production/src/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.js.map +1 -1
  694. package/dist/production/src/resolvers/query/ApiKeyQueryResolver.cjs +2 -1
  695. package/dist/production/src/resolvers/query/ApiKeyQueryResolver.cjs.map +1 -1
  696. package/dist/production/src/resolvers/query/ApiKeyQueryResolver.js +2 -1
  697. package/dist/production/src/resolvers/query/ApiKeyQueryResolver.js.map +1 -1
  698. package/dist/production/src/resolvers/query/AuthLogQueryResolver.cjs +2 -1
  699. package/dist/production/src/resolvers/query/AuthLogQueryResolver.cjs.map +1 -1
  700. package/dist/production/src/resolvers/query/AuthLogQueryResolver.js +2 -1
  701. package/dist/production/src/resolvers/query/AuthLogQueryResolver.js.map +1 -1
  702. package/dist/production/src/resolvers/query/AuthPolicyQueryResolver.cjs +2 -1
  703. package/dist/production/src/resolvers/query/AuthPolicyQueryResolver.cjs.map +1 -1
  704. package/dist/production/src/resolvers/query/AuthPolicyQueryResolver.js +2 -1
  705. package/dist/production/src/resolvers/query/AuthPolicyQueryResolver.js.map +1 -1
  706. package/dist/production/src/resolvers/query/ConfigurationQueryResolver.cjs +2 -1
  707. package/dist/production/src/resolvers/query/ConfigurationQueryResolver.cjs.map +1 -1
  708. package/dist/production/src/resolvers/query/ConfigurationQueryResolver.js +2 -1
  709. package/dist/production/src/resolvers/query/ConfigurationQueryResolver.js.map +1 -1
  710. package/dist/production/src/resolvers/query/IDPQueryResolver.cjs +2 -1
  711. package/dist/production/src/resolvers/query/IDPQueryResolver.cjs.map +1 -1
  712. package/dist/production/src/resolvers/query/IDPQueryResolver.js +2 -1
  713. package/dist/production/src/resolvers/query/IDPQueryResolver.js.map +1 -1
  714. package/dist/production/src/resolvers/query/MailTemplateQueryResolver.cjs +2 -1
  715. package/dist/production/src/resolvers/query/MailTemplateQueryResolver.cjs.map +1 -1
  716. package/dist/production/src/resolvers/query/MailTemplateQueryResolver.js +2 -1
  717. package/dist/production/src/resolvers/query/MailTemplateQueryResolver.js.map +1 -1
  718. package/dist/production/src/resolvers/query/PersonQueryResolver.cjs +2 -1
  719. package/dist/production/src/resolvers/query/PersonQueryResolver.cjs.map +1 -1
  720. package/dist/production/src/resolvers/query/PersonQueryResolver.js +2 -1
  721. package/dist/production/src/resolvers/query/PersonQueryResolver.js.map +1 -1
  722. package/dist/production/src/resolvers/query/ProjectMembersQueryResolver.cjs +2 -1
  723. package/dist/production/src/resolvers/query/ProjectMembersQueryResolver.cjs.map +1 -1
  724. package/dist/production/src/resolvers/query/ProjectMembersQueryResolver.js +2 -1
  725. package/dist/production/src/resolvers/query/ProjectMembersQueryResolver.js.map +1 -1
  726. package/dist/production/src/resolvers/query/ProjectQueryResolver.cjs +5 -3
  727. package/dist/production/src/resolvers/query/ProjectQueryResolver.cjs.map +1 -1
  728. package/dist/production/src/resolvers/query/ProjectQueryResolver.js +5 -3
  729. package/dist/production/src/resolvers/query/ProjectQueryResolver.js.map +1 -1
  730. package/dist/production/src/resolvers/responseHelpers/ApiKeyResponseFactory.cjs +2 -1
  731. package/dist/production/src/resolvers/responseHelpers/ApiKeyResponseFactory.cjs.map +1 -1
  732. package/dist/production/src/resolvers/responseHelpers/ApiKeyResponseFactory.js +2 -1
  733. package/dist/production/src/resolvers/responseHelpers/ApiKeyResponseFactory.js.map +1 -1
  734. package/dist/production/src/resolvers/responseHelpers/PersonResponseFactory.cjs +5 -0
  735. package/dist/production/src/resolvers/responseHelpers/PersonResponseFactory.cjs.map +1 -1
  736. package/dist/production/src/resolvers/responseHelpers/PersonResponseFactory.js +5 -0
  737. package/dist/production/src/resolvers/responseHelpers/PersonResponseFactory.js.map +1 -1
  738. package/dist/production/src/resolvers/responseHelpers/ProjectResponseFactory.cjs +23 -0
  739. package/dist/production/src/resolvers/responseHelpers/ProjectResponseFactory.cjs.map +1 -0
  740. package/dist/production/src/resolvers/responseHelpers/ProjectResponseFactory.js +23 -0
  741. package/dist/production/src/resolvers/responseHelpers/ProjectResponseFactory.js.map +1 -0
  742. package/dist/production/src/resolvers/responseHelpers/SignInResponseFactory.cjs +2 -1
  743. package/dist/production/src/resolvers/responseHelpers/SignInResponseFactory.cjs.map +1 -1
  744. package/dist/production/src/resolvers/responseHelpers/SignInResponseFactory.js +2 -1
  745. package/dist/production/src/resolvers/responseHelpers/SignInResponseFactory.js.map +1 -1
  746. package/dist/production/src/resolvers/types/IdentityTypeResolver.cjs +37 -4
  747. package/dist/production/src/resolvers/types/IdentityTypeResolver.cjs.map +1 -1
  748. package/dist/production/src/resolvers/types/IdentityTypeResolver.js +37 -4
  749. package/dist/production/src/resolvers/types/IdentityTypeResolver.js.map +1 -1
  750. package/dist/production/src/resolvers/types/PersonTypeResolver.cjs +30 -1
  751. package/dist/production/src/resolvers/types/PersonTypeResolver.cjs.map +1 -1
  752. package/dist/production/src/resolvers/types/PersonTypeResolver.js +30 -1
  753. package/dist/production/src/resolvers/types/PersonTypeResolver.js.map +1 -1
  754. package/dist/production/src/resolvers/types/ProjectTypeResolver.cjs +25 -1
  755. package/dist/production/src/resolvers/types/ProjectTypeResolver.cjs.map +1 -1
  756. package/dist/production/src/resolvers/types/ProjectTypeResolver.js +25 -1
  757. package/dist/production/src/resolvers/types/ProjectTypeResolver.js.map +1 -1
  758. package/dist/production/src/schema/tenant.graphql.cjs +144 -0
  759. package/dist/production/src/schema/tenant.graphql.cjs.map +1 -1
  760. package/dist/production/src/schema/tenant.graphql.js +144 -0
  761. package/dist/production/src/schema/tenant.graphql.js.map +1 -1
  762. package/dist/tests/cases/integration/mocked/configurationPanel.test.d.ts +2 -0
  763. package/dist/tests/cases/integration/mocked/configurationPanel.test.d.ts.map +1 -0
  764. package/dist/tests/cases/integration/mocked/disablePersonMutation.test.d.ts +2 -0
  765. package/dist/tests/cases/integration/mocked/disablePersonMutation.test.d.ts.map +1 -0
  766. package/dist/tests/cases/integration/mocked/enablePersonMutation.test.d.ts +2 -0
  767. package/dist/tests/cases/integration/mocked/enablePersonMutation.test.d.ts.map +1 -0
  768. package/dist/tests/cases/integration/mocked/gql/addProjectMember.d.ts.map +1 -1
  769. package/dist/tests/cases/integration/mocked/gql/changeMyPassword.d.ts.map +1 -1
  770. package/dist/tests/cases/integration/mocked/gql/changeMyProfile.d.ts.map +1 -1
  771. package/dist/tests/cases/integration/mocked/gql/changePassword.d.ts.map +1 -1
  772. package/dist/tests/cases/integration/mocked/gql/changeProfile.d.ts.map +1 -1
  773. package/dist/tests/cases/integration/mocked/gql/confirmOtp.d.ts.map +1 -1
  774. package/dist/tests/cases/integration/mocked/gql/createApiKey.d.ts.map +1 -1
  775. package/dist/tests/cases/integration/mocked/gql/createResetPasswordRequest.d.ts.map +1 -1
  776. package/dist/tests/cases/integration/mocked/gql/createSessionToken.d.ts.map +1 -1
  777. package/dist/tests/cases/integration/mocked/gql/emailOtp.d.ts.map +1 -1
  778. package/dist/tests/cases/integration/mocked/gql/emailVerification.d.ts.map +1 -1
  779. package/dist/tests/cases/integration/mocked/gql/invite.d.ts.map +1 -1
  780. package/dist/tests/cases/integration/mocked/gql/myIdentityProviders.d.ts.map +1 -1
  781. package/dist/tests/cases/integration/mocked/gql/passwordless.d.ts +4 -1
  782. package/dist/tests/cases/integration/mocked/gql/passwordless.d.ts.map +1 -1
  783. package/dist/tests/cases/integration/mocked/gql/prepareOtp.d.ts.map +1 -1
  784. package/dist/tests/cases/integration/mocked/gql/resetPassword.d.ts.map +1 -1
  785. package/dist/tests/cases/integration/mocked/gql/signIn.d.ts.map +1 -1
  786. package/dist/tests/cases/integration/mocked/gql/signInIdp.d.ts.map +1 -1
  787. package/dist/tests/cases/integration/mocked/gql/signUp.d.ts.map +1 -1
  788. package/dist/tests/cases/integration/mocked/gql/unmanagedInvite.d.ts.map +1 -1
  789. package/dist/tests/cases/integration/mocked/gql/updateProjectMember.d.ts.map +1 -1
  790. package/dist/tests/cases/integration/mocked/idpClaimMappingConfig.test.d.ts +2 -0
  791. package/dist/tests/cases/integration/mocked/idpClaimMappingConfig.test.d.ts.map +1 -0
  792. package/dist/tests/cases/integration/mocked/localAuthenticationDisabled.test.d.ts +2 -0
  793. package/dist/tests/cases/integration/mocked/localAuthenticationDisabled.test.d.ts.map +1 -0
  794. package/dist/tests/cases/integration/mocked/permissionsQuery.test.d.ts +2 -0
  795. package/dist/tests/cases/integration/mocked/permissionsQuery.test.d.ts.map +1 -0
  796. package/dist/tests/cases/integration/mocked/signInIdpClaimMapping.test.d.ts +2 -0
  797. package/dist/tests/cases/integration/mocked/signInIdpClaimMapping.test.d.ts.map +1 -0
  798. package/dist/tests/cases/integration/mocked/signInIdpClaimMappingFixes.test.d.ts +2 -0
  799. package/dist/tests/cases/integration/mocked/signInIdpClaimMappingFixes.test.d.ts.map +1 -0
  800. package/dist/tests/cases/integration/mocked/signInIdpClaimMappingSafety.test.d.ts +2 -0
  801. package/dist/tests/cases/integration/mocked/signInIdpClaimMappingSafety.test.d.ts.map +1 -0
  802. package/dist/tests/cases/integration/mocked/sql/authLogSql.d.ts.map +1 -1
  803. package/dist/tests/cases/integration/mocked/sql/authPolicySql.d.ts.map +1 -1
  804. package/dist/tests/cases/integration/mocked/sql/consumeBackupCodeSql.d.ts.map +1 -1
  805. package/dist/tests/cases/integration/mocked/sql/createApiKeySql.d.ts.map +1 -1
  806. package/dist/tests/cases/integration/mocked/sql/createIdentitySql.d.ts.map +1 -1
  807. package/dist/tests/cases/integration/mocked/sql/createMembershipSql.d.ts.map +1 -1
  808. package/dist/tests/cases/integration/mocked/sql/createMembershipVariableSql.d.ts.map +1 -1
  809. package/dist/tests/cases/integration/mocked/sql/createPersonSql.d.ts.map +1 -1
  810. package/dist/tests/cases/integration/mocked/sql/createPersonTokenSql.d.ts.map +1 -1
  811. package/dist/tests/cases/integration/mocked/sql/createSessionKeySql.d.ts.map +1 -1
  812. package/dist/tests/cases/integration/mocked/sql/disableApiKeySql.d.ts.map +1 -1
  813. package/dist/tests/cases/integration/mocked/sql/disableOneOffKeySql.d.ts.map +1 -1
  814. package/dist/tests/cases/integration/mocked/sql/emailOtpSql.d.ts.map +1 -1
  815. package/dist/tests/cases/integration/mocked/sql/generateBackupCodesSql.d.ts.map +1 -1
  816. package/dist/tests/cases/integration/mocked/sql/getApiKeySql.d.ts.map +1 -1
  817. package/dist/tests/cases/integration/mocked/sql/getConfigSql.d.ts.map +1 -1
  818. package/dist/tests/cases/integration/mocked/sql/getIdentityByIdSql.d.ts.map +1 -1
  819. package/dist/tests/cases/integration/mocked/sql/getIdentityProjectsSql.d.ts.map +1 -1
  820. package/dist/tests/cases/integration/mocked/sql/getIdpBySlugSql.d.ts.map +1 -1
  821. package/dist/tests/cases/integration/mocked/sql/getIdpSessionByApiKeySql.d.ts.map +1 -1
  822. package/dist/tests/cases/integration/mocked/sql/getMailTemplateSql.d.ts.map +1 -1
  823. package/dist/tests/cases/integration/mocked/sql/getNextLoginAttemptSql.d.ts.map +1 -1
  824. package/dist/tests/cases/integration/mocked/sql/getNextMailAttemptSql.d.ts +1 -1
  825. package/dist/tests/cases/integration/mocked/sql/getNextMailAttemptSql.d.ts.map +1 -1
  826. package/dist/tests/cases/integration/mocked/sql/getPersonByEmailSql.d.ts.map +1 -1
  827. package/dist/tests/cases/integration/mocked/sql/getPersonByIdSql.d.ts.map +1 -1
  828. package/dist/tests/cases/integration/mocked/sql/getPersonByIdentity.d.ts.map +1 -1
  829. package/dist/tests/cases/integration/mocked/sql/getPersonByIdpSql.d.ts.map +1 -1
  830. package/dist/tests/cases/integration/mocked/sql/getPersonsByIdentitySql.d.ts.map +1 -1
  831. package/dist/tests/cases/integration/mocked/sql/getProjectBySlugSql.d.ts +1 -1
  832. package/dist/tests/cases/integration/mocked/sql/getProjectBySlugSql.d.ts.map +1 -1
  833. package/dist/tests/cases/integration/mocked/sql/getProjectMembershipSql.d.ts.map +1 -1
  834. package/dist/tests/cases/integration/mocked/sql/listApiKeysSql.d.ts.map +1 -1
  835. package/dist/tests/cases/integration/mocked/sql/listPersonsSql.d.ts +2 -0
  836. package/dist/tests/cases/integration/mocked/sql/listPersonsSql.d.ts.map +1 -1
  837. package/dist/tests/cases/integration/mocked/sql/listProjectSecretsSql.d.ts.map +1 -1
  838. package/dist/tests/cases/integration/mocked/sql/listSessionsSql.d.ts.map +1 -1
  839. package/dist/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.d.ts +10 -0
  840. package/dist/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.d.ts.map +1 -0
  841. package/dist/tests/cases/integration/mocked/sql/loginHistorySql.d.ts.map +1 -1
  842. package/dist/tests/cases/integration/mocked/sql/patchVariablesSql.d.ts.map +1 -1
  843. package/dist/tests/cases/integration/mocked/sql/personIdentityProvidersSql.d.ts +1 -0
  844. package/dist/tests/cases/integration/mocked/sql/personIdentityProvidersSql.d.ts.map +1 -1
  845. package/dist/tests/cases/integration/mocked/sql/removeMembershipSql.d.ts +8 -0
  846. package/dist/tests/cases/integration/mocked/sql/removeMembershipSql.d.ts.map +1 -0
  847. package/dist/tests/cases/integration/mocked/sql/selectMembershipsSql.d.ts.map +1 -1
  848. package/dist/tests/cases/integration/mocked/sql/setEmailOtpEnabledSql.d.ts.map +1 -1
  849. package/dist/tests/cases/integration/mocked/sql/sqlTransaction.d.ts.map +1 -1
  850. package/dist/tests/cases/integration/mocked/sql/updatePesonNameSql.d.ts.map +1 -1
  851. package/dist/tests/cases/integration/mocked/sql/updatePesonPasswordSql.d.ts.map +1 -1
  852. package/dist/tests/cases/unit/databaseContextRetry.test.d.ts +2 -0
  853. package/dist/tests/cases/unit/databaseContextRetry.test.d.ts.map +1 -0
  854. package/dist/tests/cases/unit/idp/claimMappingEvaluator.test.d.ts +2 -0
  855. package/dist/tests/cases/unit/idp/claimMappingEvaluator.test.d.ts.map +1 -0
  856. package/dist/tests/cases/unit/idp/claimMappingValidation.test.d.ts +2 -0
  857. package/dist/tests/cases/unit/idp/claimMappingValidation.test.d.ts.map +1 -0
  858. package/dist/tests/cases/unit/idp/idpManagerConfigMerge.test.d.ts +2 -0
  859. package/dist/tests/cases/unit/idp/idpManagerConfigMerge.test.d.ts.map +1 -0
  860. package/dist/tests/cases/unit/idp/idpManagerUnknownConfigKeys.test.d.ts +2 -0
  861. package/dist/tests/cases/unit/idp/idpManagerUnknownConfigKeys.test.d.ts.map +1 -0
  862. package/dist/tests/cases/unit/idp/oidcClaims.test.d.ts +2 -0
  863. package/dist/tests/cases/unit/idp/oidcClaims.test.d.ts.map +1 -0
  864. package/dist/tests/cases/unit/panelAccessResolver.test.d.ts +2 -0
  865. package/dist/tests/cases/unit/panelAccessResolver.test.d.ts.map +1 -0
  866. package/dist/tests/cases/unit/updateConfigurationCommand.test.d.ts +2 -0
  867. package/dist/tests/cases/unit/updateConfigurationCommand.test.d.ts.map +1 -0
  868. package/dist/tests/src/IdPMock.d.ts.map +1 -1
  869. package/dist/tests/src/dbUtils.d.ts.map +1 -1
  870. package/dist/tests/src/tags.d.ts.map +1 -1
  871. package/dist/tests/src/testGraphql.d.ts.map +1 -1
  872. package/dist/tests/src/testTenant.d.ts +7 -1
  873. package/dist/tests/src/testTenant.d.ts.map +1 -1
  874. package/dist/tests/src/testUuid.d.ts.map +1 -1
  875. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  876. package/dist/types/TenantContainer.d.ts +21 -13
  877. package/dist/types/TenantContainer.d.ts.map +1 -1
  878. package/dist/types/bridges/system/IdentityFetcher.d.ts +2 -2
  879. package/dist/types/bridges/system/IdentityFetcher.d.ts.map +1 -1
  880. package/dist/types/exceptions.d.ts.map +1 -1
  881. package/dist/types/migrations/2026-06-18-120000-idp-claim-mapping.d.ts +3 -0
  882. package/dist/types/migrations/2026-06-18-120000-idp-claim-mapping.d.ts.map +1 -0
  883. package/dist/types/migrations/2026-08-03-120000-person-enable-auth-log.d.ts +3 -0
  884. package/dist/types/migrations/2026-08-03-120000-person-enable-auth-log.d.ts.map +1 -0
  885. package/dist/types/migrations/2026-08-04-120000-config-panel.d.ts +3 -0
  886. package/dist/types/migrations/2026-08-04-120000-config-panel.d.ts.map +1 -0
  887. package/dist/types/migrations/2026-09-04-120000-idp-disable-local-authentication.d.ts +3 -0
  888. package/dist/types/migrations/2026-09-04-120000-idp-disable-local-authentication.d.ts.map +1 -0
  889. package/dist/types/migrations/runner.d.ts.map +1 -1
  890. package/dist/types/migrations/snapshot-factory.d.ts.map +1 -1
  891. package/dist/types/migrations/snapshot.d.ts.map +1 -1
  892. package/dist/types/migrations/tenantCredentials.d.ts.map +1 -1
  893. package/dist/types/model/authorization/AclSchemaAccessNodeFactory.d.ts.map +1 -1
  894. package/dist/types/model/authorization/DirectPermissionsAccessNode.d.ts.map +1 -1
  895. package/dist/types/model/authorization/Identity.d.ts.map +1 -1
  896. package/dist/types/model/authorization/IdentityFactory.d.ts.map +1 -1
  897. package/dist/types/model/authorization/IdentityScope.d.ts.map +1 -1
  898. package/dist/types/model/authorization/MembershipMatcher.d.ts.map +1 -1
  899. package/dist/types/model/authorization/PermissionActions.d.ts.map +1 -1
  900. package/dist/types/model/authorization/PermissionContext.d.ts.map +1 -1
  901. package/dist/types/model/authorization/PermissionContextFactory.d.ts.map +1 -1
  902. package/dist/types/model/authorization/PermissionsFactory.d.ts.map +1 -1
  903. package/dist/types/model/authorization/ProjectScope.d.ts.map +1 -1
  904. package/dist/types/model/authorization/ProjectScopeFactory.d.ts.map +1 -1
  905. package/dist/types/model/commands/CommandBus.d.ts.map +1 -1
  906. package/dist/types/model/commands/apiKey/ApiKeyHelper.d.ts.map +1 -1
  907. package/dist/types/model/commands/apiKey/CreateApiKeyCommand.d.ts.map +1 -1
  908. package/dist/types/model/commands/apiKey/DisableApiKeyCommand.d.ts.map +1 -1
  909. package/dist/types/model/commands/apiKey/DisableIdentityApiKeysCommand.d.ts.map +1 -1
  910. package/dist/types/model/commands/apiKey/DisableOneOffApiKeyCommand.d.ts.map +1 -1
  911. package/dist/types/model/commands/apiKey/ProlongApiKeyCommand.d.ts +1 -1
  912. package/dist/types/model/commands/apiKey/ProlongApiKeyCommand.d.ts.map +1 -1
  913. package/dist/types/model/commands/authLog/CreateAuthLogEntryCommand.d.ts.map +1 -1
  914. package/dist/types/model/commands/authPolicy/CreateAuthPolicyCommand.d.ts.map +1 -1
  915. package/dist/types/model/commands/authPolicy/DeleteAuthPolicyCommand.d.ts.map +1 -1
  916. package/dist/types/model/commands/authPolicy/UpdateAuthPolicyCommand.d.ts.map +1 -1
  917. package/dist/types/model/commands/backupCode/ConsumeBackupCodeCommand.d.ts.map +1 -1
  918. package/dist/types/model/commands/backupCode/CreateBackupCodeCommand.d.ts.map +1 -1
  919. package/dist/types/model/commands/backupCode/DeleteBackupCodesCommand.d.ts.map +1 -1
  920. package/dist/types/model/commands/configuration/UpdateConfigurationCommand.d.ts.map +1 -1
  921. package/dist/types/model/commands/identity/CreateIdentityCommand.d.ts.map +1 -1
  922. package/dist/types/model/commands/identity/PatchIdentityGlobalRoles.d.ts.map +1 -1
  923. package/dist/types/model/commands/idp/ClaimIdpRevalidationCommand.d.ts.map +1 -1
  924. package/dist/types/model/commands/idp/CreateIdpCommand.d.ts.map +1 -1
  925. package/dist/types/model/commands/idp/CreateIdpSessionCommand.d.ts.map +1 -1
  926. package/dist/types/model/commands/idp/DisableIdpCommand.d.ts.map +1 -1
  927. package/dist/types/model/commands/idp/DisconnectPersonIdentityProviderCommand.d.ts.map +1 -1
  928. package/dist/types/model/commands/idp/EnableIdpCommand.d.ts.map +1 -1
  929. package/dist/types/model/commands/idp/UpdateIdpCommand.d.ts.map +1 -1
  930. package/dist/types/model/commands/idp/UpdateIdpSessionCommand.d.ts.map +1 -1
  931. package/dist/types/model/commands/mailTemplate/AddMailTemplateCommand.d.ts.map +1 -1
  932. package/dist/types/model/commands/mailTemplate/RemoveMailTemplateCommand.d.ts.map +1 -1
  933. package/dist/types/model/commands/membership/AddProjectMemberCommand.d.ts.map +1 -1
  934. package/dist/types/model/commands/membership/CreateOrUpdateProjectMembershipCommand.d.ts.map +1 -1
  935. package/dist/types/model/commands/membership/RemoveProjectMemberCommand.d.ts.map +1 -1
  936. package/dist/types/model/commands/membership/{RemoveProjectMembershipsCommand.d.ts → RemoveProjectMembershipCommand.d.ts} +1 -1
  937. package/dist/types/model/commands/membership/RemoveProjectMembershipCommand.d.ts.map +1 -0
  938. package/dist/types/model/commands/membership/UpdateProjectMemberCommand.d.ts.map +1 -1
  939. package/dist/types/model/commands/membership/index.d.ts +2 -1
  940. package/dist/types/model/commands/membership/index.d.ts.map +1 -1
  941. package/dist/types/model/commands/membership/variables/PatchProjectMembershipVariableValuesCommand.d.ts.map +1 -1
  942. package/dist/types/model/commands/membership/variables/PatchProjectMembershipVariablesCommand.d.ts.map +1 -1
  943. package/dist/types/model/commands/membership/variables/SetProjectMembershipVariableValuesCommand.d.ts.map +1 -1
  944. package/dist/types/model/commands/membership/variables/index.d.ts +1 -0
  945. package/dist/types/model/commands/membership/variables/index.d.ts.map +1 -1
  946. package/dist/types/model/commands/otp/ConfirmOtpCommand.d.ts.map +1 -1
  947. package/dist/types/model/commands/otp/DisableOtpCommand.d.ts.map +1 -1
  948. package/dist/types/model/commands/otp/PrepareOtpCommand.d.ts.map +1 -1
  949. package/dist/types/model/commands/otp/ReEncryptOtpSecretCommand.d.ts.map +1 -1
  950. package/dist/types/model/commands/otp/SetEmailOtpEnabledCommand.d.ts.map +1 -1
  951. package/dist/types/model/commands/person/ChangePasswordCommand.d.ts.map +1 -1
  952. package/dist/types/model/commands/person/ChangeProfileCommand.d.ts.map +1 -1
  953. package/dist/types/model/commands/person/CreatePersonCommand.d.ts.map +1 -1
  954. package/dist/types/model/commands/person/CreatePersonIdentityProviderIdentifierCommand.d.ts.map +1 -1
  955. package/dist/types/model/commands/person/DisablePersonCommand.d.ts.map +1 -1
  956. package/dist/types/model/commands/person/EnablePersonCommand.d.ts +7 -0
  957. package/dist/types/model/commands/person/EnablePersonCommand.d.ts.map +1 -0
  958. package/dist/types/model/commands/person/MarkEmailVerifiedCommand.d.ts.map +1 -1
  959. package/dist/types/model/commands/person/ResetPersonMfaCommand.d.ts.map +1 -1
  960. package/dist/types/model/commands/person/SetMfaGraceUntilCommand.d.ts.map +1 -1
  961. package/dist/types/model/commands/person/TogglePersonPasswordlessCommand.d.ts.map +1 -1
  962. package/dist/types/model/commands/personToken/ActivateOtpCommand.d.ts.map +1 -1
  963. package/dist/types/model/commands/personToken/ClaimOtpAttemptCommand.d.ts.map +1 -1
  964. package/dist/types/model/commands/personToken/CreatePersonTokenCommand.d.ts.map +1 -1
  965. package/dist/types/model/commands/personToken/IncreaseOtpAttemptCommand.d.ts.map +1 -1
  966. package/dist/types/model/commands/personToken/InvalidateEmailOtpTokensCommand.d.ts.map +1 -1
  967. package/dist/types/model/commands/personToken/InvalidateTokenCommand.d.ts.map +1 -1
  968. package/dist/types/model/commands/personToken/ResetPasswordCommand.d.ts.map +1 -1
  969. package/dist/types/model/commands/personToken/SaveEmailOtpTokenCommand.d.ts.map +1 -1
  970. package/dist/types/model/commands/personToken/SavePersonTokenCommand.d.ts.map +1 -1
  971. package/dist/types/model/commands/project/CreateProjectCommand.d.ts.map +1 -1
  972. package/dist/types/model/commands/project/SetProjectSecretCommand.d.ts.map +1 -1
  973. package/dist/types/model/commands/project/UpdateProjectCommand.d.ts.map +1 -1
  974. package/dist/types/model/commands/rateLimit/RecordRateLimitEventCommand.d.ts.map +1 -1
  975. package/dist/types/model/dtos/Password.d.ts.map +1 -1
  976. package/dist/types/model/mailing/UserMailer.d.ts.map +1 -1
  977. package/dist/types/model/mailing/templates/BackupCodesExhausted.mustache.d.ts.map +1 -1
  978. package/dist/types/model/mailing/templates/EmailChangeNotify.mustache.d.ts.map +1 -1
  979. package/dist/types/model/mailing/templates/EmailChangeVerify.mustache.d.ts.map +1 -1
  980. package/dist/types/model/mailing/templates/EmailOtp.mustache.d.ts.map +1 -1
  981. package/dist/types/model/mailing/templates/EmailVerification.mustache.d.ts.map +1 -1
  982. package/dist/types/model/mailing/templates/ExistingUserInvited.mustache.d.ts.map +1 -1
  983. package/dist/types/model/mailing/templates/ForcedSignOut.mustache.d.ts.map +1 -1
  984. package/dist/types/model/mailing/templates/Layout.mustache.d.ts.map +1 -1
  985. package/dist/types/model/mailing/templates/NewUserInvited.mustache.d.ts.map +1 -1
  986. package/dist/types/model/mailing/templates/PasswordReset.mustache.d.ts.map +1 -1
  987. package/dist/types/model/mailing/templates/PasswordlessSignIn.mustache.d.ts.map +1 -1
  988. package/dist/types/model/mailing/templates/UnusualLogin.mustache.d.ts.map +1 -1
  989. package/dist/types/model/mailing/type.d.ts.map +1 -1
  990. package/dist/types/model/queries/apiKey/ApiKeyListQuery.d.ts.map +1 -1
  991. package/dist/types/model/queries/apiKey/ApiKeyQuery.d.ts.map +1 -1
  992. package/dist/types/model/queries/apiKey/ApiKeySessionsQuery.d.ts.map +1 -1
  993. package/dist/types/model/queries/authLog/AuthLogQuery.d.ts.map +1 -1
  994. package/dist/types/model/queries/authLog/NextLoginAttemptQuery.d.ts.map +1 -1
  995. package/dist/types/model/queries/authLog/NextMailAttemptQuery.d.ts.map +1 -1
  996. package/dist/types/model/queries/authPolicy/AuthPoliciesQuery.d.ts.map +1 -1
  997. package/dist/types/model/queries/configuration/ConfigurationQuery.d.ts.map +1 -1
  998. package/dist/types/model/queries/identity/IdentityQuery.d.ts.map +1 -1
  999. package/dist/types/model/queries/idp/IdentityProviderBySlugQuery.d.ts.map +1 -1
  1000. package/dist/types/model/queries/idp/IdentityProvidersQuery.d.ts.map +1 -1
  1001. package/dist/types/model/queries/idp/IdpSessionByApiKeyQuery.d.ts.map +1 -1
  1002. package/dist/types/model/queries/idp/IdpSessionsBySidQuery.d.ts.map +1 -1
  1003. package/dist/types/model/queries/idp/IdpSessionsBySubQuery.d.ts.map +1 -1
  1004. package/dist/types/model/queries/idp/LocalAuthDisablingIdpsQuery.d.ts +18 -0
  1005. package/dist/types/model/queries/idp/LocalAuthDisablingIdpsQuery.d.ts.map +1 -0
  1006. package/dist/types/model/queries/idp/PersonIdentityProvidersQuery.d.ts +1 -0
  1007. package/dist/types/model/queries/idp/PersonIdentityProvidersQuery.d.ts.map +1 -1
  1008. package/dist/types/model/queries/idp/helpers.d.ts +1 -1
  1009. package/dist/types/model/queries/idp/helpers.d.ts.map +1 -1
  1010. package/dist/types/model/queries/idp/index.d.ts +1 -0
  1011. package/dist/types/model/queries/idp/index.d.ts.map +1 -1
  1012. package/dist/types/model/queries/idp/types.d.ts +1 -0
  1013. package/dist/types/model/queries/idp/types.d.ts.map +1 -1
  1014. package/dist/types/model/queries/mailTemplate/MailTemplateListQuery.d.ts.map +1 -1
  1015. package/dist/types/model/queries/mailTemplate/MailTemplateQuery.d.ts.map +1 -1
  1016. package/dist/types/model/queries/membership/AllProjectRolesByIdentityQuery.d.ts.map +1 -1
  1017. package/dist/types/model/queries/membership/ProjectMembersQuery.d.ts.map +1 -1
  1018. package/dist/types/model/queries/membership/ProjectMembershipByIdentityQuery.d.ts.map +1 -1
  1019. package/dist/types/model/queries/membership/ProjectRolesByIdentityQuery.d.ts.map +1 -1
  1020. package/dist/types/model/queries/membership/ProjectSlugSpecification.d.ts.map +1 -1
  1021. package/dist/types/model/queries/person/PersonByIdPQuery.d.ts.map +1 -1
  1022. package/dist/types/model/queries/person/PersonByIdentityBatchQuery.d.ts.map +1 -1
  1023. package/dist/types/model/queries/person/PersonQuery.d.ts.map +1 -1
  1024. package/dist/types/model/queries/person/PersonQueryBuilderFactory.d.ts.map +1 -1
  1025. package/dist/types/model/queries/person/PersonsQuery.d.ts.map +1 -1
  1026. package/dist/types/model/queries/person/types.d.ts +1 -0
  1027. package/dist/types/model/queries/person/types.d.ts.map +1 -1
  1028. package/dist/types/model/queries/personToken/PersonTokenQuery.d.ts.map +1 -1
  1029. package/dist/types/model/queries/project/ProjectBySlugQuery.d.ts.map +1 -1
  1030. package/dist/types/model/queries/project/ProjectSecretKeysQuery.d.ts.map +1 -1
  1031. package/dist/types/model/queries/project/ProjectSecretsQuery.d.ts.map +1 -1
  1032. package/dist/types/model/queries/project/ProjectUpdateTimestampQuery.d.ts.map +1 -1
  1033. package/dist/types/model/queries/project/ProjectsByIdentityQuery.d.ts.map +1 -1
  1034. package/dist/types/model/queries/project/ProjectsQuery.d.ts.map +1 -1
  1035. package/dist/types/model/queries/rateLimit/RateLimitCountQuery.d.ts.map +1 -1
  1036. package/dist/types/model/service/AuthLogService.d.ts.map +1 -1
  1037. package/dist/types/model/service/AuthPolicyManager.d.ts.map +1 -1
  1038. package/dist/types/model/service/AuthPolicyResolver.d.ts.map +1 -1
  1039. package/dist/types/model/service/BackupCodeManager.d.ts.map +1 -1
  1040. package/dist/types/model/service/ConfigurationManager.d.ts.map +1 -1
  1041. package/dist/types/model/service/EmailChangeManager.d.ts.map +1 -1
  1042. package/dist/types/model/service/EmailOtpManager.d.ts.map +1 -1
  1043. package/dist/types/model/service/EmailValidator.d.ts.map +1 -1
  1044. package/dist/types/model/service/EmailVerificationManager.d.ts.map +1 -1
  1045. package/dist/types/model/service/HibpChecker.d.ts.map +1 -1
  1046. package/dist/types/model/service/IDPSignInManager.d.ts +11 -1
  1047. package/dist/types/model/service/IDPSignInManager.d.ts.map +1 -1
  1048. package/dist/types/model/service/InviteManager.d.ts.map +1 -1
  1049. package/dist/types/model/service/LoginRiskAnalyzer.d.ts +1 -1
  1050. package/dist/types/model/service/LoginRiskAnalyzer.d.ts.map +1 -1
  1051. package/dist/types/model/service/MailTemplateManager.d.ts.map +1 -1
  1052. package/dist/types/model/service/MembershipValidator.d.ts.map +1 -1
  1053. package/dist/types/model/service/OtpAuthenticator.d.ts.map +1 -1
  1054. package/dist/types/model/service/OtpManager.d.ts.map +1 -1
  1055. package/dist/types/model/service/PanelAccessResolver.d.ts +19 -0
  1056. package/dist/types/model/service/PanelAccessResolver.d.ts.map +1 -0
  1057. package/dist/types/model/service/PasswordChangeManager.d.ts.map +1 -1
  1058. package/dist/types/model/service/PasswordResetManager.d.ts.map +1 -1
  1059. package/dist/types/model/service/PasswordStrengthValidator.d.ts.map +1 -1
  1060. package/dist/types/model/service/PasswordlessSignInManager.d.ts.map +1 -1
  1061. package/dist/types/model/service/PersonAccessManager.d.ts +5 -2
  1062. package/dist/types/model/service/PersonAccessManager.d.ts.map +1 -1
  1063. package/dist/types/model/service/PersonManager.d.ts.map +1 -1
  1064. package/dist/types/model/service/ProjectManager.d.ts.map +1 -1
  1065. package/dist/types/model/service/ProjectMemberManager.d.ts.map +1 -1
  1066. package/dist/types/model/service/RateLimiter.d.ts.map +1 -1
  1067. package/dist/types/model/service/RolesManager.d.ts.map +1 -1
  1068. package/dist/types/model/service/SecretsManager.d.ts.map +1 -1
  1069. package/dist/types/model/service/SignInManager.d.ts.map +1 -1
  1070. package/dist/types/model/service/SignOutManager.d.ts.map +1 -1
  1071. package/dist/types/model/service/SignUpManager.d.ts.map +1 -1
  1072. package/dist/types/model/service/apiKey/ApiKeyManager.d.ts.map +1 -1
  1073. package/dist/types/model/service/apiKey/ApiKeyService.d.ts.map +1 -1
  1074. package/dist/types/model/service/apiKey/UnpersistedApiKeyManager.d.ts +18 -0
  1075. package/dist/types/model/service/apiKey/UnpersistedApiKeyManager.d.ts.map +1 -1
  1076. package/dist/types/model/service/captcha/CaptchaValidator.d.ts.map +1 -1
  1077. package/dist/types/model/service/captcha/HCaptchaProvider.d.ts.map +1 -1
  1078. package/dist/types/model/service/captcha/RecaptchaV3Provider.d.ts.map +1 -1
  1079. package/dist/types/model/service/captcha/TurnstileProvider.d.ts.map +1 -1
  1080. package/dist/types/model/service/helpers/getPreferredProject.d.ts.map +1 -1
  1081. package/dist/types/model/service/helpers/isPasswordlessEnabled.d.ts.map +1 -1
  1082. package/dist/types/model/service/idp/BackchannelLogoutManager.d.ts.map +1 -1
  1083. package/dist/types/model/service/idp/ClaimMapping.d.ts +330 -0
  1084. package/dist/types/model/service/idp/ClaimMapping.d.ts.map +1 -0
  1085. package/dist/types/model/service/idp/ClaimMappingEvaluator.d.ts +30 -0
  1086. package/dist/types/model/service/idp/ClaimMappingEvaluator.d.ts.map +1 -0
  1087. package/dist/types/model/service/idp/ClaimMappingValidation.d.ts +55 -0
  1088. package/dist/types/model/service/idp/ClaimMappingValidation.d.ts.map +1 -0
  1089. package/dist/types/model/service/idp/IDPClaimSyncService.d.ts +113 -0
  1090. package/dist/types/model/service/idp/IDPClaimSyncService.d.ts.map +1 -0
  1091. package/dist/types/model/service/idp/IDPHandlerRegistry.d.ts.map +1 -1
  1092. package/dist/types/model/service/idp/IDPManager.d.ts +31 -1
  1093. package/dist/types/model/service/idp/IDPManager.d.ts.map +1 -1
  1094. package/dist/types/model/service/idp/IdentityProviderHandler.d.ts +7 -0
  1095. package/dist/types/model/service/idp/IdentityProviderHandler.d.ts.map +1 -1
  1096. package/dist/types/model/service/idp/IdpSessionRevalidator.d.ts +13 -1
  1097. package/dist/types/model/service/idp/IdpSessionRevalidator.d.ts.map +1 -1
  1098. package/dist/types/model/service/idp/PersonIdentityProviderManager.d.ts.map +1 -1
  1099. package/dist/types/model/service/idp/index.d.ts +4 -0
  1100. package/dist/types/model/service/idp/index.d.ts.map +1 -1
  1101. package/dist/types/model/service/idp/providers/AppleProvider.d.ts +2 -2
  1102. package/dist/types/model/service/idp/providers/AppleProvider.d.ts.map +1 -1
  1103. package/dist/types/model/service/idp/providers/FacebookProvider.d.ts +2 -2
  1104. package/dist/types/model/service/idp/providers/FacebookProvider.d.ts.map +1 -1
  1105. package/dist/types/model/service/idp/providers/OIDCHelpers.d.ts +13 -1
  1106. package/dist/types/model/service/idp/providers/OIDCHelpers.d.ts.map +1 -1
  1107. package/dist/types/model/service/idp/providers/OIDCProvider.d.ts +1 -1
  1108. package/dist/types/model/service/idp/providers/OIDCProvider.d.ts.map +1 -1
  1109. package/dist/types/model/service/idp/providers/OIDCTypes.d.ts +5 -5
  1110. package/dist/types/model/service/idp/providers/helpers.d.ts.map +1 -1
  1111. package/dist/types/model/service/index.d.ts +1 -0
  1112. package/dist/types/model/service/index.d.ts.map +1 -1
  1113. package/dist/types/model/service/membershipUtils.d.ts.map +1 -1
  1114. package/dist/types/model/type/AuthLog.d.ts +1 -1
  1115. package/dist/types/model/type/AuthLog.d.ts.map +1 -1
  1116. package/dist/types/model/type/Config.d.ts +2 -0
  1117. package/dist/types/model/type/Config.d.ts.map +1 -1
  1118. package/dist/types/model/type/IdentityProvider.d.ts +2 -0
  1119. package/dist/types/model/type/IdentityProvider.d.ts.map +1 -1
  1120. package/dist/types/model/type/RateLimit.d.ts +6 -6
  1121. package/dist/types/model/type/RateLimit.d.ts.map +1 -1
  1122. package/dist/types/model/utils/DatabaseContext.d.ts +14 -1
  1123. package/dist/types/model/utils/DatabaseContext.d.ts.map +1 -1
  1124. package/dist/types/model/utils/Response.d.ts.map +1 -1
  1125. package/dist/types/model/utils/email.d.ts.map +1 -1
  1126. package/dist/types/model/utils/hex.d.ts.map +1 -1
  1127. package/dist/types/model/utils/interval.d.ts.map +1 -1
  1128. package/dist/types/model/utils/time.d.ts.map +1 -1
  1129. package/dist/types/model/utils/token.d.ts.map +1 -1
  1130. package/dist/types/resolvers/ResolverFactory.d.ts +2 -0
  1131. package/dist/types/resolvers/ResolverFactory.d.ts.map +1 -1
  1132. package/dist/types/resolvers/TenantResolverContextFactory.d.ts.map +1 -1
  1133. package/dist/types/resolvers/errorUtils.d.ts.map +1 -1
  1134. package/dist/types/resolvers/membershipUtils.d.ts.map +1 -1
  1135. package/dist/types/resolvers/mutation/apiKey/CreateApiKeyMutationResolver.d.ts.map +1 -1
  1136. package/dist/types/resolvers/mutation/apiKey/DisableApiKeyMutationResolver.d.ts.map +1 -1
  1137. package/dist/types/resolvers/mutation/configuration/AuthPolicyMutationResolver.d.ts.map +1 -1
  1138. package/dist/types/resolvers/mutation/configuration/ConfigurationMutationResolver.d.ts.map +1 -1
  1139. package/dist/types/resolvers/mutation/identity/GlobalRolesMutationResolver.d.ts.map +1 -1
  1140. package/dist/types/resolvers/mutation/idp/AddIDPMutationResolver.d.ts.map +1 -1
  1141. package/dist/types/resolvers/mutation/idp/DisableIDPMutationResolver.d.ts.map +1 -1
  1142. package/dist/types/resolvers/mutation/idp/DisconnectMyIdentityProviderMutationResolver.d.ts.map +1 -1
  1143. package/dist/types/resolvers/mutation/idp/EnableIDPMutationResolver.d.ts.map +1 -1
  1144. package/dist/types/resolvers/mutation/idp/UpdateIDPMutationResolver.d.ts.map +1 -1
  1145. package/dist/types/resolvers/mutation/idp/audit.d.ts.map +1 -1
  1146. package/dist/types/resolvers/mutation/mailTemplates/MailTemplateMutationResolver.d.ts.map +1 -1
  1147. package/dist/types/resolvers/mutation/person/ChangePasswordMutationResolver.d.ts.map +1 -1
  1148. package/dist/types/resolvers/mutation/person/ChangeProfileMutationResolver.d.ts.map +1 -1
  1149. package/dist/types/resolvers/mutation/person/DisablePersonMutationResolver.d.ts +1 -1
  1150. package/dist/types/resolvers/mutation/person/DisablePersonMutationResolver.d.ts.map +1 -1
  1151. package/dist/types/resolvers/mutation/person/EmailOtpMutationResolver.d.ts.map +1 -1
  1152. package/dist/types/resolvers/mutation/person/EmailVerificationMutationResolver.d.ts.map +1 -1
  1153. package/dist/types/resolvers/mutation/person/EnablePersonMutationResolver.d.ts +11 -0
  1154. package/dist/types/resolvers/mutation/person/EnablePersonMutationResolver.d.ts.map +1 -0
  1155. package/dist/types/resolvers/mutation/person/ForceSignOutMutationResolver.d.ts.map +1 -1
  1156. package/dist/types/resolvers/mutation/person/IDPMutationResolver.d.ts.map +1 -1
  1157. package/dist/types/resolvers/mutation/person/InviteMutationResolver.d.ts.map +1 -1
  1158. package/dist/types/resolvers/mutation/person/OtpMutationResolver.d.ts.map +1 -1
  1159. package/dist/types/resolvers/mutation/person/PasswordlessMutationResolver.d.ts.map +1 -1
  1160. package/dist/types/resolvers/mutation/person/RegenerateBackupCodesMutationResolver.d.ts.map +1 -1
  1161. package/dist/types/resolvers/mutation/person/ResetPasswordMutationResolver.d.ts.map +1 -1
  1162. package/dist/types/resolvers/mutation/person/ResetPersonMfaMutationResolver.d.ts.map +1 -1
  1163. package/dist/types/resolvers/mutation/person/RevokeSessionMutationResolver.d.ts.map +1 -1
  1164. package/dist/types/resolvers/mutation/person/SignInMutationResolver.d.ts.map +1 -1
  1165. package/dist/types/resolvers/mutation/person/SignOutMutationResolver.d.ts.map +1 -1
  1166. package/dist/types/resolvers/mutation/person/SignUpMutationResolver.d.ts.map +1 -1
  1167. package/dist/types/resolvers/mutation/person/TogglePasswordlessMutationResolver.d.ts.map +1 -1
  1168. package/dist/types/resolvers/mutation/project/CreateProjectMutationResolver.d.ts.map +1 -1
  1169. package/dist/types/resolvers/mutation/project/SetProjectSecretMutationResolver.d.ts.map +1 -1
  1170. package/dist/types/resolvers/mutation/project/UpdateProjectMutationResolver.d.ts.map +1 -1
  1171. package/dist/types/resolvers/mutation/projectMember/AddProjectMemberMutationResolver.d.ts.map +1 -1
  1172. package/dist/types/resolvers/mutation/projectMember/RemoveProjectMemberMutationResolver.d.ts.map +1 -1
  1173. package/dist/types/resolvers/mutation/projectMember/UpdateProjectMemberMutationResolver.d.ts.map +1 -1
  1174. package/dist/types/resolvers/mutation/projectMember/audit.d.ts +1 -1
  1175. package/dist/types/resolvers/mutation/projectMember/audit.d.ts.map +1 -1
  1176. package/dist/types/resolvers/query/ApiKeyQueryResolver.d.ts.map +1 -1
  1177. package/dist/types/resolvers/query/AuthLogQueryResolver.d.ts.map +1 -1
  1178. package/dist/types/resolvers/query/AuthPolicyQueryResolver.d.ts.map +1 -1
  1179. package/dist/types/resolvers/query/ConfigurationQueryResolver.d.ts.map +1 -1
  1180. package/dist/types/resolvers/query/IDPQueryResolver.d.ts.map +1 -1
  1181. package/dist/types/resolvers/query/MailTemplateQueryResolver.d.ts.map +1 -1
  1182. package/dist/types/resolvers/query/MeQueryResolver.d.ts.map +1 -1
  1183. package/dist/types/resolvers/query/PersonQueryResolver.d.ts.map +1 -1
  1184. package/dist/types/resolvers/query/ProjectMembersQueryResolver.d.ts.map +1 -1
  1185. package/dist/types/resolvers/query/ProjectQueryResolver.d.ts.map +1 -1
  1186. package/dist/types/resolvers/responseHelpers/ApiKeyResponseFactory.d.ts.map +1 -1
  1187. package/dist/types/resolvers/responseHelpers/PersonResponseFactory.d.ts.map +1 -1
  1188. package/dist/types/resolvers/responseHelpers/ProjectResponseFactory.d.ts +10 -0
  1189. package/dist/types/resolvers/responseHelpers/ProjectResponseFactory.d.ts.map +1 -0
  1190. package/dist/types/resolvers/responseHelpers/SignInResponseFactory.d.ts.map +1 -1
  1191. package/dist/types/resolvers/types/IdentityTypeResolver.d.ts.map +1 -1
  1192. package/dist/types/resolvers/types/PersonTypeResolver.d.ts +15 -1
  1193. package/dist/types/resolvers/types/PersonTypeResolver.d.ts.map +1 -1
  1194. package/dist/types/resolvers/types/ProjectTypeResolver.d.ts +15 -6
  1195. package/dist/types/resolvers/types/ProjectTypeResolver.d.ts.map +1 -1
  1196. package/dist/types/schema/index.d.ts +194 -4
  1197. package/dist/types/schema/index.d.ts.map +1 -1
  1198. package/dist/types/schema/tenant.graphql.d.ts.map +1 -1
  1199. package/dist/types/schema/types.d.ts +2 -2
  1200. package/dist/types/schema/types.d.ts.map +1 -1
  1201. package/dist/types/tsconfig.tsbuildinfo +1 -1
  1202. package/dist/types/utils/Mailer.d.ts.map +1 -1
  1203. package/dist/types/utils/TemplateRenderer.d.ts.map +1 -1
  1204. package/dist/types/utils/array.d.ts.map +1 -1
  1205. package/dist/types/utils/batchQuery.d.ts.map +1 -1
  1206. package/package.json +12 -12
  1207. package/src/TenantContainer.ts +17 -3
  1208. package/src/migrations/2026-06-18-120000-idp-claim-mapping.ts +15 -0
  1209. package/src/migrations/2026-08-03-120000-person-enable-auth-log.ts +12 -0
  1210. package/src/migrations/2026-08-04-120000-config-panel.ts +13 -0
  1211. package/src/migrations/2026-09-04-120000-idp-disable-local-authentication.ts +18 -0
  1212. package/src/migrations/runner.ts +8 -0
  1213. package/src/migrations/snapshot.ts +8 -2
  1214. package/src/model/commands/configuration/UpdateConfigurationCommand.ts +5 -4
  1215. package/src/model/commands/idp/CreateIdpCommand.ts +7 -1
  1216. package/src/model/commands/idp/UpdateIdpCommand.ts +5 -1
  1217. package/src/model/commands/membership/UpdateProjectMemberCommand.ts +1 -1
  1218. package/src/model/commands/membership/index.ts +2 -1
  1219. package/src/model/commands/membership/variables/index.ts +1 -0
  1220. package/src/model/commands/person/EnablePersonCommand.ts +18 -0
  1221. package/src/model/queries/configuration/ConfigurationQuery.ts +4 -0
  1222. package/src/model/queries/idp/LocalAuthDisablingIdpsQuery.ts +41 -0
  1223. package/src/model/queries/idp/PersonIdentityProvidersQuery.ts +2 -0
  1224. package/src/model/queries/idp/helpers.ts +3 -2
  1225. package/src/model/queries/idp/index.ts +1 -0
  1226. package/src/model/queries/idp/types.ts +1 -0
  1227. package/src/model/queries/membership/ProjectMembersQuery.ts +1 -0
  1228. package/src/model/queries/person/PersonQueryBuilderFactory.ts +1 -0
  1229. package/src/model/queries/person/types.ts +1 -0
  1230. package/src/model/service/AuthLogService.ts +18 -2
  1231. package/src/model/service/IDPSignInManager.ts +49 -1
  1232. package/src/model/service/PanelAccessResolver.ts +32 -0
  1233. package/src/model/service/PasswordResetManager.ts +9 -1
  1234. package/src/model/service/PasswordlessSignInManager.ts +23 -1
  1235. package/src/model/service/PersonAccessManager.ts +19 -3
  1236. package/src/model/service/SignInManager.ts +9 -1
  1237. package/src/model/service/apiKey/ApiKeyManager.ts +5 -3
  1238. package/src/model/service/apiKey/UnpersistedApiKeyManager.ts +24 -0
  1239. package/src/model/service/idp/ClaimMapping.ts +283 -0
  1240. package/src/model/service/idp/ClaimMappingEvaluator.ts +226 -0
  1241. package/src/model/service/idp/ClaimMappingValidation.ts +158 -0
  1242. package/src/model/service/idp/IDPClaimSyncService.ts +401 -0
  1243. package/src/model/service/idp/IDPManager.ts +126 -21
  1244. package/src/model/service/idp/IdentityProviderHandler.ts +7 -0
  1245. package/src/model/service/idp/IdpSessionRevalidator.ts +90 -0
  1246. package/src/model/service/idp/PersonIdentityProviderManager.ts +8 -0
  1247. package/src/model/service/idp/index.ts +4 -0
  1248. package/src/model/service/idp/providers/OIDCHelpers.ts +161 -53
  1249. package/src/model/service/idp/providers/OIDCProvider.ts +4 -1
  1250. package/src/model/service/index.ts +1 -0
  1251. package/src/model/type/AuthLog.ts +3 -0
  1252. package/src/model/type/Config.ts +3 -0
  1253. package/src/model/type/IdentityProvider.ts +2 -0
  1254. package/src/model/utils/DatabaseContext.ts +27 -6
  1255. package/src/resolvers/ResolverFactory.ts +6 -0
  1256. package/src/resolvers/mutation/idp/AddIDPMutationResolver.ts +1 -0
  1257. package/src/resolvers/mutation/idp/UpdateIDPMutationResolver.ts +1 -0
  1258. package/src/resolvers/mutation/idp/audit.ts +1 -0
  1259. package/src/resolvers/mutation/person/DisablePersonMutationResolver.ts +3 -3
  1260. package/src/resolvers/mutation/person/EnablePersonMutationResolver.ts +50 -0
  1261. package/src/resolvers/mutation/person/IDPMutationResolver.ts +31 -0
  1262. package/src/resolvers/mutation/person/PasswordlessMutationResolver.ts +7 -0
  1263. package/src/resolvers/mutation/person/SignInMutationResolver.ts +2 -2
  1264. package/src/resolvers/query/ProjectQueryResolver.ts +3 -2
  1265. package/src/resolvers/responseHelpers/PersonResponseFactory.ts +5 -0
  1266. package/src/resolvers/responseHelpers/ProjectResponseFactory.ts +24 -0
  1267. package/src/resolvers/types/IdentityTypeResolver.ts +39 -3
  1268. package/src/resolvers/types/PersonTypeResolver.ts +35 -2
  1269. package/src/resolvers/types/ProjectTypeResolver.ts +33 -1
  1270. package/src/schema/index.ts +212 -0
  1271. package/src/schema/tenant.graphql.ts +144 -0
  1272. package/tests/cases/integration/mocked/configurationPanel.test.ts +105 -0
  1273. package/tests/cases/integration/mocked/disablePersonMutation.test.ts +89 -0
  1274. package/tests/cases/integration/mocked/enablePersonMutation.test.ts +130 -0
  1275. package/tests/cases/integration/mocked/gql/passwordless.ts +10 -0
  1276. package/tests/cases/integration/mocked/idpClaimMappingConfig.test.ts +332 -0
  1277. package/tests/cases/integration/mocked/listProjectMembersQuery.test.ts +8 -3
  1278. package/tests/cases/integration/mocked/localAuthenticationDisabled.test.ts +182 -0
  1279. package/tests/cases/integration/mocked/permissionsQuery.test.ts +153 -0
  1280. package/tests/cases/integration/mocked/personIdentityProviders.test.ts +67 -0
  1281. package/tests/cases/integration/mocked/personsQuery.test.ts +38 -5
  1282. package/tests/cases/integration/mocked/signInAnomalyDetection.test.ts +11 -0
  1283. package/tests/cases/integration/mocked/signInIdpClaimMapping.test.ts +1258 -0
  1284. package/tests/cases/integration/mocked/signInIdpClaimMappingFixes.test.ts +344 -0
  1285. package/tests/cases/integration/mocked/signInIdpClaimMappingSafety.test.ts +531 -0
  1286. package/tests/cases/integration/mocked/signInIdpMutation.test.ts +5 -0
  1287. package/tests/cases/integration/mocked/signInMfaEnforcement.test.ts +7 -0
  1288. package/tests/cases/integration/mocked/signInMutation.test.ts +14 -0
  1289. package/tests/cases/integration/mocked/signInPasswordlessMutation.test.ts +36 -0
  1290. package/tests/cases/integration/mocked/sql/getConfigSql.ts +3 -0
  1291. package/tests/cases/integration/mocked/sql/getIdpBySlugSql.ts +1 -1
  1292. package/tests/cases/integration/mocked/sql/getProjectBySlugSql.ts +1 -1
  1293. package/tests/cases/integration/mocked/sql/listPersonsSql.ts +5 -2
  1294. package/tests/cases/integration/mocked/sql/localAuthDisablingIdpsSql.ts +21 -0
  1295. package/tests/cases/integration/mocked/sql/personIdentityProvidersSql.ts +3 -1
  1296. package/tests/cases/integration/mocked/sql/removeMembershipSql.ts +14 -0
  1297. package/tests/cases/integration/mocked/trustForwardedClientInfo.test.ts +3 -0
  1298. package/tests/cases/unit/databaseContextRetry.test.ts +81 -0
  1299. package/tests/cases/unit/idp/claimMappingEvaluator.test.ts +554 -0
  1300. package/tests/cases/unit/idp/claimMappingValidation.test.ts +134 -0
  1301. package/tests/cases/unit/idp/idpManagerConfigMerge.test.ts +97 -0
  1302. package/tests/cases/unit/idp/idpManagerUnknownConfigKeys.test.ts +108 -0
  1303. package/tests/cases/unit/idp/idpSessionRevalidator.test.ts +226 -24
  1304. package/tests/cases/unit/idp/oidcClaims.test.ts +29 -0
  1305. package/tests/cases/unit/idp/oidcResponse.test.ts +54 -0
  1306. package/tests/cases/unit/idp/oidcRevalidate.test.ts +94 -2
  1307. package/tests/cases/unit/panelAccessResolver.test.ts +57 -0
  1308. package/tests/cases/unit/updateConfigurationCommand.test.ts +63 -0
  1309. package/tests/src/IdPMock.ts +4 -0
  1310. package/tests/src/testTenant.ts +32 -1
  1311. package/dist/development/src/model/commands/membership/RemoveProjectMembershipsCommand.cjs.map +0 -1
  1312. package/dist/development/src/model/commands/membership/RemoveProjectMembershipsCommand.js.map +0 -1
  1313. package/dist/production/src/model/commands/membership/RemoveProjectMembershipsCommand.cjs.map +0 -1
  1314. package/dist/production/src/model/commands/membership/RemoveProjectMembershipsCommand.js.map +0 -1
  1315. package/dist/types/model/commands/membership/RemoveProjectMembershipsCommand.d.ts.map +0 -1
  1316. /package/src/model/commands/membership/{RemoveProjectMembershipsCommand.ts → RemoveProjectMembershipCommand.ts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"SignInManager.js","sources":["../../../../../../packages/engine-tenant-api/src/model/service/SignInManager.ts"],"sourcesContent":["import { CreateSessionTokenErrorCode, MfaEnrollment, SignInErrorCode } from '../../schema/index.js'\nimport { ApiKeyManager, AuthPolicyResolver, BackupCodeManager, EmailOtpManager, LoginRiskAnalyzer, OtpManager } from '../service/index.js'\nimport { ConfigurationQuery, PersonQuery, PersonRow, PersonUniqueIdentifier } from '../queries/index.js'\nimport { Config } from '../type/Config.js'\nimport { Providers } from '../providers.js'\nimport { DatabaseContext } from '../utils/index.js'\nimport { Response, ResponseError, ResponseOk } from '../utils/Response.js'\nimport { ImplementationException } from '../../exceptions.js'\nimport { AuthLogService } from './AuthLogService.js'\nimport { UserMailer } from '../mailing/index.js'\nimport { ApiKeyRequestInfo, SetMfaGraceUntilCommand } from '../commands/index.js'\nimport { intervalToSeconds } from '../utils/interval.js'\n\nclass SignInManager {\n\tconstructor(\n\t\tprivate readonly apiKeyManager: ApiKeyManager,\n\t\tprivate readonly providers: Providers,\n\t\tprivate readonly otpManager: OtpManager,\n\t\tprivate readonly backupCodeManager: BackupCodeManager,\n\t\tprivate readonly emailOtpManager: EmailOtpManager,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly loginRiskAnalyzer: LoginRiskAnalyzer,\n\t\tprivate readonly userMailer: UserMailer,\n\t) {}\n\n\tasync signIn(\n\t\tdbContext: DatabaseContext,\n\t\temail: string,\n\t\tpassword: string,\n\t\texpiration?: number,\n\t\totpCode?: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t\tbackupCode?: string,\n\t\tconfig?: Config,\n\t\tgeoCountry?: string,\n\t): Promise<SignInResponse> {\n\t\tconst person = await dbContext.queryHandler.fetch(PersonQuery.byEmail(email))\n\n\t\tif (person === null) {\n\t\t\treturn new ResponseError('UNKNOWN_EMAIL', `Person with email ${email} not found`, {\n\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\tpersonInput: email,\n\t\t\t\t}),\n\t\t\t})\n\t\t}\n\n\t\tconst authLogData = {\n\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\tpersonInput: email,\n\t\t\t\tpersonId: person.id,\n\t\t\t}),\n\t\t}\n\t\tif (!person.password_hash) {\n\t\t\treturn new ResponseError('NO_PASSWORD_SET', `No password set`, authLogData)\n\t\t}\n\n\t\tif (person.disabled_at !== null) {\n\t\t\treturn new ResponseError('PERSON_DISABLED', `Person is disabled`, authLogData)\n\t\t}\n\n\t\tconst passwordValid = await this.providers.bcryptCompare(password, person.password_hash)\n\n\t\tif (!passwordValid) {\n\t\t\treturn new ResponseError('INVALID_PASSWORD', `Password does not match`, authLogData)\n\t\t}\n\n\t\t// Block sign-in for accounts that were created while email verification\n\t\t// was required but never proved ownership. Accounts predating the\n\t\t// requirement carry email_verification_required = false and pass through.\n\t\tif (person.email_verification_required && person.email_verified_at === null) {\n\t\t\treturn new ResponseError('EMAIL_NOT_VERIFIED', `E-mail address has not been verified`, authLogData)\n\t\t}\n\n\t\tlet usedBackupCode = false\n\t\t// Whether a second factor was actively proven in THIS request. Anomaly\n\t\t// step-up (A03) reuses this: a login that already cleared a second factor\n\t\t// is not asked to step up again.\n\t\tlet mfaSatisfiedThisRequest = false\n\t\tconst hasActiveTotp = Boolean(person.otp_secret && person.otp_activated_at)\n\t\tif (hasActiveTotp) {\n\t\t\t// Active TOTP takes precedence (A07 path, unchanged).\n\t\t\tif (otpCode) {\n\t\t\t\tif (!await this.otpManager.verifyOtp(dbContext, person, otpCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else if (backupCode) {\n\t\t\t\tif (!await this.backupCodeManager.verifyAndConsume(dbContext, person, backupCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tusedBackupCode = true\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else {\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', `2FA is enabled. OTP token is required`, authLogData)\n\t\t\t}\n\t\t} else if (person.email_otp_enabled) {\n\t\t\t// No active TOTP, but email OTP is enabled (A05).\n\t\t\tif (otpCode) {\n\t\t\t\tif (!await this.emailOtpManager.verifyAndConsume(dbContext, person, otpCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else if (backupCode) {\n\t\t\t\tif (!await this.backupCodeManager.verifyAndConsume(dbContext, person, backupCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tusedBackupCode = true\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else {\n\t\t\t\t// Dispatch a fresh code and ask the client to retry with otpToken. Reuses\n\t\t\t\t// the existing OTP_REQUIRED contract so clients that already handle it work.\n\t\t\t\t// The send is rate-limited per person; when throttled nothing is emailed\n\t\t\t\t// (emailOtpSent: false) but a previously emailed code stays valid.\n\t\t\t\t// Independent fetch (not the passed-in `config`): the email-OTP send is\n\t\t\t\t// rate-limited by the live config and this branch never reaches the\n\t\t\t\t// anomaly analyzer, so query shape stays as before A03.\n\t\t\t\tconst emailOtpConfig = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\t\t\t\tconst decision = await this.emailOtpManager.sendCode(dbContext, person, emailOtpConfig)\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', `2FA is enabled. OTP token is required`, {\n\t\t\t\t\temailOtpSent: decision.ok,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\t// No active factor at all (A06). Enforce MFA if a matching auth_policy\n\t\t\t// requires it. With no policy rows configured this resolves to\n\t\t\t// mfaRequired=false and sign-in proceeds exactly as before.\n\t\t\tconst enforcement = await this.enforceMfaEnrollment(dbContext, person, otpCode, authLogData)\n\t\t\tif (enforcement !== null) {\n\t\t\t\tif ('error' in enforcement) {\n\t\t\t\t\treturn enforcement\n\t\t\t\t}\n\t\t\t\t// Enrollment completed in this request (pending TOTP promoted to active),\n\t\t\t\t// so MFA is definitively satisfied — anomaly step-up cannot apply.\n\t\t\t\tconst riskHandled = await this.applyRiskPolicy(dbContext, person, {\n\t\t\t\t\tconfig,\n\t\t\t\t\tgeoCountry,\n\t\t\t\t\trequestInfo,\n\t\t\t\t\totpCode,\n\t\t\t\t\tmfaSatisfiedThisRequest: true,\n\t\t\t\t\tauthLogData,\n\t\t\t\t})\n\t\t\t\tif (riskHandled !== null && 'error' in riskHandled) {\n\t\t\t\t\treturn riskHandled\n\t\t\t\t}\n\t\t\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(\n\t\t\t\t\tdbContext,\n\t\t\t\t\tperson.identity_id,\n\t\t\t\t\texpiration,\n\t\t\t\t\trequestInfo,\n\t\t\t\t\ttrustForwardedInfo,\n\t\t\t\t)\n\t\t\t\treturn new ResponseOk({\n\t\t\t\t\tperson,\n\t\t\t\t\ttoken: sessionToken,\n\t\t\t\t\tbackupCodes: enforcement.backupCodes,\n\t\t\t\t\tunusualLoginDetected: riskHandled?.unusualLoginDetected || undefined,\n\t\t\t\t\trisk: riskHandled?.risk,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst riskHandled = await this.applyRiskPolicy(dbContext, person, {\n\t\t\tconfig,\n\t\t\tgeoCountry,\n\t\t\trequestInfo,\n\t\t\totpCode,\n\t\t\tmfaSatisfiedThisRequest,\n\t\t\tauthLogData,\n\t\t})\n\t\tif (riskHandled !== null && 'error' in riskHandled) {\n\t\t\treturn riskHandled\n\t\t}\n\n\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(dbContext, person.identity_id, expiration, requestInfo, trustForwardedInfo)\n\n\t\treturn new ResponseOk({\n\t\t\tperson,\n\t\t\ttoken: sessionToken,\n\t\t\tusedBackupCode,\n\t\t\tunusualLoginDetected: riskHandled?.unusualLoginDetected || undefined,\n\t\t\trisk: riskHandled?.risk,\n\t\t\t...authLogData,\n\t\t})\n\t}\n\n\t/**\n\t * A03 — evaluate the sign-in risk score and act per policy. Returns:\n\t * - `null` when anomaly detection is off or no `config` was supplied (the\n\t * default): no DB work, sign-in proceeds unchanged.\n\t * - `{ unusualLoginDetected: true }` when an informational UNUSUAL_LOGIN email\n\t * was sent (best-effort) but the session may still be issued.\n\t * - a `ResponseError('OTP_REQUIRED')` when step-up is required and no code was\n\t * supplied — reuses the email-OTP step-up channel (dispatches a code).\n\t * - a `ResponseError('INVALID_OTP_TOKEN')` when a step-up code was supplied but\n\t * did not verify. A valid code consumes the token and the session is issued.\n\t * - `{ unusualLoginDetected: false }` when the score was below every threshold.\n\t *\n\t * The audit entries (`unusual_login_detected`, `step_up_required`) are emitted\n\t * by the resolver from the response metadata, matching the project convention.\n\t */\n\tprivate async applyRiskPolicy(\n\t\tdbContext: DatabaseContext,\n\t\tperson: PersonRow,\n\t\targs: {\n\t\t\tconfig?: Config\n\t\t\tgeoCountry?: string\n\t\t\trequestInfo?: ApiKeyRequestInfo\n\t\t\totpCode?: string\n\t\t\tmfaSatisfiedThisRequest: boolean\n\t\t\tauthLogData: { [AuthLogService.Key]: AuthLogService.Bag }\n\t\t},\n\t): Promise<RiskPolicyOutcome> {\n\t\tconst { config, geoCountry, requestInfo, otpCode, mfaSatisfiedThisRequest, authLogData } = args\n\t\tconst policy = config?.login.anomalyDetection\n\t\tif (!config || !policy?.enabled) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst assessment = await this.loginRiskAnalyzer.analyze(dbContext, person.id, {\n\t\t\tgeoCountry: geoCountry ?? null,\n\t\t\tdeviceFingerprint: this.loginRiskAnalyzer.fingerprint(requestInfo?.userAgent),\n\t\t\tip: requestInfo?.ip ?? null,\n\t\t}, policy)\n\n\t\tif (assessment.action === 'allow') {\n\t\t\treturn { unusualLoginDetected: false }\n\t\t}\n\n\t\tconst riskMeta = { score: assessment.score, reasons: assessment.reasons }\n\n\t\tif (assessment.action === 'stepUp' && !mfaSatisfiedThisRequest) {\n\t\t\t// Reuse the existing email-OTP step-up channel. This is independent of\n\t\t\t// `person.email_otp_enabled`, which gates email OTP only as the *primary*\n\t\t\t// second factor (A05); the step-up challenge stands on its own, so a person\n\t\t\t// with no standing MFA can still complete it. Without consuming the code\n\t\t\t// here, such a person would never satisfy the step-up and would loop on\n\t\t\t// OTP_REQUIRED forever (the score is unchanged on every retry).\n\t\t\tif (otpCode) {\n\t\t\t\t// Retry leg: the client resent the emailed code as otpToken. Mirror the\n\t\t\t\t// A05 contract — an invalid code is rejected (not re-sent); the per-code\n\t\t\t\t// 3-attempt cap still applies, and a fresh code is issued only when the\n\t\t\t\t// client retries without a code.\n\t\t\t\tif (!await this.emailOtpManager.verifyAndConsume(dbContext, person, otpCode)) {\n\t\t\t\t\t// A failed *forced* second factor is itself an anomaly signal — carry the\n\t\t\t\t\t// step-up/risk metadata so the resolver still emits unusual_login_detected\n\t\t\t\t\t// + step_up_required with the score/reasons, rather than logging it as an\n\t\t\t\t\t// ordinary mistyped-OTP login indistinguishable from a normal 2FA typo.\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', {\n\t\t\t\t\t\tstepUpRequired: true,\n\t\t\t\t\t\tunusualLoginDetected: true,\n\t\t\t\t\t\trisk: riskMeta,\n\t\t\t\t\t\t...authLogData,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Step-up satisfied this request — fall through to notify + allow below.\n\t\t\t} else {\n\t\t\t\t// First leg: dispatch a code and ask the client to retry with otpToken.\n\t\t\t\t// Rate-limited per person; a previously emailed code stays valid when throttled.\n\t\t\t\tconst decision = await this.emailOtpManager.sendCode(dbContext, person, config)\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', 'Additional verification is required for this sign-in', {\n\t\t\t\t\temailOtpSent: decision.ok,\n\t\t\t\t\tstepUpRequired: true,\n\t\t\t\t\tunusualLoginDetected: true,\n\t\t\t\t\trisk: riskMeta,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// action === 'email', or step-up satisfied (already this request, or just now\n\t\t// via the emailed code): notify the person of the unusual sign-in. A failed\n\t\t// email must never block sign-in.\n\t\tawait this.sendUnusualLoginEmail(dbContext, person, geoCountry ?? null, requestInfo?.ip ?? null)\n\t\treturn { unusualLoginDetected: true, risk: riskMeta }\n\t}\n\n\tprivate async sendUnusualLoginEmail(dbContext: DatabaseContext, person: PersonRow, geoCountry: string | null, ip: string | null): Promise<void> {\n\t\tif (!person.email) {\n\t\t\t// No address to notify (e.g. an IDP-only person) — nothing to send.\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tawait this.userMailer.sendUnusualLoginEmail(dbContext, {\n\t\t\t\temail: person.email,\n\t\t\t\tgeoCountry,\n\t\t\t\tipAddress: ip,\n\t\t\t}, { projectId: null, variant: '' })\n\t\t} catch {\n\t\t\t// ignore — informational only\n\t\t}\n\t}\n\n\t/**\n\t * A06 enforcement for the no-active-factor case. Returns:\n\t * - `null` when MFA is not required, or grace currently allows sign-in\n\t * (caller proceeds to issue a session as normal). With zero auth_policy\n\t * rows this is always the outcome — no behavior change.\n\t * - a `ResponseError` (MFA_ENROLLMENT_REQUIRED with the pending secret to\n\t * enroll, or INVALID_OTP_TOKEN) when sign-in must be blocked.\n\t * - `{ ok: true, backupCodes }` when enrollment just completed (pending TOTP\n\t * promoted to active); caller issues the session.\n\t */\n\tprivate async enforceMfaEnrollment(\n\t\tdbContext: DatabaseContext,\n\t\tperson: PersonRow,\n\t\totpCode: string | undefined,\n\t\tauthLogData: { [AuthLogService.Key]: AuthLogService.Bag },\n\t): Promise<\n\t\t| null\n\t\t| ResponseError<SignInErrorCode, {\n\t\t\temailOtpSent?: boolean\n\t\t\tmfaEnrollmentRequired?: boolean\n\t\t\tmfaEnrollment?: MfaEnrollment\n\t\t\t[AuthLogService.Key]: AuthLogService.Bag\n\t\t}>\n\t\t| { backupCodes: string[] }\n\t> {\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, person.identity_id, person.roles)\n\t\tif (!policy.mfaRequired) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst now = this.providers.now()\n\n\t\t// Resolve the effective grace duration (seconds). A per-policy override\n\t\t// wins; otherwise we fall back to the global config default. Only fetch the\n\t\t// config when the override is absent, to keep query shapes minimal.\n\t\tlet graceSeconds: number\n\t\tif (policy.graceDuration !== null) {\n\t\t\tgraceSeconds = intervalToSeconds(policy.graceDuration)\n\t\t} else {\n\t\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\t\t\tgraceSeconds = intervalToSeconds(config.login.mfaGraceDuration)\n\t\t}\n\n\t\t// Grace handling. Default grace duration is 0 (immediate enforcement), so\n\t\t// by default neither branch below opens a window.\n\t\tif (person.mfa_grace_until !== null) {\n\t\t\tif (person.mfa_grace_until.getTime() > now.getTime()) {\n\t\t\t\t// Still inside an open grace window — allow sign-in.\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Grace expired → fall through to enforcement.\n\t\t} else if (graceSeconds > 0) {\n\t\t\t// Open the grace window now and allow this sign-in.\n\t\t\tconst graceUntil = new Date(now.getTime() + graceSeconds * 1000)\n\t\t\tawait dbContext.commandBus.execute(new SetMfaGraceUntilCommand(person.id, graceUntil))\n\t\t\treturn null\n\t\t}\n\n\t\t// Enforce.\n\t\tif (otpCode && person.otp_pending_secret) {\n\t\t\t// Enrollment completion: verify against the pending secret, then promote.\n\t\t\tif (!await this.otpManager.verifyPendingOtp(person, otpCode)) {\n\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t}\n\t\t\tconst promoted = await this.otpManager.confirmOtp(dbContext, person)\n\t\t\tif (!promoted) {\n\t\t\t\t// Lost a concurrent enrollment-completion race: the pending secret was\n\t\t\t\t// already promoted by the other request (which issued the backup codes).\n\t\t\t\t// Don't regenerate here — that would invalidate the codes already\n\t\t\t\t// returned. TOTP is now active, so a retry signs in via the normal path.\n\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t}\n\t\t\tconst backupCodes = await this.backupCodeManager.generate(dbContext, person.id)\n\t\t\treturn { backupCodes }\n\t\t}\n\n\t\t// Provision a fresh pending TOTP secret and ask the client to enroll.\n\t\tconst otp = await this.otpManager.prepareOtp(dbContext, person, 'Contember')\n\t\tconst mfaEnrollment: MfaEnrollment = { otpUri: otp.uri, otpSecret: otp.secret }\n\t\treturn new ResponseError('MFA_ENROLLMENT_REQUIRED', 'MFA enrollment is required', {\n\t\t\tmfaEnrollmentRequired: true,\n\t\t\tmfaEnrollment,\n\t\t\t...authLogData,\n\t\t})\n\t}\n\n\tasync createSessionToken(\n\t\tdbContext: DatabaseContext,\n\t\tpersonIdentifier: PersonUniqueIdentifier,\n\t\texpiration?: number,\n\t\tverifier?: (person: PersonRow) => Promise<void>,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateSessionTokenResponse> {\n\t\tconst person = await dbContext.queryHandler.fetch(PersonQuery.byUniqueIdentifier(personIdentifier))\n\n\t\tif (person === null) {\n\t\t\tif (personIdentifier.type === 'email') {\n\t\t\t\treturn new ResponseError('UNKNOWN_EMAIL', `Person with email ${personIdentifier.email} not found`, {\n\t\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\t\tpersonInput: personIdentifier.email,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t} else if (personIdentifier.type === 'id') {\n\t\t\t\treturn new ResponseError('UNKNOWN_PERSON_ID', `Person with id ${personIdentifier.id} not found`, {\n\t\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\t\tpersonInput: personIdentifier.id,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tconst inputValue = personIdentifier.type === 'email' ? personIdentifier.email : personIdentifier.id\n\t\tif (person.disabled_at !== null) {\n\t\t\treturn new ResponseError('PERSON_DISABLED', `Person with id ${person.id} is disabled`, {\n\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\tpersonId: person.id,\n\t\t\t\t\tpersonInput: inputValue,\n\t\t\t\t}),\n\t\t\t})\n\t\t}\n\n\t\tawait verifier?.(person)\n\n\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(dbContext, person.identity_id, expiration, requestInfo, trustForwardedInfo)\n\t\treturn new ResponseOk({\n\t\t\tperson,\n\t\t\ttoken: sessionToken,\n\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\tpersonId: person.id,\n\t\t\t\tpersonInput: inputValue,\n\t\t\t}),\n\t\t})\n\t}\n}\n\n/** A03: greppable payload describing why a sign-in scored as unusual. */\nexport interface RiskMeta {\n\treadonly score: number\n\treadonly reasons: readonly string[]\n}\n\n/**\n * Result of {@link SignInManager.applyRiskPolicy}: `null` = feature off / no\n * config; a metadata object when the session may still be issued; a\n * `ResponseError` when step-up blocks the session.\n */\ntype RiskPolicyOutcome =\n\t| null\n\t| { unusualLoginDetected: boolean; risk?: RiskMeta }\n\t| ResponseError<SignInErrorCode, {\n\t\temailOtpSent?: boolean\n\t\tstepUpRequired?: boolean\n\t\tunusualLoginDetected?: boolean\n\t\trisk?: RiskMeta\n\t\t[AuthLogService.Key]: AuthLogService.Bag\n\t}>\n\nexport interface SignInResult {\n\treadonly person: PersonRow\n\treadonly token: string\n\t/** True when MFA was satisfied by consuming a backup code (instead of a TOTP token). */\n\treadonly usedBackupCode?: boolean\n\t/** Set when sign-in completed an MFA enrollment (A06): the freshly issued backup codes, shown once. */\n\treadonly backupCodes?: string[]\n\t/** A03: true when this sign-in scored as unusual and an UNUSUAL_LOGIN email was sent. */\n\treadonly unusualLoginDetected?: boolean\n\t/** A03: the risk score + reasons, recorded in the `unusual_login_detected` audit `event_data`. */\n\treadonly risk?: RiskMeta\n\t[AuthLogService.Key]: AuthLogService.Bag\n}\n\nexport type SignInResponse = Response<SignInResult, SignInErrorCode, {\n\t/** Set on an OTP_REQUIRED error when an email-OTP code was just dispatched (A05). */\n\temailOtpSent?: boolean\n\t/** Set on a MFA_ENROLLMENT_REQUIRED error (A06): triggers the `mfa_enrollment_required` audit. */\n\tmfaEnrollmentRequired?: boolean\n\t/** Set on a MFA_ENROLLMENT_REQUIRED error (A06): the pending secret the client must enroll. */\n\tmfaEnrollment?: MfaEnrollment\n\t/** Set on an OTP_REQUIRED error raised by A03 anomaly step-up: triggers the `step_up_required` audit. */\n\tstepUpRequired?: boolean\n\t/** A03: true when the sign-in scored as unusual (triggers the `unusual_login_detected` audit). */\n\tunusualLoginDetected?: boolean\n\t/** A03: the risk score + reasons, recorded in the audit `event_data`. */\n\trisk?: RiskMeta\n\t[AuthLogService.Key]: AuthLogService.Bag\n}>\nexport type CreateSessionTokenResponse = Response<SignInResult, CreateSessionTokenErrorCode, {\n\t[AuthLogService.Key]: AuthLogService.Bag\n}>\n\nexport { SignInManager }\n"],"names":["riskHandled","sessionToken"],"mappings":"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"}
1
+ {"version":3,"file":"SignInManager.js","sources":["../../../../../../packages/engine-tenant-api/src/model/service/SignInManager.ts"],"sourcesContent":["import { CreateSessionTokenErrorCode, MfaEnrollment, SignInErrorCode } from '../../schema/index.js'\nimport { ApiKeyManager, AuthPolicyResolver, BackupCodeManager, EmailOtpManager, LoginRiskAnalyzer, OtpManager } from '../service/index.js'\nimport { ConfigurationQuery, LocalAuthDisablingIdpsQuery, PersonQuery, PersonRow, PersonUniqueIdentifier } from '../queries/index.js'\nimport { Config } from '../type/Config.js'\nimport { Providers } from '../providers.js'\nimport { DatabaseContext } from '../utils/index.js'\nimport { Response, ResponseError, ResponseOk } from '../utils/Response.js'\nimport { ImplementationException } from '../../exceptions.js'\nimport { AuthLogService } from './AuthLogService.js'\nimport { UserMailer } from '../mailing/index.js'\nimport { ApiKeyRequestInfo, SetMfaGraceUntilCommand } from '../commands/index.js'\nimport { intervalToSeconds } from '../utils/interval.js'\n\nclass SignInManager {\n\tconstructor(\n\t\tprivate readonly apiKeyManager: ApiKeyManager,\n\t\tprivate readonly providers: Providers,\n\t\tprivate readonly otpManager: OtpManager,\n\t\tprivate readonly backupCodeManager: BackupCodeManager,\n\t\tprivate readonly emailOtpManager: EmailOtpManager,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly loginRiskAnalyzer: LoginRiskAnalyzer,\n\t\tprivate readonly userMailer: UserMailer,\n\t) {}\n\n\tasync signIn(\n\t\tdbContext: DatabaseContext,\n\t\temail: string,\n\t\tpassword: string,\n\t\texpiration?: number,\n\t\totpCode?: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t\tbackupCode?: string,\n\t\tconfig?: Config,\n\t\tgeoCountry?: string,\n\t): Promise<SignInResponse> {\n\t\tconst person = await dbContext.queryHandler.fetch(PersonQuery.byEmail(email))\n\n\t\tif (person === null) {\n\t\t\treturn new ResponseError('UNKNOWN_EMAIL', `Person with email ${email} not found`, {\n\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\tpersonInput: email,\n\t\t\t\t}),\n\t\t\t})\n\t\t}\n\n\t\tconst authLogData = {\n\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\tpersonInput: email,\n\t\t\t\tpersonId: person.id,\n\t\t\t}),\n\t\t}\n\t\t// Refused before the password is even looked at: for a person federated\n\t\t// through a provider with `disableLocalAuthentication`, a password is not a\n\t\t// credential this tenant accepts, however it got set.\n\t\tconst exclusiveIdps = await dbContext.queryHandler.fetch(new LocalAuthDisablingIdpsQuery(person.id))\n\t\tif (exclusiveIdps.length > 0) {\n\t\t\treturn new ResponseError('IDP_REQUIRED', `Sign in using ${exclusiveIdps.join(' or ')}`, authLogData)\n\t\t}\n\n\t\tif (!person.password_hash) {\n\t\t\treturn new ResponseError('NO_PASSWORD_SET', `No password set`, authLogData)\n\t\t}\n\n\t\tif (person.disabled_at !== null) {\n\t\t\treturn new ResponseError('PERSON_DISABLED', `Person is disabled`, authLogData)\n\t\t}\n\n\t\tconst passwordValid = await this.providers.bcryptCompare(password, person.password_hash)\n\n\t\tif (!passwordValid) {\n\t\t\treturn new ResponseError('INVALID_PASSWORD', `Password does not match`, authLogData)\n\t\t}\n\n\t\t// Block sign-in for accounts that were created while email verification\n\t\t// was required but never proved ownership. Accounts predating the\n\t\t// requirement carry email_verification_required = false and pass through.\n\t\tif (person.email_verification_required && person.email_verified_at === null) {\n\t\t\treturn new ResponseError('EMAIL_NOT_VERIFIED', `E-mail address has not been verified`, authLogData)\n\t\t}\n\n\t\tlet usedBackupCode = false\n\t\t// Whether a second factor was actively proven in THIS request. Anomaly\n\t\t// step-up (A03) reuses this: a login that already cleared a second factor\n\t\t// is not asked to step up again.\n\t\tlet mfaSatisfiedThisRequest = false\n\t\tconst hasActiveTotp = Boolean(person.otp_secret && person.otp_activated_at)\n\t\tif (hasActiveTotp) {\n\t\t\t// Active TOTP takes precedence (A07 path, unchanged).\n\t\t\tif (otpCode) {\n\t\t\t\tif (!await this.otpManager.verifyOtp(dbContext, person, otpCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else if (backupCode) {\n\t\t\t\tif (!await this.backupCodeManager.verifyAndConsume(dbContext, person, backupCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tusedBackupCode = true\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else {\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', `2FA is enabled. OTP token is required`, authLogData)\n\t\t\t}\n\t\t} else if (person.email_otp_enabled) {\n\t\t\t// No active TOTP, but email OTP is enabled (A05).\n\t\t\tif (otpCode) {\n\t\t\t\tif (!await this.emailOtpManager.verifyAndConsume(dbContext, person, otpCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else if (backupCode) {\n\t\t\t\tif (!await this.backupCodeManager.verifyAndConsume(dbContext, person, backupCode)) {\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t\t}\n\t\t\t\tusedBackupCode = true\n\t\t\t\tmfaSatisfiedThisRequest = true\n\t\t\t} else {\n\t\t\t\t// Dispatch a fresh code and ask the client to retry with otpToken. Reuses\n\t\t\t\t// the existing OTP_REQUIRED contract so clients that already handle it work.\n\t\t\t\t// The send is rate-limited per person; when throttled nothing is emailed\n\t\t\t\t// (emailOtpSent: false) but a previously emailed code stays valid.\n\t\t\t\t// Independent fetch (not the passed-in `config`): the email-OTP send is\n\t\t\t\t// rate-limited by the live config and this branch never reaches the\n\t\t\t\t// anomaly analyzer, so query shape stays as before A03.\n\t\t\t\tconst emailOtpConfig = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\t\t\t\tconst decision = await this.emailOtpManager.sendCode(dbContext, person, emailOtpConfig)\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', `2FA is enabled. OTP token is required`, {\n\t\t\t\t\temailOtpSent: decision.ok,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t} else {\n\t\t\t// No active factor at all (A06). Enforce MFA if a matching auth_policy\n\t\t\t// requires it. With no policy rows configured this resolves to\n\t\t\t// mfaRequired=false and sign-in proceeds exactly as before.\n\t\t\tconst enforcement = await this.enforceMfaEnrollment(dbContext, person, otpCode, authLogData)\n\t\t\tif (enforcement !== null) {\n\t\t\t\tif ('error' in enforcement) {\n\t\t\t\t\treturn enforcement\n\t\t\t\t}\n\t\t\t\t// Enrollment completed in this request (pending TOTP promoted to active),\n\t\t\t\t// so MFA is definitively satisfied — anomaly step-up cannot apply.\n\t\t\t\tconst riskHandled = await this.applyRiskPolicy(dbContext, person, {\n\t\t\t\t\tconfig,\n\t\t\t\t\tgeoCountry,\n\t\t\t\t\trequestInfo,\n\t\t\t\t\totpCode,\n\t\t\t\t\tmfaSatisfiedThisRequest: true,\n\t\t\t\t\tauthLogData,\n\t\t\t\t})\n\t\t\t\tif (riskHandled !== null && 'error' in riskHandled) {\n\t\t\t\t\treturn riskHandled\n\t\t\t\t}\n\t\t\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(\n\t\t\t\t\tdbContext,\n\t\t\t\t\tperson.identity_id,\n\t\t\t\t\texpiration,\n\t\t\t\t\trequestInfo,\n\t\t\t\t\ttrustForwardedInfo,\n\t\t\t\t)\n\t\t\t\treturn new ResponseOk({\n\t\t\t\t\tperson,\n\t\t\t\t\ttoken: sessionToken,\n\t\t\t\t\tbackupCodes: enforcement.backupCodes,\n\t\t\t\t\tunusualLoginDetected: riskHandled?.unusualLoginDetected || undefined,\n\t\t\t\t\trisk: riskHandled?.risk,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tconst riskHandled = await this.applyRiskPolicy(dbContext, person, {\n\t\t\tconfig,\n\t\t\tgeoCountry,\n\t\t\trequestInfo,\n\t\t\totpCode,\n\t\t\tmfaSatisfiedThisRequest,\n\t\t\tauthLogData,\n\t\t})\n\t\tif (riskHandled !== null && 'error' in riskHandled) {\n\t\t\treturn riskHandled\n\t\t}\n\n\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(dbContext, person.identity_id, expiration, requestInfo, trustForwardedInfo)\n\n\t\treturn new ResponseOk({\n\t\t\tperson,\n\t\t\ttoken: sessionToken,\n\t\t\tusedBackupCode,\n\t\t\tunusualLoginDetected: riskHandled?.unusualLoginDetected || undefined,\n\t\t\trisk: riskHandled?.risk,\n\t\t\t...authLogData,\n\t\t})\n\t}\n\n\t/**\n\t * A03 — evaluate the sign-in risk score and act per policy. Returns:\n\t * - `null` when anomaly detection is off or no `config` was supplied (the\n\t * default): no DB work, sign-in proceeds unchanged.\n\t * - `{ unusualLoginDetected: true }` when an informational UNUSUAL_LOGIN email\n\t * was sent (best-effort) but the session may still be issued.\n\t * - a `ResponseError('OTP_REQUIRED')` when step-up is required and no code was\n\t * supplied — reuses the email-OTP step-up channel (dispatches a code).\n\t * - a `ResponseError('INVALID_OTP_TOKEN')` when a step-up code was supplied but\n\t * did not verify. A valid code consumes the token and the session is issued.\n\t * - `{ unusualLoginDetected: false }` when the score was below every threshold.\n\t *\n\t * The audit entries (`unusual_login_detected`, `step_up_required`) are emitted\n\t * by the resolver from the response metadata, matching the project convention.\n\t */\n\tprivate async applyRiskPolicy(\n\t\tdbContext: DatabaseContext,\n\t\tperson: PersonRow,\n\t\targs: {\n\t\t\tconfig?: Config\n\t\t\tgeoCountry?: string\n\t\t\trequestInfo?: ApiKeyRequestInfo\n\t\t\totpCode?: string\n\t\t\tmfaSatisfiedThisRequest: boolean\n\t\t\tauthLogData: { [AuthLogService.Key]: AuthLogService.Bag }\n\t\t},\n\t): Promise<RiskPolicyOutcome> {\n\t\tconst { config, geoCountry, requestInfo, otpCode, mfaSatisfiedThisRequest, authLogData } = args\n\t\tconst policy = config?.login.anomalyDetection\n\t\tif (!config || !policy?.enabled) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst assessment = await this.loginRiskAnalyzer.analyze(dbContext, person.id, {\n\t\t\tgeoCountry: geoCountry ?? null,\n\t\t\tdeviceFingerprint: this.loginRiskAnalyzer.fingerprint(requestInfo?.userAgent),\n\t\t\tip: requestInfo?.ip ?? null,\n\t\t}, policy)\n\n\t\tif (assessment.action === 'allow') {\n\t\t\treturn { unusualLoginDetected: false }\n\t\t}\n\n\t\tconst riskMeta = { score: assessment.score, reasons: assessment.reasons }\n\n\t\tif (assessment.action === 'stepUp' && !mfaSatisfiedThisRequest) {\n\t\t\t// Reuse the existing email-OTP step-up channel. This is independent of\n\t\t\t// `person.email_otp_enabled`, which gates email OTP only as the *primary*\n\t\t\t// second factor (A05); the step-up challenge stands on its own, so a person\n\t\t\t// with no standing MFA can still complete it. Without consuming the code\n\t\t\t// here, such a person would never satisfy the step-up and would loop on\n\t\t\t// OTP_REQUIRED forever (the score is unchanged on every retry).\n\t\t\tif (otpCode) {\n\t\t\t\t// Retry leg: the client resent the emailed code as otpToken. Mirror the\n\t\t\t\t// A05 contract — an invalid code is rejected (not re-sent); the per-code\n\t\t\t\t// 3-attempt cap still applies, and a fresh code is issued only when the\n\t\t\t\t// client retries without a code.\n\t\t\t\tif (!await this.emailOtpManager.verifyAndConsume(dbContext, person, otpCode)) {\n\t\t\t\t\t// A failed *forced* second factor is itself an anomaly signal — carry the\n\t\t\t\t\t// step-up/risk metadata so the resolver still emits unusual_login_detected\n\t\t\t\t\t// + step_up_required with the score/reasons, rather than logging it as an\n\t\t\t\t\t// ordinary mistyped-OTP login indistinguishable from a normal 2FA typo.\n\t\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', {\n\t\t\t\t\t\tstepUpRequired: true,\n\t\t\t\t\t\tunusualLoginDetected: true,\n\t\t\t\t\t\trisk: riskMeta,\n\t\t\t\t\t\t...authLogData,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\t// Step-up satisfied this request — fall through to notify + allow below.\n\t\t\t} else {\n\t\t\t\t// First leg: dispatch a code and ask the client to retry with otpToken.\n\t\t\t\t// Rate-limited per person; a previously emailed code stays valid when throttled.\n\t\t\t\tconst decision = await this.emailOtpManager.sendCode(dbContext, person, config)\n\t\t\t\treturn new ResponseError('OTP_REQUIRED', 'Additional verification is required for this sign-in', {\n\t\t\t\t\temailOtpSent: decision.ok,\n\t\t\t\t\tstepUpRequired: true,\n\t\t\t\t\tunusualLoginDetected: true,\n\t\t\t\t\trisk: riskMeta,\n\t\t\t\t\t...authLogData,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// action === 'email', or step-up satisfied (already this request, or just now\n\t\t// via the emailed code): notify the person of the unusual sign-in. A failed\n\t\t// email must never block sign-in.\n\t\tawait this.sendUnusualLoginEmail(dbContext, person, geoCountry ?? null, requestInfo?.ip ?? null)\n\t\treturn { unusualLoginDetected: true, risk: riskMeta }\n\t}\n\n\tprivate async sendUnusualLoginEmail(dbContext: DatabaseContext, person: PersonRow, geoCountry: string | null, ip: string | null): Promise<void> {\n\t\tif (!person.email) {\n\t\t\t// No address to notify (e.g. an IDP-only person) — nothing to send.\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tawait this.userMailer.sendUnusualLoginEmail(dbContext, {\n\t\t\t\temail: person.email,\n\t\t\t\tgeoCountry,\n\t\t\t\tipAddress: ip,\n\t\t\t}, { projectId: null, variant: '' })\n\t\t} catch {\n\t\t\t// ignore — informational only\n\t\t}\n\t}\n\n\t/**\n\t * A06 enforcement for the no-active-factor case. Returns:\n\t * - `null` when MFA is not required, or grace currently allows sign-in\n\t * (caller proceeds to issue a session as normal). With zero auth_policy\n\t * rows this is always the outcome — no behavior change.\n\t * - a `ResponseError` (MFA_ENROLLMENT_REQUIRED with the pending secret to\n\t * enroll, or INVALID_OTP_TOKEN) when sign-in must be blocked.\n\t * - `{ ok: true, backupCodes }` when enrollment just completed (pending TOTP\n\t * promoted to active); caller issues the session.\n\t */\n\tprivate async enforceMfaEnrollment(\n\t\tdbContext: DatabaseContext,\n\t\tperson: PersonRow,\n\t\totpCode: string | undefined,\n\t\tauthLogData: { [AuthLogService.Key]: AuthLogService.Bag },\n\t): Promise<\n\t\t| null\n\t\t| ResponseError<SignInErrorCode, {\n\t\t\temailOtpSent?: boolean\n\t\t\tmfaEnrollmentRequired?: boolean\n\t\t\tmfaEnrollment?: MfaEnrollment\n\t\t\t[AuthLogService.Key]: AuthLogService.Bag\n\t\t}>\n\t\t| { backupCodes: string[] }\n\t> {\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, person.identity_id, person.roles)\n\t\tif (!policy.mfaRequired) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst now = this.providers.now()\n\n\t\t// Resolve the effective grace duration (seconds). A per-policy override\n\t\t// wins; otherwise we fall back to the global config default. Only fetch the\n\t\t// config when the override is absent, to keep query shapes minimal.\n\t\tlet graceSeconds: number\n\t\tif (policy.graceDuration !== null) {\n\t\t\tgraceSeconds = intervalToSeconds(policy.graceDuration)\n\t\t} else {\n\t\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\t\t\tgraceSeconds = intervalToSeconds(config.login.mfaGraceDuration)\n\t\t}\n\n\t\t// Grace handling. Default grace duration is 0 (immediate enforcement), so\n\t\t// by default neither branch below opens a window.\n\t\tif (person.mfa_grace_until !== null) {\n\t\t\tif (person.mfa_grace_until.getTime() > now.getTime()) {\n\t\t\t\t// Still inside an open grace window — allow sign-in.\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Grace expired → fall through to enforcement.\n\t\t} else if (graceSeconds > 0) {\n\t\t\t// Open the grace window now and allow this sign-in.\n\t\t\tconst graceUntil = new Date(now.getTime() + graceSeconds * 1000)\n\t\t\tawait dbContext.commandBus.execute(new SetMfaGraceUntilCommand(person.id, graceUntil))\n\t\t\treturn null\n\t\t}\n\n\t\t// Enforce.\n\t\tif (otpCode && person.otp_pending_secret) {\n\t\t\t// Enrollment completion: verify against the pending secret, then promote.\n\t\t\tif (!await this.otpManager.verifyPendingOtp(person, otpCode)) {\n\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t}\n\t\t\tconst promoted = await this.otpManager.confirmOtp(dbContext, person)\n\t\t\tif (!promoted) {\n\t\t\t\t// Lost a concurrent enrollment-completion race: the pending secret was\n\t\t\t\t// already promoted by the other request (which issued the backup codes).\n\t\t\t\t// Don't regenerate here — that would invalidate the codes already\n\t\t\t\t// returned. TOTP is now active, so a retry signs in via the normal path.\n\t\t\t\treturn new ResponseError('INVALID_OTP_TOKEN', 'OTP token validation has failed', authLogData)\n\t\t\t}\n\t\t\tconst backupCodes = await this.backupCodeManager.generate(dbContext, person.id)\n\t\t\treturn { backupCodes }\n\t\t}\n\n\t\t// Provision a fresh pending TOTP secret and ask the client to enroll.\n\t\tconst otp = await this.otpManager.prepareOtp(dbContext, person, 'Contember')\n\t\tconst mfaEnrollment: MfaEnrollment = { otpUri: otp.uri, otpSecret: otp.secret }\n\t\treturn new ResponseError('MFA_ENROLLMENT_REQUIRED', 'MFA enrollment is required', {\n\t\t\tmfaEnrollmentRequired: true,\n\t\t\tmfaEnrollment,\n\t\t\t...authLogData,\n\t\t})\n\t}\n\n\tasync createSessionToken(\n\t\tdbContext: DatabaseContext,\n\t\tpersonIdentifier: PersonUniqueIdentifier,\n\t\texpiration?: number,\n\t\tverifier?: (person: PersonRow) => Promise<void>,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateSessionTokenResponse> {\n\t\tconst person = await dbContext.queryHandler.fetch(PersonQuery.byUniqueIdentifier(personIdentifier))\n\n\t\tif (person === null) {\n\t\t\tif (personIdentifier.type === 'email') {\n\t\t\t\treturn new ResponseError('UNKNOWN_EMAIL', `Person with email ${personIdentifier.email} not found`, {\n\t\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\t\tpersonInput: personIdentifier.email,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t} else if (personIdentifier.type === 'id') {\n\t\t\t\treturn new ResponseError('UNKNOWN_PERSON_ID', `Person with id ${personIdentifier.id} not found`, {\n\t\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\t\tpersonInput: personIdentifier.id,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tthrow new ImplementationException()\n\t\t}\n\n\t\tconst inputValue = personIdentifier.type === 'email' ? personIdentifier.email : personIdentifier.id\n\t\tif (person.disabled_at !== null) {\n\t\t\treturn new ResponseError('PERSON_DISABLED', `Person with id ${person.id} is disabled`, {\n\t\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\t\tpersonId: person.id,\n\t\t\t\t\tpersonInput: inputValue,\n\t\t\t\t}),\n\t\t\t})\n\t\t}\n\n\t\tawait verifier?.(person)\n\n\t\tconst sessionToken = await this.apiKeyManager.createSessionApiKey(dbContext, person.identity_id, expiration, requestInfo, trustForwardedInfo)\n\t\treturn new ResponseOk({\n\t\t\tperson,\n\t\t\ttoken: sessionToken,\n\t\t\t[AuthLogService.Key]: new AuthLogService.Bag({\n\t\t\t\tpersonId: person.id,\n\t\t\t\tpersonInput: inputValue,\n\t\t\t}),\n\t\t})\n\t}\n}\n\n/** A03: greppable payload describing why a sign-in scored as unusual. */\nexport interface RiskMeta {\n\treadonly score: number\n\treadonly reasons: readonly string[]\n}\n\n/**\n * Result of {@link SignInManager.applyRiskPolicy}: `null` = feature off / no\n * config; a metadata object when the session may still be issued; a\n * `ResponseError` when step-up blocks the session.\n */\ntype RiskPolicyOutcome =\n\t| null\n\t| { unusualLoginDetected: boolean; risk?: RiskMeta }\n\t| ResponseError<SignInErrorCode, {\n\t\temailOtpSent?: boolean\n\t\tstepUpRequired?: boolean\n\t\tunusualLoginDetected?: boolean\n\t\trisk?: RiskMeta\n\t\t[AuthLogService.Key]: AuthLogService.Bag\n\t}>\n\nexport interface SignInResult {\n\treadonly person: PersonRow\n\treadonly token: string\n\t/** True when MFA was satisfied by consuming a backup code (instead of a TOTP token). */\n\treadonly usedBackupCode?: boolean\n\t/** Set when sign-in completed an MFA enrollment (A06): the freshly issued backup codes, shown once. */\n\treadonly backupCodes?: string[]\n\t/** A03: true when this sign-in scored as unusual and an UNUSUAL_LOGIN email was sent. */\n\treadonly unusualLoginDetected?: boolean\n\t/** A03: the risk score + reasons, recorded in the `unusual_login_detected` audit `event_data`. */\n\treadonly risk?: RiskMeta\n\t[AuthLogService.Key]: AuthLogService.Bag\n}\n\nexport type SignInResponse = Response<SignInResult, SignInErrorCode, {\n\t/** Set on an OTP_REQUIRED error when an email-OTP code was just dispatched (A05). */\n\temailOtpSent?: boolean\n\t/** Set on a MFA_ENROLLMENT_REQUIRED error (A06): triggers the `mfa_enrollment_required` audit. */\n\tmfaEnrollmentRequired?: boolean\n\t/** Set on a MFA_ENROLLMENT_REQUIRED error (A06): the pending secret the client must enroll. */\n\tmfaEnrollment?: MfaEnrollment\n\t/** Set on an OTP_REQUIRED error raised by A03 anomaly step-up: triggers the `step_up_required` audit. */\n\tstepUpRequired?: boolean\n\t/** A03: true when the sign-in scored as unusual (triggers the `unusual_login_detected` audit). */\n\tunusualLoginDetected?: boolean\n\t/** A03: the risk score + reasons, recorded in the audit `event_data`. */\n\trisk?: RiskMeta\n\t[AuthLogService.Key]: AuthLogService.Bag\n}>\nexport type CreateSessionTokenResponse = Response<SignInResult, CreateSessionTokenErrorCode, {\n\t[AuthLogService.Key]: AuthLogService.Bag\n}>\n\nexport { SignInManager }\n"],"names":["riskHandled","sessionToken"],"mappings":"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"}
@@ -92,8 +92,8 @@ class ApiKeyManager {
92
92
  return new Response.ResponseError("disabled", "IdP session was revoked");
93
93
  }
94
94
  }
95
- setImmediate(async () => {
96
- await dbContext.commandBus.execute(
95
+ setImmediate(() => {
96
+ dbContext.commandBus.execute(
97
97
  new ProlongApiKeyCommand.ProlongApiKeyCommand(
98
98
  apiKeyRow.id,
99
99
  apiKeyRow.type,
@@ -107,7 +107,8 @@ class ApiKeyManager {
107
107
  },
108
108
  apiKeyRow.max_expires_at
109
109
  )
110
- );
110
+ ).catch(() => {
111
+ });
111
112
  });
112
113
  return new Response.ResponseOk(
113
114
  new VerifyResult(
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeyManager.cjs","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/ApiKeyManager.ts"],"sourcesContent":["import {\n\tApiKeyRequestInfo,\n\tCreateApiKeyCommand,\n\tDisableApiKeyCommand,\n\tDisableIdentityApiKeysCommand,\n\tDisableOneOffApiKeyCommand,\n\tPROLONG_THROTTLE_MS,\n\tProlongApiKeyCommand,\n} from '../../commands/index.js'\nimport { ApiKey } from '../../type/index.js'\n\nimport { Response, ResponseError, ResponseOk } from '../../utils/Response.js'\nimport { DatabaseContext, TokenHash } from '../../utils/index.js'\nimport { ApiKeyService, CreateApiKeyResponse } from './ApiKeyService.js'\nimport assert from 'node:assert'\nimport { Acl } from '@contember/schema'\nimport { ApiKeyByIdQuery, ApiKeyByTokenQuery, ApiKeyRow, ConfigurationQuery, IdentityQuery } from '../../queries/index.js'\nimport PostgresInterval from 'postgres-interval'\nimport { Config } from '../../type/Config.js'\nimport { intervalToPostgres, intervalToSeconds } from '../../utils/interval.js'\nimport { AuthPolicyResolver } from '../AuthPolicyResolver.js'\nimport { AuthLogService } from '../AuthLogService.js'\nimport { IdpSessionRevalidator } from '../idp/IdpSessionRevalidator.js'\nimport { UnpersistedApiKeyManager } from './UnpersistedApiKeyManager.js'\n\nexport class ApiKeyManager {\n\tconstructor(\n\t\tprivate readonly apiKeyService: ApiKeyService,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly authLogService: AuthLogService,\n\t\tprivate readonly unpersistedApiKeyManager: UnpersistedApiKeyManager = new UnpersistedApiKeyManager(),\n\t\tprivate readonly idpSessionRevalidator?: IdpSessionRevalidator,\n\t) {}\n\n\tasync verifyAndProlong(\n\t\tdbContext: DatabaseContext,\n\t\treadDbContext: DatabaseContext,\n\t\ttoken: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\tforwardedInfo?: ApiKeyRequestInfo,\n\t): Promise<VerifyResponse> {\n\t\tconst unpersistedResult = this.unpersistedApiKeyManager.verify(token)\n\t\tif (unpersistedResult !== null) {\n\t\t\t// Configured (unpersisted) root token: no DB row to prolong/track.\n\t\t\treturn new ResponseOk(unpersistedResult)\n\t\t}\n\n\t\tconst apiKeyRow = await readDbContext.queryHandler.fetch(new ApiKeyByTokenQuery(token))\n\t\tif (apiKeyRow === null) {\n\t\t\treturn new ResponseError(VerifyErrorCode.NOT_FOUND, 'API key was not found')\n\t\t}\n\n\t\tif (apiKeyRow.disabled_at !== null) {\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key was disabled at ${apiKeyRow.disabled_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// Single injectable clock for all session-policy time math (matches issuance,\n\t\t// which uses providers.now()), so the A19 idle/max logic is deterministically testable.\n\t\tconst now = dbContext.providers.now()\n\t\tif (apiKeyRow.expires_at !== null && apiKeyRow.expires_at <= now) {\n\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, `API key expired at ${apiKeyRow.expires_at.toISOString()}`)\n\t\t}\n\n\t\t// A19: absolute hard cap (defensive — the prolong clamp should already keep\n\t\t// expires_at <= max_expires_at, but enforce it here regardless).\n\t\tif (apiKeyRow.max_expires_at !== null && apiKeyRow.max_expires_at <= now) {\n\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key reached its maximum lifetime at ${apiKeyRow.max_expires_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// A19: idle timeout. Reject + disable when the session has been idle longer\n\t\t// than the policy's idle_timeout. A null idle_timeout (today's default) or a\n\t\t// never-used key (last_used_at null) are never idle-expired.\n\t\tif (apiKeyRow.idle_timeout !== null && apiKeyRow.last_used_at !== null) {\n\t\t\tconst idleMs = intervalToSeconds(apiKeyRow.idle_timeout) * 1000\n\t\t\t// last_used_at is written at most once per PROLONG_THROTTLE_MS (tracking is\n\t\t\t// throttled), so it can legitimately lag a continuously-active session by up\n\t\t\t// to that window. Add the throttle as slack so a small idle_timeout can't\n\t\t\t// idle-expire a session that is actually in use.\n\t\t\tif (now.getTime() - apiKeyRow.last_used_at.getTime() > idleMs + PROLONG_THROTTLE_MS) {\n\t\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\t\ttype: 'session_expired_idle',\n\t\t\t\t\t\tidentityId: apiKeyRow.identity_id,\n\t\t\t\t\t\tpersonId: apiKeyRow.person_id,\n\t\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\teventData: {\n\t\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at?.toISOString() ?? null,\n\t\t\t\t\t\t\tidleTimeout: String(apiKeyRow.idle_timeout),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort: auth path must stay resilient */\n\t\t\t\t}\n\t\t\t\treturn new ResponseError(\n\t\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t\t`API key was idle since ${apiKeyRow.last_used_at.toISOString()} and exceeded the idle timeout`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst effectiveInfo: ApiKeyRequestInfo | undefined = apiKeyRow.trust_forwarded_info && forwardedInfo\n\t\t\t? {\n\t\t\t\tip: forwardedInfo.ip ?? requestInfo?.ip,\n\t\t\t\tuserAgent: forwardedInfo.userAgent ?? requestInfo?.userAgent,\n\t\t\t}\n\t\t\t: requestInfo\n\n\t\t// Re-validate IdP-backed sessions against their identity provider (A24). In the\n\t\t// default `background` mode this returns immediately and revalidates after the\n\t\t// response (revocation lands on the next request); in `blocking` mode it can revoke\n\t\t// the current request. No-op for password sessions and IdPs without revalidation.\n\t\t// `effectiveInfo` is passed so the audit entries record the originating request's IP/UA.\n\t\tif (this.idpSessionRevalidator) {\n\t\t\tconst outcome = await this.idpSessionRevalidator.revalidate(dbContext, readDbContext, apiKeyRow, effectiveInfo)\n\t\t\tif (outcome === 'revoked') {\n\t\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, 'IdP session was revoked')\n\t\t\t}\n\t\t}\n\n\t\tsetImmediate(async () => {\n\t\t\tawait dbContext.commandBus.execute(\n\t\t\t\tnew ProlongApiKeyCommand(\n\t\t\t\t\tapiKeyRow.id,\n\t\t\t\t\tapiKeyRow.type,\n\t\t\t\t\tapiKeyRow.expiration,\n\t\t\t\t\tapiKeyRow.expires_at,\n\t\t\t\t\teffectiveInfo,\n\t\t\t\t\t{\n\t\t\t\t\t\tlastIp: apiKeyRow.last_ip,\n\t\t\t\t\t\tlastUserAgent: apiKeyRow.last_user_agent,\n\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at,\n\t\t\t\t\t},\n\t\t\t\t\tapiKeyRow.max_expires_at,\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\n\t\treturn new ResponseOk(\n\t\t\tnew VerifyResult(\n\t\t\t\tapiKeyRow.identity_id,\n\t\t\t\tapiKeyRow.id,\n\t\t\t\tapiKeyRow.roles,\n\t\t\t\tapiKeyRow.person_id,\n\t\t\t\tapiKeyRow.trust_forwarded_info,\n\t\t\t),\n\t\t)\n\t}\n\n\tasync createSessionApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<string> {\n\t\tconst { token } = await this.createSessionApiKeyWithId(dbContext, identityId, expiration, requestInfo, trustForwardedInfo)\n\t\treturn token\n\t}\n\n\t/**\n\t * Like {@link createSessionApiKey} but also returns the api_key id — needed when the\n\t * caller has to bind another row to the session (e.g. an `idp_session` for IdP\n\t * re-validation).\n\t */\n\tasync createSessionApiKeyWithId(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<{ id: string; token: string }> {\n\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\n\t\t// A19: snapshot the effective session policy onto the api_key at sign-in.\n\t\t// With no matching policy, every field is null/baseline and the result is\n\t\t// byte-for-byte today's behavior.\n\t\tconst [identityRow] = await dbContext.queryHandler.fetch(new IdentityQuery([identityId]))\n\t\tconst globalRoles = identityRow?.roles ?? []\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, identityId, globalRoles)\n\n\t\tconst defaultExpirationMinutes = intervalToSeconds(config.login.defaultTokenExpiration) / 60\n\t\t// remember_me_allowed === false: ignore a client-supplied longer lifetime and\n\t\t// force the default. (true/null leave the requested expiration untouched.)\n\t\tconst requestedMinutes = policy.rememberMeAllowed === false\n\t\t\t? defaultExpirationMinutes\n\t\t\t: (expiration ?? defaultExpirationMinutes)\n\n\t\t// Cap by maxTokenExpiration (existing behavior) AND by the policy's\n\t\t// tokenExpiration (A19) — strictest wins.\n\t\tlet expirationCapped = config.login.maxTokenExpiration\n\t\t\t? Math.min(requestedMinutes, intervalToSeconds(config.login.maxTokenExpiration) / 60)\n\t\t\t: requestedMinutes\n\t\tif (policy.tokenExpiration) {\n\t\t\texpirationCapped = Math.min(expirationCapped, intervalToSeconds(policy.tokenExpiration) / 60)\n\t\t}\n\n\t\t// Absolute hard cap: issued_at + policy.tokenExpiration. NULL = uncapped\n\t\t// sliding window (today's behavior).\n\t\tconst issuedAt = dbContext.providers.now()\n\t\tconst maxExpiresAt = policy.tokenExpiration\n\t\t\t? new Date(issuedAt.getTime() + intervalToSeconds(policy.tokenExpiration) * 1000)\n\t\t\t: null\n\n\t\tconst command = new CreateApiKeyCommand({\n\t\t\ttype: ApiKey.Type.SESSION,\n\t\t\tidentityId,\n\t\t\texpiration: expirationCapped,\n\t\t\trequestInfo,\n\t\t\ttrustForwardedInfo,\n\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\tmaxExpiresAt,\n\t\t})\n\t\tconst { id, token } = await dbContext.commandBus.execute(command)\n\t\tassert(token !== undefined)\n\n\t\t// A19: audit only when a policy is actually in effect. With zero auth_policy\n\t\t// rows the resolved policy is inert and no extra insert happens.\n\t\tconst policyApplied = policy.mfaRequired === true\n\t\t\t|| policy.tokenExpiration !== null\n\t\t\t|| policy.idleTimeout !== null\n\t\t\t|| policy.rememberMeAllowed !== null\n\t\tif (policyApplied) {\n\t\t\ttry {\n\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\ttype: 'session_policy_applied',\n\t\t\t\t\tidentityId,\n\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\teventData: {\n\t\t\t\t\t\ttokenExpiration: policy.tokenExpiration ? intervalToPostgres(policy.tokenExpiration) : null,\n\t\t\t\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\t\t\t\trememberMeAllowed: policy.rememberMeAllowed,\n\t\t\t\t\t\tmfaRequired: policy.mfaRequired,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t/* best-effort: sign-in must not break on an audit failure */\n\t\t\t}\n\t\t}\n\n\t\treturn { id, token }\n\t}\n\n\tasync findApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<ApiKeyRow | null> {\n\t\treturn await dbContext.queryHandler.fetch(\n\t\t\tnew ApiKeyByIdQuery(apiKeyId),\n\t\t)\n\t}\n\n\tasync disableOneOffApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableOneOffApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<boolean> {\n\t\treturn await dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableIdentityApiKeys(dbContext: DatabaseContext, identityId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableIdentityApiKeysCommand(identityId))\n\t}\n\n\tasync createGlobalPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tdescription: string,\n\t\troles: readonly string[],\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createPermanentApiKey(db, description, roles, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n\n\tasync createProjectPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tprojectId: string,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tdescription: string,\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createProjectPermanentApiKey(db, projectId, memberships, description, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n}\n\nexport type VerifyResponse = Response<VerifyResult, VerifyErrorCode>\n\nexport class VerifyResult {\n\treadonly valid = true\n\n\tconstructor(\n\t\tpublic readonly identityId: string,\n\t\tpublic readonly apiKeyId: string,\n\t\tpublic readonly roles: string[],\n\t\tpublic readonly personId: string | null,\n\t\tpublic readonly trustForwardedInfo: boolean = false,\n\t) {}\n}\n\nexport enum VerifyErrorCode {\n\tNOT_FOUND = 'not_found',\n\tDISABLED = 'disabled',\n\tEXPIRED = 'expired',\n\tNO_AUTH_HEADER = 'no_auth_header',\n\tINVALID_AUTH_HEADER = 'invalid_auth_header',\n}\n"],"names":["UnpersistedApiKeyManager","ResponseOk","ApiKeyByTokenQuery","ResponseError","DisableApiKeyCommand","intervalToSeconds","PROLONG_THROTTLE_MS","ProlongApiKeyCommand","ConfigurationQuery","IdentityQuery","CreateApiKeyCommand","ApiKey","intervalToPostgres","ApiKeyByIdQuery","DisableOneOffApiKeyCommand","DisableIdentityApiKeysCommand","VerifyErrorCode"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBO,MAAM,cAAc;AAAA,EAC1B,YACkB,eACA,oBACA,gBACA,2BAAqD,IAAIA,yBAAAA,yBAAA,GACzD,uBAChB;AALgB,SAAA,gBAAA;AACA,SAAA,qBAAA;AACA,SAAA,iBAAA;AACA,SAAA,2BAAA;AACA,SAAA,wBAAA;AAAA,EACf;AAAA,EAEH,MAAM,iBACL,WACA,eACA,OACA,aACA,eAC0B;AAC1B,UAAM,oBAAoB,KAAK,yBAAyB,OAAO,KAAK;AACpE,QAAI,sBAAsB,MAAM;AAE/B,aAAO,IAAIC,SAAAA,WAAW,iBAAiB;AAAA,IACxC;AAEA,UAAM,YAAY,MAAM,cAAc,aAAa,MAAM,IAAIC,YAAAA,mBAAmB,KAAK,CAAC;AACtF,QAAI,cAAc,MAAM;AACvB,aAAO,IAAIC,SAAAA,cAAc,aAA2B,uBAAuB;AAAA,IAC5E;AAEA,QAAI,UAAU,gBAAgB,MAAM;AACnC,aAAO,IAAIA,SAAAA;AAAAA,QACV;AAAA,QACA,2BAA2B,UAAU,YAAY,YAAA,CAAa;AAAA,MAAA;AAAA,IAEhE;AAIA,UAAM,MAAM,UAAU,UAAU,IAAA;AAChC,QAAI,UAAU,eAAe,QAAQ,UAAU,cAAc,KAAK;AACjE,aAAO,IAAIA,SAAAA,cAAc,YAA0B,sBAAsB,UAAU,WAAW,YAAA,CAAa,EAAE;AAAA,IAC9G;AAIA,QAAI,UAAU,mBAAmB,QAAQ,UAAU,kBAAkB,KAAK;AACzE,YAAM,UAAU,WAAW,QAAQ,IAAIC,qBAAAA,qBAAqB,UAAU,EAAE,CAAC;AACzE,aAAO,IAAID,SAAAA;AAAAA,QACV;AAAA,QACA,2CAA2C,UAAU,eAAe,YAAA,CAAa;AAAA,MAAA;AAAA,IAEnF;AAKA,QAAI,UAAU,iBAAiB,QAAQ,UAAU,iBAAiB,MAAM;AACvE,YAAM,SAASE,SAAAA,kBAAkB,UAAU,YAAY,IAAI;AAK3D,UAAI,IAAI,YAAY,UAAU,aAAa,QAAA,IAAY,SAASC,0CAAqB;AACpF,cAAM,UAAU,WAAW,QAAQ,IAAIF,qBAAAA,qBAAqB,UAAU,EAAE,CAAC;AACzE,YAAI;AACH,gBAAM,KAAK,eAAe,gBAAgB,WAAW;AAAA,YACpD,MAAM;AAAA,YACN,YAAY,UAAU;AAAA,YACtB,UAAU,UAAU;AAAA,YACpB,WAAW,aAAa;AAAA,YACxB,WAAW,aAAa;AAAA,YACxB,SAAS;AAAA,YACT,WAAW;AAAA,cACV,YAAY,UAAU,cAAc,YAAA,KAAiB;AAAA,cACrD,aAAa,OAAO,UAAU,YAAY;AAAA,YAAA;AAAA,UAC3C,CACA;AAAA,QACF,QAAQ;AAAA,QAER;AACA,eAAO,IAAID,SAAAA;AAAAA,UACV;AAAA,UACA,0BAA0B,UAAU,aAAa,YAAA,CAAa;AAAA,QAAA;AAAA,MAEhE;AAAA,IACD;AAEA,UAAM,gBAA+C,UAAU,wBAAwB,gBACpF;AAAA,MACD,IAAI,cAAc,MAAM,aAAa;AAAA,MACrC,WAAW,cAAc,aAAa,aAAa;AAAA,IAAA,IAElD;AAOH,QAAI,KAAK,uBAAuB;AAC/B,YAAM,UAAU,MAAM,KAAK,sBAAsB,WAAW,WAAW,eAAe,WAAW,aAAa;AAC9G,UAAI,YAAY,WAAW;AAC1B,eAAO,IAAIA,SAAAA,cAAc,YAA0B,yBAAyB;AAAA,MAC7E;AAAA,IACD;AAEA,iBAAa,YAAY;AACxB,YAAM,UAAU,WAAW;AAAA,QAC1B,IAAII,qBAAAA;AAAAA,UACH,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV;AAAA,UACA;AAAA,YACC,QAAQ,UAAU;AAAA,YAClB,eAAe,UAAU;AAAA,YACzB,YAAY,UAAU;AAAA,UAAA;AAAA,UAEvB,UAAU;AAAA,QAAA;AAAA,MACX;AAAA,IAEF,CAAC;AAED,WAAO,IAAIN,SAAAA;AAAAA,MACV,IAAI;AAAA,QACH,UAAU;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,IACX;AAAA,EAEF;AAAA,EAEA,MAAM,oBACL,WACA,YACA,YACA,aACA,oBACkB;AAClB,UAAM,EAAE,UAAU,MAAM,KAAK,0BAA0B,WAAW,YAAY,YAAY,aAAa,kBAAkB;AACzH,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,0BACL,WACA,YACA,YACA,aACA,oBACyC;AACzC,UAAM,SAAS,MAAM,UAAU,aAAa,MAAM,IAAIO,mBAAAA,mBAAmB,UAAU,SAAS,CAAC;AAK7F,UAAM,CAAC,WAAW,IAAI,MAAM,UAAU,aAAa,MAAM,IAAIC,cAAAA,cAAc,CAAC,UAAU,CAAC,CAAC;AACxF,UAAM,cAAc,aAAa,SAAS,CAAA;AAC1C,UAAM,SAAS,MAAM,KAAK,mBAAmB,mBAAmB,WAAW,YAAY,WAAW;AAElG,UAAM,2BAA2BJ,SAAAA,kBAAkB,OAAO,MAAM,sBAAsB,IAAI;AAG1F,UAAM,mBAAmB,OAAO,sBAAsB,QACnD,2BACC,cAAc;AAIlB,QAAI,mBAAmB,OAAO,MAAM,qBACjC,KAAK,IAAI,kBAAkBA,SAAAA,kBAAkB,OAAO,MAAM,kBAAkB,IAAI,EAAE,IAClF;AACH,QAAI,OAAO,iBAAiB;AAC3B,yBAAmB,KAAK,IAAI,kBAAkBA,SAAAA,kBAAkB,OAAO,eAAe,IAAI,EAAE;AAAA,IAC7F;AAIA,UAAM,WAAW,UAAU,UAAU,IAAA;AACrC,UAAM,eAAe,OAAO,kBACzB,IAAI,KAAK,SAAS,QAAA,IAAYA,SAAAA,kBAAkB,OAAO,eAAe,IAAI,GAAI,IAC9E;AAEH,UAAM,UAAU,IAAIK,wCAAoB;AAAA,MACvC,MAAMC,OAAAA,OAAO,KAAK;AAAA,MAClB;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,aAAa,OAAO,cAAcC,SAAAA,mBAAmB,OAAO,WAAW,IAAI;AAAA,MAC3E;AAAA,IAAA,CACA;AACD,UAAM,EAAE,IAAI,MAAA,IAAU,MAAM,UAAU,WAAW,QAAQ,OAAO;AAChE,WAAO,UAAU,MAAS;AAI1B,UAAM,gBAAgB,OAAO,gBAAgB,QACzC,OAAO,oBAAoB,QAC3B,OAAO,gBAAgB,QACvB,OAAO,sBAAsB;AACjC,QAAI,eAAe;AAClB,UAAI;AACH,cAAM,KAAK,eAAe,gBAAgB,WAAW;AAAA,UACpD,MAAM;AAAA,UACN;AAAA,UACA,WAAW,aAAa;AAAA,UACxB,WAAW,aAAa;AAAA,UACxB,SAAS;AAAA,UACT,WAAW;AAAA,YACV,iBAAiB,OAAO,kBAAkBA,SAAAA,mBAAmB,OAAO,eAAe,IAAI;AAAA,YACvF,aAAa,OAAO,cAAcA,SAAAA,mBAAmB,OAAO,WAAW,IAAI;AAAA,YAC3E,mBAAmB,OAAO;AAAA,YAC1B,aAAa,OAAO;AAAA,UAAA;AAAA,QACrB,CACA;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACD;AAEA,WAAO,EAAE,IAAI,MAAA;AAAA,EACd;AAAA,EAEA,MAAM,WAAW,WAA4B,UAA6C;AACzF,WAAO,MAAM,UAAU,aAAa;AAAA,MACnC,IAAIC,YAAAA,gBAAgB,QAAQ;AAAA,IAAA;AAAA,EAE9B;AAAA,EAEA,MAAM,oBAAoB,WAA4B,UAAiC;AACtF,UAAM,UAAU,WAAW,QAAQ,IAAIC,2BAAAA,2BAA2B,QAAQ,CAAC;AAAA,EAC5E;AAAA,EAEA,MAAM,cAAc,WAA4B,UAAoC;AACnF,WAAO,MAAM,UAAU,WAAW,QAAQ,IAAIV,qBAAAA,qBAAqB,QAAQ,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAM,uBAAuB,WAA4B,YAAmC;AAC3F,UAAM,UAAU,WAAW,QAAQ,IAAIW,8BAAAA,8BAA8B,UAAU,CAAC;AAAA,EACjF;AAAA,EAEA,MAAM,4BACL,WACA,aACA,OACA,WACA,oBACgC;AAChC,WAAO,MAAM,UAAU,YAAY,OAAM,OAAM;AAC9C,aAAO,MAAM,KAAK,cAAc,sBAAsB,IAAI,aAAa,OAAO,WAAW,kBAAkB;AAAA,IAC5G,CAAC;AAAA,EACF;AAAA,EAEA,MAAM,6BACL,WACA,WACA,aACA,aACA,WACA,oBACgC;AAChC,WAAO,MAAM,UAAU,YAAY,OAAM,OAAM;AAC9C,aAAO,MAAM,KAAK,cAAc,6BAA6B,IAAI,WAAW,aAAa,aAAa,WAAW,kBAAkB;AAAA,IACpI,CAAC;AAAA,EACF;AACD;AAIO,MAAM,aAAa;AAAA,EAGzB,YACiB,YACA,UACA,OACA,UACA,qBAA8B,OAC7C;AALe,SAAA,aAAA;AACA,SAAA,WAAA;AACA,SAAA,QAAA;AACA,SAAA,WAAA;AACA,SAAA,qBAAA;AAPjB,kBAAA,MAAS,SAAQ,IAAA;AAAA,EAQd;AACJ;AAEO,IAAK,oCAAAC,qBAAL;AACNA,mBAAA,WAAA,IAAY;AACZA,mBAAA,UAAA,IAAW;AACXA,mBAAA,SAAA,IAAU;AACVA,mBAAA,gBAAA,IAAiB;AACjBA,mBAAA,qBAAA,IAAsB;AALX,SAAAA;AAAA,GAAA,mBAAA,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"ApiKeyManager.cjs","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/ApiKeyManager.ts"],"sourcesContent":["import {\n\tApiKeyRequestInfo,\n\tCreateApiKeyCommand,\n\tDisableApiKeyCommand,\n\tDisableIdentityApiKeysCommand,\n\tDisableOneOffApiKeyCommand,\n\tPROLONG_THROTTLE_MS,\n\tProlongApiKeyCommand,\n} from '../../commands/index.js'\nimport { ApiKey } from '../../type/index.js'\n\nimport { Response, ResponseError, ResponseOk } from '../../utils/Response.js'\nimport { DatabaseContext, TokenHash } from '../../utils/index.js'\nimport { ApiKeyService, CreateApiKeyResponse } from './ApiKeyService.js'\nimport assert from 'node:assert'\nimport { Acl } from '@contember/schema'\nimport { ApiKeyByIdQuery, ApiKeyByTokenQuery, ApiKeyRow, ConfigurationQuery, IdentityQuery } from '../../queries/index.js'\nimport PostgresInterval from 'postgres-interval'\nimport { Config } from '../../type/Config.js'\nimport { intervalToPostgres, intervalToSeconds } from '../../utils/interval.js'\nimport { AuthPolicyResolver } from '../AuthPolicyResolver.js'\nimport { AuthLogService } from '../AuthLogService.js'\nimport { IdpSessionRevalidator } from '../idp/IdpSessionRevalidator.js'\nimport { UnpersistedApiKeyManager } from './UnpersistedApiKeyManager.js'\n\nexport class ApiKeyManager {\n\tconstructor(\n\t\tprivate readonly apiKeyService: ApiKeyService,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly authLogService: AuthLogService,\n\t\tprivate readonly unpersistedApiKeyManager: UnpersistedApiKeyManager = new UnpersistedApiKeyManager(),\n\t\tprivate readonly idpSessionRevalidator?: IdpSessionRevalidator,\n\t) {}\n\n\tasync verifyAndProlong(\n\t\tdbContext: DatabaseContext,\n\t\treadDbContext: DatabaseContext,\n\t\ttoken: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\tforwardedInfo?: ApiKeyRequestInfo,\n\t): Promise<VerifyResponse> {\n\t\tconst unpersistedResult = this.unpersistedApiKeyManager.verify(token)\n\t\tif (unpersistedResult !== null) {\n\t\t\t// Configured (unpersisted) root token: no DB row to prolong/track.\n\t\t\treturn new ResponseOk(unpersistedResult)\n\t\t}\n\n\t\tconst apiKeyRow = await readDbContext.queryHandler.fetch(new ApiKeyByTokenQuery(token))\n\t\tif (apiKeyRow === null) {\n\t\t\treturn new ResponseError(VerifyErrorCode.NOT_FOUND, 'API key was not found')\n\t\t}\n\n\t\tif (apiKeyRow.disabled_at !== null) {\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key was disabled at ${apiKeyRow.disabled_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// Single injectable clock for all session-policy time math (matches issuance,\n\t\t// which uses providers.now()), so the A19 idle/max logic is deterministically testable.\n\t\tconst now = dbContext.providers.now()\n\t\tif (apiKeyRow.expires_at !== null && apiKeyRow.expires_at <= now) {\n\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, `API key expired at ${apiKeyRow.expires_at.toISOString()}`)\n\t\t}\n\n\t\t// A19: absolute hard cap (defensive — the prolong clamp should already keep\n\t\t// expires_at <= max_expires_at, but enforce it here regardless).\n\t\tif (apiKeyRow.max_expires_at !== null && apiKeyRow.max_expires_at <= now) {\n\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key reached its maximum lifetime at ${apiKeyRow.max_expires_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// A19: idle timeout. Reject + disable when the session has been idle longer\n\t\t// than the policy's idle_timeout. A null idle_timeout (today's default) or a\n\t\t// never-used key (last_used_at null) are never idle-expired.\n\t\tif (apiKeyRow.idle_timeout !== null && apiKeyRow.last_used_at !== null) {\n\t\t\tconst idleMs = intervalToSeconds(apiKeyRow.idle_timeout) * 1000\n\t\t\t// last_used_at is written at most once per PROLONG_THROTTLE_MS (tracking is\n\t\t\t// throttled), so it can legitimately lag a continuously-active session by up\n\t\t\t// to that window. Add the throttle as slack so a small idle_timeout can't\n\t\t\t// idle-expire a session that is actually in use.\n\t\t\tif (now.getTime() - apiKeyRow.last_used_at.getTime() > idleMs + PROLONG_THROTTLE_MS) {\n\t\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\t\ttype: 'session_expired_idle',\n\t\t\t\t\t\tidentityId: apiKeyRow.identity_id,\n\t\t\t\t\t\tpersonId: apiKeyRow.person_id,\n\t\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\teventData: {\n\t\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at?.toISOString() ?? null,\n\t\t\t\t\t\t\tidleTimeout: String(apiKeyRow.idle_timeout),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort: auth path must stay resilient */\n\t\t\t\t}\n\t\t\t\treturn new ResponseError(\n\t\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t\t`API key was idle since ${apiKeyRow.last_used_at.toISOString()} and exceeded the idle timeout`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst effectiveInfo: ApiKeyRequestInfo | undefined = apiKeyRow.trust_forwarded_info && forwardedInfo\n\t\t\t? {\n\t\t\t\tip: forwardedInfo.ip ?? requestInfo?.ip,\n\t\t\t\tuserAgent: forwardedInfo.userAgent ?? requestInfo?.userAgent,\n\t\t\t}\n\t\t\t: requestInfo\n\n\t\t// Re-validate IdP-backed sessions against their identity provider (A24). In the\n\t\t// default `background` mode this returns immediately and revalidates after the\n\t\t// response (revocation lands on the next request); in `blocking` mode it can revoke\n\t\t// the current request. No-op for password sessions and IdPs without revalidation.\n\t\t// `effectiveInfo` is passed so the audit entries record the originating request's IP/UA.\n\t\tif (this.idpSessionRevalidator) {\n\t\t\tconst outcome = await this.idpSessionRevalidator.revalidate(dbContext, readDbContext, apiKeyRow, effectiveInfo)\n\t\t\tif (outcome === 'revoked') {\n\t\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, 'IdP session was revoked')\n\t\t\t}\n\t\t}\n\n\t\t// Detached (runs after the response), so a rejection here would be an unhandledRejection and can take the\n\t\t// worker down. Tracking is best-effort — the next request writes it again.\n\t\tsetImmediate(() => {\n\t\t\tdbContext.commandBus.execute(\n\t\t\t\tnew ProlongApiKeyCommand(\n\t\t\t\t\tapiKeyRow.id,\n\t\t\t\t\tapiKeyRow.type,\n\t\t\t\t\tapiKeyRow.expiration,\n\t\t\t\t\tapiKeyRow.expires_at,\n\t\t\t\t\teffectiveInfo,\n\t\t\t\t\t{\n\t\t\t\t\t\tlastIp: apiKeyRow.last_ip,\n\t\t\t\t\t\tlastUserAgent: apiKeyRow.last_user_agent,\n\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at,\n\t\t\t\t\t},\n\t\t\t\t\tapiKeyRow.max_expires_at,\n\t\t\t\t),\n\t\t\t).catch(() => {})\n\t\t})\n\n\t\treturn new ResponseOk(\n\t\t\tnew VerifyResult(\n\t\t\t\tapiKeyRow.identity_id,\n\t\t\t\tapiKeyRow.id,\n\t\t\t\tapiKeyRow.roles,\n\t\t\t\tapiKeyRow.person_id,\n\t\t\t\tapiKeyRow.trust_forwarded_info,\n\t\t\t),\n\t\t)\n\t}\n\n\tasync createSessionApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<string> {\n\t\tconst { token } = await this.createSessionApiKeyWithId(dbContext, identityId, expiration, requestInfo, trustForwardedInfo)\n\t\treturn token\n\t}\n\n\t/**\n\t * Like {@link createSessionApiKey} but also returns the api_key id — needed when the\n\t * caller has to bind another row to the session (e.g. an `idp_session` for IdP\n\t * re-validation).\n\t */\n\tasync createSessionApiKeyWithId(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<{ id: string; token: string }> {\n\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\n\t\t// A19: snapshot the effective session policy onto the api_key at sign-in.\n\t\t// With no matching policy, every field is null/baseline and the result is\n\t\t// byte-for-byte today's behavior.\n\t\tconst [identityRow] = await dbContext.queryHandler.fetch(new IdentityQuery([identityId]))\n\t\tconst globalRoles = identityRow?.roles ?? []\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, identityId, globalRoles)\n\n\t\tconst defaultExpirationMinutes = intervalToSeconds(config.login.defaultTokenExpiration) / 60\n\t\t// remember_me_allowed === false: ignore a client-supplied longer lifetime and\n\t\t// force the default. (true/null leave the requested expiration untouched.)\n\t\tconst requestedMinutes = policy.rememberMeAllowed === false\n\t\t\t? defaultExpirationMinutes\n\t\t\t: (expiration ?? defaultExpirationMinutes)\n\n\t\t// Cap by maxTokenExpiration (existing behavior) AND by the policy's\n\t\t// tokenExpiration (A19) — strictest wins.\n\t\tlet expirationCapped = config.login.maxTokenExpiration\n\t\t\t? Math.min(requestedMinutes, intervalToSeconds(config.login.maxTokenExpiration) / 60)\n\t\t\t: requestedMinutes\n\t\tif (policy.tokenExpiration) {\n\t\t\texpirationCapped = Math.min(expirationCapped, intervalToSeconds(policy.tokenExpiration) / 60)\n\t\t}\n\n\t\t// Absolute hard cap: issued_at + policy.tokenExpiration. NULL = uncapped\n\t\t// sliding window (today's behavior).\n\t\tconst issuedAt = dbContext.providers.now()\n\t\tconst maxExpiresAt = policy.tokenExpiration\n\t\t\t? new Date(issuedAt.getTime() + intervalToSeconds(policy.tokenExpiration) * 1000)\n\t\t\t: null\n\n\t\tconst command = new CreateApiKeyCommand({\n\t\t\ttype: ApiKey.Type.SESSION,\n\t\t\tidentityId,\n\t\t\texpiration: expirationCapped,\n\t\t\trequestInfo,\n\t\t\ttrustForwardedInfo,\n\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\tmaxExpiresAt,\n\t\t})\n\t\tconst { id, token } = await dbContext.commandBus.execute(command)\n\t\tassert(token !== undefined)\n\n\t\t// A19: audit only when a policy is actually in effect. With zero auth_policy\n\t\t// rows the resolved policy is inert and no extra insert happens.\n\t\tconst policyApplied = policy.mfaRequired === true\n\t\t\t|| policy.tokenExpiration !== null\n\t\t\t|| policy.idleTimeout !== null\n\t\t\t|| policy.rememberMeAllowed !== null\n\t\tif (policyApplied) {\n\t\t\ttry {\n\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\ttype: 'session_policy_applied',\n\t\t\t\t\tidentityId,\n\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\teventData: {\n\t\t\t\t\t\ttokenExpiration: policy.tokenExpiration ? intervalToPostgres(policy.tokenExpiration) : null,\n\t\t\t\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\t\t\t\trememberMeAllowed: policy.rememberMeAllowed,\n\t\t\t\t\t\tmfaRequired: policy.mfaRequired,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t/* best-effort: sign-in must not break on an audit failure */\n\t\t\t}\n\t\t}\n\n\t\treturn { id, token }\n\t}\n\n\tasync findApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<ApiKeyRow | null> {\n\t\treturn await dbContext.queryHandler.fetch(\n\t\t\tnew ApiKeyByIdQuery(apiKeyId),\n\t\t)\n\t}\n\n\tasync disableOneOffApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableOneOffApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<boolean> {\n\t\treturn await dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableIdentityApiKeys(dbContext: DatabaseContext, identityId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableIdentityApiKeysCommand(identityId))\n\t}\n\n\tasync createGlobalPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tdescription: string,\n\t\troles: readonly string[],\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createPermanentApiKey(db, description, roles, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n\n\tasync createProjectPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tprojectId: string,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tdescription: string,\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createProjectPermanentApiKey(db, projectId, memberships, description, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n}\n\nexport type VerifyResponse = Response<VerifyResult, VerifyErrorCode>\n\nexport class VerifyResult {\n\treadonly valid = true\n\n\tconstructor(\n\t\tpublic readonly identityId: string,\n\t\tpublic readonly apiKeyId: string,\n\t\tpublic readonly roles: string[],\n\t\tpublic readonly personId: string | null,\n\t\tpublic readonly trustForwardedInfo: boolean = false,\n\t) {}\n}\n\nexport enum VerifyErrorCode {\n\tNOT_FOUND = 'not_found',\n\tDISABLED = 'disabled',\n\tEXPIRED = 'expired',\n\tNO_AUTH_HEADER = 'no_auth_header',\n\tINVALID_AUTH_HEADER = 'invalid_auth_header',\n}\n"],"names":["UnpersistedApiKeyManager","ResponseOk","ApiKeyByTokenQuery","ResponseError","DisableApiKeyCommand","intervalToSeconds","PROLONG_THROTTLE_MS","ProlongApiKeyCommand","ConfigurationQuery","IdentityQuery","CreateApiKeyCommand","ApiKey","intervalToPostgres","ApiKeyByIdQuery","DisableOneOffApiKeyCommand","DisableIdentityApiKeysCommand","VerifyErrorCode"],"mappings":"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"}
@@ -90,8 +90,8 @@ class ApiKeyManager {
90
90
  return new ResponseError("disabled", "IdP session was revoked");
91
91
  }
92
92
  }
93
- setImmediate(async () => {
94
- await dbContext.commandBus.execute(
93
+ setImmediate(() => {
94
+ dbContext.commandBus.execute(
95
95
  new ProlongApiKeyCommand(
96
96
  apiKeyRow.id,
97
97
  apiKeyRow.type,
@@ -105,7 +105,8 @@ class ApiKeyManager {
105
105
  },
106
106
  apiKeyRow.max_expires_at
107
107
  )
108
- );
108
+ ).catch(() => {
109
+ });
109
110
  });
110
111
  return new ResponseOk(
111
112
  new VerifyResult(
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeyManager.js","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/ApiKeyManager.ts"],"sourcesContent":["import {\n\tApiKeyRequestInfo,\n\tCreateApiKeyCommand,\n\tDisableApiKeyCommand,\n\tDisableIdentityApiKeysCommand,\n\tDisableOneOffApiKeyCommand,\n\tPROLONG_THROTTLE_MS,\n\tProlongApiKeyCommand,\n} from '../../commands/index.js'\nimport { ApiKey } from '../../type/index.js'\n\nimport { Response, ResponseError, ResponseOk } from '../../utils/Response.js'\nimport { DatabaseContext, TokenHash } from '../../utils/index.js'\nimport { ApiKeyService, CreateApiKeyResponse } from './ApiKeyService.js'\nimport assert from 'node:assert'\nimport { Acl } from '@contember/schema'\nimport { ApiKeyByIdQuery, ApiKeyByTokenQuery, ApiKeyRow, ConfigurationQuery, IdentityQuery } from '../../queries/index.js'\nimport PostgresInterval from 'postgres-interval'\nimport { Config } from '../../type/Config.js'\nimport { intervalToPostgres, intervalToSeconds } from '../../utils/interval.js'\nimport { AuthPolicyResolver } from '../AuthPolicyResolver.js'\nimport { AuthLogService } from '../AuthLogService.js'\nimport { IdpSessionRevalidator } from '../idp/IdpSessionRevalidator.js'\nimport { UnpersistedApiKeyManager } from './UnpersistedApiKeyManager.js'\n\nexport class ApiKeyManager {\n\tconstructor(\n\t\tprivate readonly apiKeyService: ApiKeyService,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly authLogService: AuthLogService,\n\t\tprivate readonly unpersistedApiKeyManager: UnpersistedApiKeyManager = new UnpersistedApiKeyManager(),\n\t\tprivate readonly idpSessionRevalidator?: IdpSessionRevalidator,\n\t) {}\n\n\tasync verifyAndProlong(\n\t\tdbContext: DatabaseContext,\n\t\treadDbContext: DatabaseContext,\n\t\ttoken: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\tforwardedInfo?: ApiKeyRequestInfo,\n\t): Promise<VerifyResponse> {\n\t\tconst unpersistedResult = this.unpersistedApiKeyManager.verify(token)\n\t\tif (unpersistedResult !== null) {\n\t\t\t// Configured (unpersisted) root token: no DB row to prolong/track.\n\t\t\treturn new ResponseOk(unpersistedResult)\n\t\t}\n\n\t\tconst apiKeyRow = await readDbContext.queryHandler.fetch(new ApiKeyByTokenQuery(token))\n\t\tif (apiKeyRow === null) {\n\t\t\treturn new ResponseError(VerifyErrorCode.NOT_FOUND, 'API key was not found')\n\t\t}\n\n\t\tif (apiKeyRow.disabled_at !== null) {\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key was disabled at ${apiKeyRow.disabled_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// Single injectable clock for all session-policy time math (matches issuance,\n\t\t// which uses providers.now()), so the A19 idle/max logic is deterministically testable.\n\t\tconst now = dbContext.providers.now()\n\t\tif (apiKeyRow.expires_at !== null && apiKeyRow.expires_at <= now) {\n\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, `API key expired at ${apiKeyRow.expires_at.toISOString()}`)\n\t\t}\n\n\t\t// A19: absolute hard cap (defensive — the prolong clamp should already keep\n\t\t// expires_at <= max_expires_at, but enforce it here regardless).\n\t\tif (apiKeyRow.max_expires_at !== null && apiKeyRow.max_expires_at <= now) {\n\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key reached its maximum lifetime at ${apiKeyRow.max_expires_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// A19: idle timeout. Reject + disable when the session has been idle longer\n\t\t// than the policy's idle_timeout. A null idle_timeout (today's default) or a\n\t\t// never-used key (last_used_at null) are never idle-expired.\n\t\tif (apiKeyRow.idle_timeout !== null && apiKeyRow.last_used_at !== null) {\n\t\t\tconst idleMs = intervalToSeconds(apiKeyRow.idle_timeout) * 1000\n\t\t\t// last_used_at is written at most once per PROLONG_THROTTLE_MS (tracking is\n\t\t\t// throttled), so it can legitimately lag a continuously-active session by up\n\t\t\t// to that window. Add the throttle as slack so a small idle_timeout can't\n\t\t\t// idle-expire a session that is actually in use.\n\t\t\tif (now.getTime() - apiKeyRow.last_used_at.getTime() > idleMs + PROLONG_THROTTLE_MS) {\n\t\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\t\ttype: 'session_expired_idle',\n\t\t\t\t\t\tidentityId: apiKeyRow.identity_id,\n\t\t\t\t\t\tpersonId: apiKeyRow.person_id,\n\t\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\teventData: {\n\t\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at?.toISOString() ?? null,\n\t\t\t\t\t\t\tidleTimeout: String(apiKeyRow.idle_timeout),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort: auth path must stay resilient */\n\t\t\t\t}\n\t\t\t\treturn new ResponseError(\n\t\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t\t`API key was idle since ${apiKeyRow.last_used_at.toISOString()} and exceeded the idle timeout`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst effectiveInfo: ApiKeyRequestInfo | undefined = apiKeyRow.trust_forwarded_info && forwardedInfo\n\t\t\t? {\n\t\t\t\tip: forwardedInfo.ip ?? requestInfo?.ip,\n\t\t\t\tuserAgent: forwardedInfo.userAgent ?? requestInfo?.userAgent,\n\t\t\t}\n\t\t\t: requestInfo\n\n\t\t// Re-validate IdP-backed sessions against their identity provider (A24). In the\n\t\t// default `background` mode this returns immediately and revalidates after the\n\t\t// response (revocation lands on the next request); in `blocking` mode it can revoke\n\t\t// the current request. No-op for password sessions and IdPs without revalidation.\n\t\t// `effectiveInfo` is passed so the audit entries record the originating request's IP/UA.\n\t\tif (this.idpSessionRevalidator) {\n\t\t\tconst outcome = await this.idpSessionRevalidator.revalidate(dbContext, readDbContext, apiKeyRow, effectiveInfo)\n\t\t\tif (outcome === 'revoked') {\n\t\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, 'IdP session was revoked')\n\t\t\t}\n\t\t}\n\n\t\tsetImmediate(async () => {\n\t\t\tawait dbContext.commandBus.execute(\n\t\t\t\tnew ProlongApiKeyCommand(\n\t\t\t\t\tapiKeyRow.id,\n\t\t\t\t\tapiKeyRow.type,\n\t\t\t\t\tapiKeyRow.expiration,\n\t\t\t\t\tapiKeyRow.expires_at,\n\t\t\t\t\teffectiveInfo,\n\t\t\t\t\t{\n\t\t\t\t\t\tlastIp: apiKeyRow.last_ip,\n\t\t\t\t\t\tlastUserAgent: apiKeyRow.last_user_agent,\n\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at,\n\t\t\t\t\t},\n\t\t\t\t\tapiKeyRow.max_expires_at,\n\t\t\t\t),\n\t\t\t)\n\t\t})\n\n\t\treturn new ResponseOk(\n\t\t\tnew VerifyResult(\n\t\t\t\tapiKeyRow.identity_id,\n\t\t\t\tapiKeyRow.id,\n\t\t\t\tapiKeyRow.roles,\n\t\t\t\tapiKeyRow.person_id,\n\t\t\t\tapiKeyRow.trust_forwarded_info,\n\t\t\t),\n\t\t)\n\t}\n\n\tasync createSessionApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<string> {\n\t\tconst { token } = await this.createSessionApiKeyWithId(dbContext, identityId, expiration, requestInfo, trustForwardedInfo)\n\t\treturn token\n\t}\n\n\t/**\n\t * Like {@link createSessionApiKey} but also returns the api_key id — needed when the\n\t * caller has to bind another row to the session (e.g. an `idp_session` for IdP\n\t * re-validation).\n\t */\n\tasync createSessionApiKeyWithId(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<{ id: string; token: string }> {\n\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\n\t\t// A19: snapshot the effective session policy onto the api_key at sign-in.\n\t\t// With no matching policy, every field is null/baseline and the result is\n\t\t// byte-for-byte today's behavior.\n\t\tconst [identityRow] = await dbContext.queryHandler.fetch(new IdentityQuery([identityId]))\n\t\tconst globalRoles = identityRow?.roles ?? []\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, identityId, globalRoles)\n\n\t\tconst defaultExpirationMinutes = intervalToSeconds(config.login.defaultTokenExpiration) / 60\n\t\t// remember_me_allowed === false: ignore a client-supplied longer lifetime and\n\t\t// force the default. (true/null leave the requested expiration untouched.)\n\t\tconst requestedMinutes = policy.rememberMeAllowed === false\n\t\t\t? defaultExpirationMinutes\n\t\t\t: (expiration ?? defaultExpirationMinutes)\n\n\t\t// Cap by maxTokenExpiration (existing behavior) AND by the policy's\n\t\t// tokenExpiration (A19) — strictest wins.\n\t\tlet expirationCapped = config.login.maxTokenExpiration\n\t\t\t? Math.min(requestedMinutes, intervalToSeconds(config.login.maxTokenExpiration) / 60)\n\t\t\t: requestedMinutes\n\t\tif (policy.tokenExpiration) {\n\t\t\texpirationCapped = Math.min(expirationCapped, intervalToSeconds(policy.tokenExpiration) / 60)\n\t\t}\n\n\t\t// Absolute hard cap: issued_at + policy.tokenExpiration. NULL = uncapped\n\t\t// sliding window (today's behavior).\n\t\tconst issuedAt = dbContext.providers.now()\n\t\tconst maxExpiresAt = policy.tokenExpiration\n\t\t\t? new Date(issuedAt.getTime() + intervalToSeconds(policy.tokenExpiration) * 1000)\n\t\t\t: null\n\n\t\tconst command = new CreateApiKeyCommand({\n\t\t\ttype: ApiKey.Type.SESSION,\n\t\t\tidentityId,\n\t\t\texpiration: expirationCapped,\n\t\t\trequestInfo,\n\t\t\ttrustForwardedInfo,\n\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\tmaxExpiresAt,\n\t\t})\n\t\tconst { id, token } = await dbContext.commandBus.execute(command)\n\t\tassert(token !== undefined)\n\n\t\t// A19: audit only when a policy is actually in effect. With zero auth_policy\n\t\t// rows the resolved policy is inert and no extra insert happens.\n\t\tconst policyApplied = policy.mfaRequired === true\n\t\t\t|| policy.tokenExpiration !== null\n\t\t\t|| policy.idleTimeout !== null\n\t\t\t|| policy.rememberMeAllowed !== null\n\t\tif (policyApplied) {\n\t\t\ttry {\n\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\ttype: 'session_policy_applied',\n\t\t\t\t\tidentityId,\n\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\teventData: {\n\t\t\t\t\t\ttokenExpiration: policy.tokenExpiration ? intervalToPostgres(policy.tokenExpiration) : null,\n\t\t\t\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\t\t\t\trememberMeAllowed: policy.rememberMeAllowed,\n\t\t\t\t\t\tmfaRequired: policy.mfaRequired,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t/* best-effort: sign-in must not break on an audit failure */\n\t\t\t}\n\t\t}\n\n\t\treturn { id, token }\n\t}\n\n\tasync findApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<ApiKeyRow | null> {\n\t\treturn await dbContext.queryHandler.fetch(\n\t\t\tnew ApiKeyByIdQuery(apiKeyId),\n\t\t)\n\t}\n\n\tasync disableOneOffApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableOneOffApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<boolean> {\n\t\treturn await dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableIdentityApiKeys(dbContext: DatabaseContext, identityId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableIdentityApiKeysCommand(identityId))\n\t}\n\n\tasync createGlobalPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tdescription: string,\n\t\troles: readonly string[],\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createPermanentApiKey(db, description, roles, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n\n\tasync createProjectPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tprojectId: string,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tdescription: string,\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createProjectPermanentApiKey(db, projectId, memberships, description, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n}\n\nexport type VerifyResponse = Response<VerifyResult, VerifyErrorCode>\n\nexport class VerifyResult {\n\treadonly valid = true\n\n\tconstructor(\n\t\tpublic readonly identityId: string,\n\t\tpublic readonly apiKeyId: string,\n\t\tpublic readonly roles: string[],\n\t\tpublic readonly personId: string | null,\n\t\tpublic readonly trustForwardedInfo: boolean = false,\n\t) {}\n}\n\nexport enum VerifyErrorCode {\n\tNOT_FOUND = 'not_found',\n\tDISABLED = 'disabled',\n\tEXPIRED = 'expired',\n\tNO_AUTH_HEADER = 'no_auth_header',\n\tINVALID_AUTH_HEADER = 'invalid_auth_header',\n}\n"],"names":["VerifyErrorCode"],"mappings":"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"}
1
+ {"version":3,"file":"ApiKeyManager.js","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/ApiKeyManager.ts"],"sourcesContent":["import {\n\tApiKeyRequestInfo,\n\tCreateApiKeyCommand,\n\tDisableApiKeyCommand,\n\tDisableIdentityApiKeysCommand,\n\tDisableOneOffApiKeyCommand,\n\tPROLONG_THROTTLE_MS,\n\tProlongApiKeyCommand,\n} from '../../commands/index.js'\nimport { ApiKey } from '../../type/index.js'\n\nimport { Response, ResponseError, ResponseOk } from '../../utils/Response.js'\nimport { DatabaseContext, TokenHash } from '../../utils/index.js'\nimport { ApiKeyService, CreateApiKeyResponse } from './ApiKeyService.js'\nimport assert from 'node:assert'\nimport { Acl } from '@contember/schema'\nimport { ApiKeyByIdQuery, ApiKeyByTokenQuery, ApiKeyRow, ConfigurationQuery, IdentityQuery } from '../../queries/index.js'\nimport PostgresInterval from 'postgres-interval'\nimport { Config } from '../../type/Config.js'\nimport { intervalToPostgres, intervalToSeconds } from '../../utils/interval.js'\nimport { AuthPolicyResolver } from '../AuthPolicyResolver.js'\nimport { AuthLogService } from '../AuthLogService.js'\nimport { IdpSessionRevalidator } from '../idp/IdpSessionRevalidator.js'\nimport { UnpersistedApiKeyManager } from './UnpersistedApiKeyManager.js'\n\nexport class ApiKeyManager {\n\tconstructor(\n\t\tprivate readonly apiKeyService: ApiKeyService,\n\t\tprivate readonly authPolicyResolver: AuthPolicyResolver,\n\t\tprivate readonly authLogService: AuthLogService,\n\t\tprivate readonly unpersistedApiKeyManager: UnpersistedApiKeyManager = new UnpersistedApiKeyManager(),\n\t\tprivate readonly idpSessionRevalidator?: IdpSessionRevalidator,\n\t) {}\n\n\tasync verifyAndProlong(\n\t\tdbContext: DatabaseContext,\n\t\treadDbContext: DatabaseContext,\n\t\ttoken: string,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\tforwardedInfo?: ApiKeyRequestInfo,\n\t): Promise<VerifyResponse> {\n\t\tconst unpersistedResult = this.unpersistedApiKeyManager.verify(token)\n\t\tif (unpersistedResult !== null) {\n\t\t\t// Configured (unpersisted) root token: no DB row to prolong/track.\n\t\t\treturn new ResponseOk(unpersistedResult)\n\t\t}\n\n\t\tconst apiKeyRow = await readDbContext.queryHandler.fetch(new ApiKeyByTokenQuery(token))\n\t\tif (apiKeyRow === null) {\n\t\t\treturn new ResponseError(VerifyErrorCode.NOT_FOUND, 'API key was not found')\n\t\t}\n\n\t\tif (apiKeyRow.disabled_at !== null) {\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key was disabled at ${apiKeyRow.disabled_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// Single injectable clock for all session-policy time math (matches issuance,\n\t\t// which uses providers.now()), so the A19 idle/max logic is deterministically testable.\n\t\tconst now = dbContext.providers.now()\n\t\tif (apiKeyRow.expires_at !== null && apiKeyRow.expires_at <= now) {\n\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, `API key expired at ${apiKeyRow.expires_at.toISOString()}`)\n\t\t}\n\n\t\t// A19: absolute hard cap (defensive — the prolong clamp should already keep\n\t\t// expires_at <= max_expires_at, but enforce it here regardless).\n\t\tif (apiKeyRow.max_expires_at !== null && apiKeyRow.max_expires_at <= now) {\n\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\treturn new ResponseError(\n\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t`API key reached its maximum lifetime at ${apiKeyRow.max_expires_at.toISOString()}`,\n\t\t\t)\n\t\t}\n\n\t\t// A19: idle timeout. Reject + disable when the session has been idle longer\n\t\t// than the policy's idle_timeout. A null idle_timeout (today's default) or a\n\t\t// never-used key (last_used_at null) are never idle-expired.\n\t\tif (apiKeyRow.idle_timeout !== null && apiKeyRow.last_used_at !== null) {\n\t\t\tconst idleMs = intervalToSeconds(apiKeyRow.idle_timeout) * 1000\n\t\t\t// last_used_at is written at most once per PROLONG_THROTTLE_MS (tracking is\n\t\t\t// throttled), so it can legitimately lag a continuously-active session by up\n\t\t\t// to that window. Add the throttle as slack so a small idle_timeout can't\n\t\t\t// idle-expire a session that is actually in use.\n\t\t\tif (now.getTime() - apiKeyRow.last_used_at.getTime() > idleMs + PROLONG_THROTTLE_MS) {\n\t\t\t\tawait dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyRow.id))\n\t\t\t\ttry {\n\t\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\t\ttype: 'session_expired_idle',\n\t\t\t\t\t\tidentityId: apiKeyRow.identity_id,\n\t\t\t\t\t\tpersonId: apiKeyRow.person_id,\n\t\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\teventData: {\n\t\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at?.toISOString() ?? null,\n\t\t\t\t\t\t\tidleTimeout: String(apiKeyRow.idle_timeout),\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\t/* best-effort: auth path must stay resilient */\n\t\t\t\t}\n\t\t\t\treturn new ResponseError(\n\t\t\t\t\tVerifyErrorCode.DISABLED,\n\t\t\t\t\t`API key was idle since ${apiKeyRow.last_used_at.toISOString()} and exceeded the idle timeout`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tconst effectiveInfo: ApiKeyRequestInfo | undefined = apiKeyRow.trust_forwarded_info && forwardedInfo\n\t\t\t? {\n\t\t\t\tip: forwardedInfo.ip ?? requestInfo?.ip,\n\t\t\t\tuserAgent: forwardedInfo.userAgent ?? requestInfo?.userAgent,\n\t\t\t}\n\t\t\t: requestInfo\n\n\t\t// Re-validate IdP-backed sessions against their identity provider (A24). In the\n\t\t// default `background` mode this returns immediately and revalidates after the\n\t\t// response (revocation lands on the next request); in `blocking` mode it can revoke\n\t\t// the current request. No-op for password sessions and IdPs without revalidation.\n\t\t// `effectiveInfo` is passed so the audit entries record the originating request's IP/UA.\n\t\tif (this.idpSessionRevalidator) {\n\t\t\tconst outcome = await this.idpSessionRevalidator.revalidate(dbContext, readDbContext, apiKeyRow, effectiveInfo)\n\t\t\tif (outcome === 'revoked') {\n\t\t\t\treturn new ResponseError(VerifyErrorCode.DISABLED, 'IdP session was revoked')\n\t\t\t}\n\t\t}\n\n\t\t// Detached (runs after the response), so a rejection here would be an unhandledRejection and can take the\n\t\t// worker down. Tracking is best-effort — the next request writes it again.\n\t\tsetImmediate(() => {\n\t\t\tdbContext.commandBus.execute(\n\t\t\t\tnew ProlongApiKeyCommand(\n\t\t\t\t\tapiKeyRow.id,\n\t\t\t\t\tapiKeyRow.type,\n\t\t\t\t\tapiKeyRow.expiration,\n\t\t\t\t\tapiKeyRow.expires_at,\n\t\t\t\t\teffectiveInfo,\n\t\t\t\t\t{\n\t\t\t\t\t\tlastIp: apiKeyRow.last_ip,\n\t\t\t\t\t\tlastUserAgent: apiKeyRow.last_user_agent,\n\t\t\t\t\t\tlastUsedAt: apiKeyRow.last_used_at,\n\t\t\t\t\t},\n\t\t\t\t\tapiKeyRow.max_expires_at,\n\t\t\t\t),\n\t\t\t).catch(() => {})\n\t\t})\n\n\t\treturn new ResponseOk(\n\t\t\tnew VerifyResult(\n\t\t\t\tapiKeyRow.identity_id,\n\t\t\t\tapiKeyRow.id,\n\t\t\t\tapiKeyRow.roles,\n\t\t\t\tapiKeyRow.person_id,\n\t\t\t\tapiKeyRow.trust_forwarded_info,\n\t\t\t),\n\t\t)\n\t}\n\n\tasync createSessionApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<string> {\n\t\tconst { token } = await this.createSessionApiKeyWithId(dbContext, identityId, expiration, requestInfo, trustForwardedInfo)\n\t\treturn token\n\t}\n\n\t/**\n\t * Like {@link createSessionApiKey} but also returns the api_key id — needed when the\n\t * caller has to bind another row to the session (e.g. an `idp_session` for IdP\n\t * re-validation).\n\t */\n\tasync createSessionApiKeyWithId(\n\t\tdbContext: DatabaseContext,\n\t\tidentityId: string,\n\t\texpiration?: number,\n\t\trequestInfo?: ApiKeyRequestInfo,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<{ id: string; token: string }> {\n\t\tconst config = await dbContext.queryHandler.fetch(new ConfigurationQuery(dbContext.providers))\n\n\t\t// A19: snapshot the effective session policy onto the api_key at sign-in.\n\t\t// With no matching policy, every field is null/baseline and the result is\n\t\t// byte-for-byte today's behavior.\n\t\tconst [identityRow] = await dbContext.queryHandler.fetch(new IdentityQuery([identityId]))\n\t\tconst globalRoles = identityRow?.roles ?? []\n\t\tconst policy = await this.authPolicyResolver.resolveForIdentity(dbContext, identityId, globalRoles)\n\n\t\tconst defaultExpirationMinutes = intervalToSeconds(config.login.defaultTokenExpiration) / 60\n\t\t// remember_me_allowed === false: ignore a client-supplied longer lifetime and\n\t\t// force the default. (true/null leave the requested expiration untouched.)\n\t\tconst requestedMinutes = policy.rememberMeAllowed === false\n\t\t\t? defaultExpirationMinutes\n\t\t\t: (expiration ?? defaultExpirationMinutes)\n\n\t\t// Cap by maxTokenExpiration (existing behavior) AND by the policy's\n\t\t// tokenExpiration (A19) — strictest wins.\n\t\tlet expirationCapped = config.login.maxTokenExpiration\n\t\t\t? Math.min(requestedMinutes, intervalToSeconds(config.login.maxTokenExpiration) / 60)\n\t\t\t: requestedMinutes\n\t\tif (policy.tokenExpiration) {\n\t\t\texpirationCapped = Math.min(expirationCapped, intervalToSeconds(policy.tokenExpiration) / 60)\n\t\t}\n\n\t\t// Absolute hard cap: issued_at + policy.tokenExpiration. NULL = uncapped\n\t\t// sliding window (today's behavior).\n\t\tconst issuedAt = dbContext.providers.now()\n\t\tconst maxExpiresAt = policy.tokenExpiration\n\t\t\t? new Date(issuedAt.getTime() + intervalToSeconds(policy.tokenExpiration) * 1000)\n\t\t\t: null\n\n\t\tconst command = new CreateApiKeyCommand({\n\t\t\ttype: ApiKey.Type.SESSION,\n\t\t\tidentityId,\n\t\t\texpiration: expirationCapped,\n\t\t\trequestInfo,\n\t\t\ttrustForwardedInfo,\n\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\tmaxExpiresAt,\n\t\t})\n\t\tconst { id, token } = await dbContext.commandBus.execute(command)\n\t\tassert(token !== undefined)\n\n\t\t// A19: audit only when a policy is actually in effect. With zero auth_policy\n\t\t// rows the resolved policy is inert and no extra insert happens.\n\t\tconst policyApplied = policy.mfaRequired === true\n\t\t\t|| policy.tokenExpiration !== null\n\t\t\t|| policy.idleTimeout !== null\n\t\t\t|| policy.rememberMeAllowed !== null\n\t\tif (policyApplied) {\n\t\t\ttry {\n\t\t\t\tawait this.authLogService.logSessionEvent(dbContext, {\n\t\t\t\t\ttype: 'session_policy_applied',\n\t\t\t\t\tidentityId,\n\t\t\t\t\tipAddress: requestInfo?.ip,\n\t\t\t\t\tuserAgent: requestInfo?.userAgent,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\teventData: {\n\t\t\t\t\t\ttokenExpiration: policy.tokenExpiration ? intervalToPostgres(policy.tokenExpiration) : null,\n\t\t\t\t\t\tidleTimeout: policy.idleTimeout ? intervalToPostgres(policy.idleTimeout) : null,\n\t\t\t\t\t\trememberMeAllowed: policy.rememberMeAllowed,\n\t\t\t\t\t\tmfaRequired: policy.mfaRequired,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t/* best-effort: sign-in must not break on an audit failure */\n\t\t\t}\n\t\t}\n\n\t\treturn { id, token }\n\t}\n\n\tasync findApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<ApiKeyRow | null> {\n\t\treturn await dbContext.queryHandler.fetch(\n\t\t\tnew ApiKeyByIdQuery(apiKeyId),\n\t\t)\n\t}\n\n\tasync disableOneOffApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableOneOffApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableApiKey(dbContext: DatabaseContext, apiKeyId: string): Promise<boolean> {\n\t\treturn await dbContext.commandBus.execute(new DisableApiKeyCommand(apiKeyId))\n\t}\n\n\tasync disableIdentityApiKeys(dbContext: DatabaseContext, identityId: string): Promise<void> {\n\t\tawait dbContext.commandBus.execute(new DisableIdentityApiKeysCommand(identityId))\n\t}\n\n\tasync createGlobalPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tdescription: string,\n\t\troles: readonly string[],\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createPermanentApiKey(db, description, roles, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n\n\tasync createProjectPermanentApiKey(\n\t\tdbContext: DatabaseContext,\n\t\tprojectId: string,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tdescription: string,\n\t\ttokenHash?: TokenHash,\n\t\ttrustForwardedInfo?: boolean,\n\t): Promise<CreateApiKeyResponse> {\n\t\treturn await dbContext.transaction(async db => {\n\t\t\treturn await this.apiKeyService.createProjectPermanentApiKey(db, projectId, memberships, description, tokenHash, trustForwardedInfo)\n\t\t})\n\t}\n}\n\nexport type VerifyResponse = Response<VerifyResult, VerifyErrorCode>\n\nexport class VerifyResult {\n\treadonly valid = true\n\n\tconstructor(\n\t\tpublic readonly identityId: string,\n\t\tpublic readonly apiKeyId: string,\n\t\tpublic readonly roles: string[],\n\t\tpublic readonly personId: string | null,\n\t\tpublic readonly trustForwardedInfo: boolean = false,\n\t) {}\n}\n\nexport enum VerifyErrorCode {\n\tNOT_FOUND = 'not_found',\n\tDISABLED = 'disabled',\n\tEXPIRED = 'expired',\n\tNO_AUTH_HEADER = 'no_auth_header',\n\tINVALID_AUTH_HEADER = 'invalid_auth_header',\n}\n"],"names":["VerifyErrorCode"],"mappings":"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"}
@@ -14,6 +14,10 @@ var __defProp = Object.defineProperty;
14
14
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15
15
  var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
16
16
  const UNPERSISTED_ROOT_IDENTITY_ID = "f3b2c4d6-0000-4000-8000-000000000001";
17
+ const UNPERSISTED_LOGIN_IDENTITY_ID = "f3b2c4d6-0000-4000-8000-000000000002";
18
+ const unpersistedIdentityIds = /* @__PURE__ */ new Set([UNPERSISTED_ROOT_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID]);
19
+ const isUnpersistedIdentityId = (identityId) => identityId !== void 0 && unpersistedIdentityIds.has(identityId);
20
+ const createUnpersistedLoginVerifyResult = () => new ApiKeyManager.VerifyResult(UNPERSISTED_LOGIN_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID, [Roles.TenantRole.LOGIN], null, false);
17
21
  class UnpersistedApiKeyManager {
18
22
  constructor(apiKeys = []) {
19
23
  __publicField(this, "apiKeys");
@@ -77,6 +81,9 @@ class UnpersistedApiKeyManager {
77
81
  return new UnpersistedApiKeyManager(hashes.map((it) => UnpersistedApiKeyManager.createRootApiKey(it)));
78
82
  }
79
83
  }
84
+ exports.UNPERSISTED_LOGIN_IDENTITY_ID = UNPERSISTED_LOGIN_IDENTITY_ID;
80
85
  exports.UNPERSISTED_ROOT_IDENTITY_ID = UNPERSISTED_ROOT_IDENTITY_ID;
81
86
  exports.UnpersistedApiKeyManager = UnpersistedApiKeyManager;
87
+ exports.createUnpersistedLoginVerifyResult = createUnpersistedLoginVerifyResult;
88
+ exports.isUnpersistedIdentityId = isUnpersistedIdentityId;
82
89
  //# sourceMappingURL=UnpersistedApiKeyManager.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"UnpersistedApiKeyManager.cjs","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/UnpersistedApiKeyManager.ts"],"sourcesContent":["import { computeTokenHash, isTokenHash, timingSafeCompareHash, TokenHash } from '../../utils/index.js'\nimport { TenantRole } from '../../authorization/index.js'\nimport { VerifyResult } from './ApiKeyManager.js'\n\n/**\n * Well-known identity id used for super-admin identities authenticated via an\n * unpersisted (configured) root token. The id is stable so that audit logs and\n * created entities consistently reference the same virtual identity, even\n * though no row exists in the `identity` table.\n */\nexport const UNPERSISTED_ROOT_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000001'\n\nexport interface UnpersistedApiKey {\n\treadonly tokenHash: TokenHash\n\treadonly identityId: string\n\treadonly roles: readonly string[]\n}\n\n/**\n * Verifies incoming tokens against a fixed set of root tokens configured out of\n * band (env/config) instead of being stored in the database. This enables token\n * rotation without database writes and avoids persisting the token hash.\n *\n * Token hashes are compared in constant time to avoid leaking information about\n * the configured secret.\n */\nexport class UnpersistedApiKeyManager {\n\tprivate readonly apiKeys: readonly UnpersistedApiKey[]\n\n\tconstructor(apiKeys: readonly UnpersistedApiKey[] = []) {\n\t\tthis.apiKeys = apiKeys\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.apiKeys.length === 0\n\t}\n\n\tverify(token: string): VerifyResult | null {\n\t\tif (this.apiKeys.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst tokenHash = computeTokenHash(token)\n\t\t// Iterate over all configured keys (no early return) so that matching\n\t\t// remains independent of the position of the matching key.\n\t\tlet matched: UnpersistedApiKey | null = null\n\t\tfor (const apiKey of this.apiKeys) {\n\t\t\tif (timingSafeCompareHash(tokenHash, apiKey.tokenHash)) {\n\t\t\t\tmatched = apiKey\n\t\t\t}\n\t\t}\n\t\tif (matched === null) {\n\t\t\treturn null\n\t\t}\n\t\treturn new VerifyResult(\n\t\t\tmatched.identityId,\n\t\t\t// There is no persisted api_key row, so there is no api key id.\n\t\t\tmatched.identityId,\n\t\t\t[...matched.roles],\n\t\t\tnull,\n\t\t\tfalse,\n\t\t)\n\t}\n\n\tstatic createRootApiKey(tokenHash: TokenHash): UnpersistedApiKey {\n\t\treturn {\n\t\t\ttokenHash,\n\t\t\tidentityId: UNPERSISTED_ROOT_IDENTITY_ID,\n\t\t\troles: [TenantRole.SUPER_ADMIN],\n\t\t}\n\t}\n\n\t/**\n\t * Builds a manager for unpersisted super-admin root tokens from configured\n\t * plain tokens and/or pre-computed token hashes.\n\t */\n\tstatic createForRootTokens(input: {\n\t\ttokens?: readonly string[]\n\t\ttokenHashes?: readonly string[]\n\t}): UnpersistedApiKeyManager {\n\t\tconst hashes: TokenHash[] = []\n\t\tfor (const token of input.tokens ?? []) {\n\t\t\tif (token.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thashes.push(computeTokenHash(token))\n\t\t}\n\t\tfor (const hash of input.tokenHashes ?? []) {\n\t\t\tif (hash.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst normalized = hash.toLowerCase()\n\t\t\tif (!isTokenHash(normalized)) {\n\t\t\t\tthrow new Error(`Invalid unpersisted root token hash: expected a 64-character sha256 hex string.`)\n\t\t\t}\n\t\t\thashes.push(normalized)\n\t\t}\n\t\treturn new UnpersistedApiKeyManager(hashes.map(it => UnpersistedApiKeyManager.createRootApiKey(it)))\n\t}\n}\n"],"names":["token","computeTokenHash","timingSafeCompareHash","VerifyResult","TenantRole","isTokenHash"],"mappings":";;;;;;;;;;;;;;;AAUO,MAAM,+BAA+B;AAgBrC,MAAM,yBAAyB;AAAA,EAGrC,YAAY,UAAwC,IAAI;AAFxD,kBAAA,MAAiB,SAAA;AAGhB,SAAK,UAAU;AAAA,EAChB;AAAA,EAEA,IAAI,UAAmB;AACtB,WAAO,KAAK,QAAQ,WAAW;AAAA,EAChC;AAAA,EAEA,OAAOA,SAAoC;AAC1C,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,aAAO;AAAA,IACR;AACA,UAAM,YAAYC,MAAAA,iBAAiBD,OAAK;AAGxC,QAAI,UAAoC;AACxC,eAAW,UAAU,KAAK,SAAS;AAClC,UAAIE,4BAAsB,WAAW,OAAO,SAAS,GAAG;AACvD,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,YAAY,MAAM;AACrB,aAAO;AAAA,IACR;AACA,WAAO,IAAIC,cAAAA;AAAAA,MACV,QAAQ;AAAA;AAAA,MAER,QAAQ;AAAA,MACR,CAAC,GAAG,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAEA,OAAO,iBAAiB,WAAyC;AAChE,WAAO;AAAA,MACN;AAAA,MACA,YAAY;AAAA,MACZ,OAAO,CAACC,MAAAA,WAAW,WAAW;AAAA,IAAA;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,oBAAoB,OAGE;AAC5B,UAAM,SAAsB,CAAA;AAC5B,eAAWJ,WAAS,MAAM,UAAU,CAAA,GAAI;AACvC,UAAIA,QAAM,WAAW,GAAG;AACvB;AAAA,MACD;AACA,aAAO,KAAKC,uBAAiBD,OAAK,CAAC;AAAA,IACpC;AACA,eAAW,QAAQ,MAAM,eAAe,CAAA,GAAI;AAC3C,UAAI,KAAK,WAAW,GAAG;AACtB;AAAA,MACD;AACA,YAAM,aAAa,KAAK,YAAA;AACxB,UAAI,CAACK,MAAAA,YAAY,UAAU,GAAG;AAC7B,cAAM,IAAI,MAAM,iFAAiF;AAAA,MAClG;AACA,aAAO,KAAK,UAAU;AAAA,IACvB;AACA,WAAO,IAAI,yBAAyB,OAAO,IAAI,QAAM,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;AAAA,EACpG;AACD;;;"}
1
+ {"version":3,"file":"UnpersistedApiKeyManager.cjs","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/UnpersistedApiKeyManager.ts"],"sourcesContent":["import { computeTokenHash, isTokenHash, timingSafeCompareHash, TokenHash } from '../../utils/index.js'\nimport { TenantRole } from '../../authorization/index.js'\nimport { VerifyResult } from './ApiKeyManager.js'\n\n/**\n * Well-known identity id used for super-admin identities authenticated via an\n * unpersisted (configured) root token. The id is stable so that audit logs and\n * created entities consistently reference the same virtual identity, even\n * though no row exists in the `identity` table.\n */\nexport const UNPERSISTED_ROOT_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000001'\n\n/**\n * Well-known identity id for the pre-sign-in caller of a first-party UI served by the engine (the\n * management panel's login screen). It holds exactly {@link TenantRole.LOGIN} and exists only in\n * memory, so the surface it opens is fixed by this file rather than by whatever token an operator\n * happened to configure.\n */\nexport const UNPERSISTED_LOGIN_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000002'\n\nconst unpersistedIdentityIds: ReadonlySet<string> = new Set([UNPERSISTED_ROOT_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID])\n\n/**\n * Whether an identity id is one of the virtual identities above, i.e. has no row in `identity`.\n * Anything writing a foreign key to `identity` must ask first — see `AuthLogService`.\n */\nexport const isUnpersistedIdentityId = (identityId: string | undefined): boolean => identityId !== undefined && unpersistedIdentityIds.has(identityId)\n\n/**\n * The pre-sign-in caller of an engine-served UI, as an already-verified result. There is no token to\n * present: the surface is reached by being routed there, so nothing is compared and nothing is read\n * from the database.\n */\nexport const createUnpersistedLoginVerifyResult = (): VerifyResult =>\n\tnew VerifyResult(UNPERSISTED_LOGIN_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID, [TenantRole.LOGIN], null, false)\n\nexport interface UnpersistedApiKey {\n\treadonly tokenHash: TokenHash\n\treadonly identityId: string\n\treadonly roles: readonly string[]\n}\n\n/**\n * Verifies incoming tokens against a fixed set of root tokens configured out of\n * band (env/config) instead of being stored in the database. This enables token\n * rotation without database writes and avoids persisting the token hash.\n *\n * Token hashes are compared in constant time to avoid leaking information about\n * the configured secret.\n */\nexport class UnpersistedApiKeyManager {\n\tprivate readonly apiKeys: readonly UnpersistedApiKey[]\n\n\tconstructor(apiKeys: readonly UnpersistedApiKey[] = []) {\n\t\tthis.apiKeys = apiKeys\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.apiKeys.length === 0\n\t}\n\n\tverify(token: string): VerifyResult | null {\n\t\tif (this.apiKeys.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst tokenHash = computeTokenHash(token)\n\t\t// Iterate over all configured keys (no early return) so that matching\n\t\t// remains independent of the position of the matching key.\n\t\tlet matched: UnpersistedApiKey | null = null\n\t\tfor (const apiKey of this.apiKeys) {\n\t\t\tif (timingSafeCompareHash(tokenHash, apiKey.tokenHash)) {\n\t\t\t\tmatched = apiKey\n\t\t\t}\n\t\t}\n\t\tif (matched === null) {\n\t\t\treturn null\n\t\t}\n\t\treturn new VerifyResult(\n\t\t\tmatched.identityId,\n\t\t\t// There is no persisted api_key row, so there is no api key id.\n\t\t\tmatched.identityId,\n\t\t\t[...matched.roles],\n\t\t\tnull,\n\t\t\tfalse,\n\t\t)\n\t}\n\n\tstatic createRootApiKey(tokenHash: TokenHash): UnpersistedApiKey {\n\t\treturn {\n\t\t\ttokenHash,\n\t\t\tidentityId: UNPERSISTED_ROOT_IDENTITY_ID,\n\t\t\troles: [TenantRole.SUPER_ADMIN],\n\t\t}\n\t}\n\n\t/**\n\t * Builds a manager for unpersisted super-admin root tokens from configured\n\t * plain tokens and/or pre-computed token hashes.\n\t */\n\tstatic createForRootTokens(input: {\n\t\ttokens?: readonly string[]\n\t\ttokenHashes?: readonly string[]\n\t}): UnpersistedApiKeyManager {\n\t\tconst hashes: TokenHash[] = []\n\t\tfor (const token of input.tokens ?? []) {\n\t\t\tif (token.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thashes.push(computeTokenHash(token))\n\t\t}\n\t\tfor (const hash of input.tokenHashes ?? []) {\n\t\t\tif (hash.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst normalized = hash.toLowerCase()\n\t\t\tif (!isTokenHash(normalized)) {\n\t\t\t\tthrow new Error(`Invalid unpersisted root token hash: expected a 64-character sha256 hex string.`)\n\t\t\t}\n\t\t\thashes.push(normalized)\n\t\t}\n\t\treturn new UnpersistedApiKeyManager(hashes.map(it => UnpersistedApiKeyManager.createRootApiKey(it)))\n\t}\n}\n"],"names":["VerifyResult","TenantRole","token","computeTokenHash","timingSafeCompareHash","isTokenHash"],"mappings":";;;;;;;;;;;;;;;AAUO,MAAM,+BAA+B;AAQrC,MAAM,gCAAgC;AAE7C,MAAM,yBAA8C,oBAAI,IAAI,CAAC,8BAA8B,6BAA6B,CAAC;AAMlH,MAAM,0BAA0B,CAAC,eAA4C,eAAe,UAAa,uBAAuB,IAAI,UAAU;AAO9I,MAAM,qCAAqC,MACjD,IAAIA,cAAAA,aAAa,+BAA+B,+BAA+B,CAACC,iBAAW,KAAK,GAAG,MAAM,KAAK;AAgBxG,MAAM,yBAAyB;AAAA,EAGrC,YAAY,UAAwC,IAAI;AAFxD,kBAAA,MAAiB,SAAA;AAGhB,SAAK,UAAU;AAAA,EAChB;AAAA,EAEA,IAAI,UAAmB;AACtB,WAAO,KAAK,QAAQ,WAAW;AAAA,EAChC;AAAA,EAEA,OAAOC,SAAoC;AAC1C,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,aAAO;AAAA,IACR;AACA,UAAM,YAAYC,MAAAA,iBAAiBD,OAAK;AAGxC,QAAI,UAAoC;AACxC,eAAW,UAAU,KAAK,SAAS;AAClC,UAAIE,4BAAsB,WAAW,OAAO,SAAS,GAAG;AACvD,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,YAAY,MAAM;AACrB,aAAO;AAAA,IACR;AACA,WAAO,IAAIJ,cAAAA;AAAAA,MACV,QAAQ;AAAA;AAAA,MAER,QAAQ;AAAA,MACR,CAAC,GAAG,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAEA,OAAO,iBAAiB,WAAyC;AAChE,WAAO;AAAA,MACN;AAAA,MACA,YAAY;AAAA,MACZ,OAAO,CAACC,MAAAA,WAAW,WAAW;AAAA,IAAA;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,oBAAoB,OAGE;AAC5B,UAAM,SAAsB,CAAA;AAC5B,eAAWC,WAAS,MAAM,UAAU,CAAA,GAAI;AACvC,UAAIA,QAAM,WAAW,GAAG;AACvB;AAAA,MACD;AACA,aAAO,KAAKC,uBAAiBD,OAAK,CAAC;AAAA,IACpC;AACA,eAAW,QAAQ,MAAM,eAAe,CAAA,GAAI;AAC3C,UAAI,KAAK,WAAW,GAAG;AACtB;AAAA,MACD;AACA,YAAM,aAAa,KAAK,YAAA;AACxB,UAAI,CAACG,MAAAA,YAAY,UAAU,GAAG;AAC7B,cAAM,IAAI,MAAM,iFAAiF;AAAA,MAClG;AACA,aAAO,KAAK,UAAU;AAAA,IACvB;AACA,WAAO,IAAI,yBAAyB,OAAO,IAAI,QAAM,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;AAAA,EACpG;AACD;;;;;;"}
@@ -12,6 +12,10 @@ var __defProp = Object.defineProperty;
12
12
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
13
  var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
14
14
  const UNPERSISTED_ROOT_IDENTITY_ID = "f3b2c4d6-0000-4000-8000-000000000001";
15
+ const UNPERSISTED_LOGIN_IDENTITY_ID = "f3b2c4d6-0000-4000-8000-000000000002";
16
+ const unpersistedIdentityIds = /* @__PURE__ */ new Set([UNPERSISTED_ROOT_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID]);
17
+ const isUnpersistedIdentityId = (identityId) => identityId !== void 0 && unpersistedIdentityIds.has(identityId);
18
+ const createUnpersistedLoginVerifyResult = () => new VerifyResult(UNPERSISTED_LOGIN_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID, [TenantRole.LOGIN], null, false);
15
19
  class UnpersistedApiKeyManager {
16
20
  constructor(apiKeys = []) {
17
21
  __publicField(this, "apiKeys");
@@ -76,7 +80,10 @@ class UnpersistedApiKeyManager {
76
80
  }
77
81
  }
78
82
  export {
83
+ UNPERSISTED_LOGIN_IDENTITY_ID,
79
84
  UNPERSISTED_ROOT_IDENTITY_ID,
80
- UnpersistedApiKeyManager
85
+ UnpersistedApiKeyManager,
86
+ createUnpersistedLoginVerifyResult,
87
+ isUnpersistedIdentityId
81
88
  };
82
89
  //# sourceMappingURL=UnpersistedApiKeyManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UnpersistedApiKeyManager.js","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/UnpersistedApiKeyManager.ts"],"sourcesContent":["import { computeTokenHash, isTokenHash, timingSafeCompareHash, TokenHash } from '../../utils/index.js'\nimport { TenantRole } from '../../authorization/index.js'\nimport { VerifyResult } from './ApiKeyManager.js'\n\n/**\n * Well-known identity id used for super-admin identities authenticated via an\n * unpersisted (configured) root token. The id is stable so that audit logs and\n * created entities consistently reference the same virtual identity, even\n * though no row exists in the `identity` table.\n */\nexport const UNPERSISTED_ROOT_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000001'\n\nexport interface UnpersistedApiKey {\n\treadonly tokenHash: TokenHash\n\treadonly identityId: string\n\treadonly roles: readonly string[]\n}\n\n/**\n * Verifies incoming tokens against a fixed set of root tokens configured out of\n * band (env/config) instead of being stored in the database. This enables token\n * rotation without database writes and avoids persisting the token hash.\n *\n * Token hashes are compared in constant time to avoid leaking information about\n * the configured secret.\n */\nexport class UnpersistedApiKeyManager {\n\tprivate readonly apiKeys: readonly UnpersistedApiKey[]\n\n\tconstructor(apiKeys: readonly UnpersistedApiKey[] = []) {\n\t\tthis.apiKeys = apiKeys\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.apiKeys.length === 0\n\t}\n\n\tverify(token: string): VerifyResult | null {\n\t\tif (this.apiKeys.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst tokenHash = computeTokenHash(token)\n\t\t// Iterate over all configured keys (no early return) so that matching\n\t\t// remains independent of the position of the matching key.\n\t\tlet matched: UnpersistedApiKey | null = null\n\t\tfor (const apiKey of this.apiKeys) {\n\t\t\tif (timingSafeCompareHash(tokenHash, apiKey.tokenHash)) {\n\t\t\t\tmatched = apiKey\n\t\t\t}\n\t\t}\n\t\tif (matched === null) {\n\t\t\treturn null\n\t\t}\n\t\treturn new VerifyResult(\n\t\t\tmatched.identityId,\n\t\t\t// There is no persisted api_key row, so there is no api key id.\n\t\t\tmatched.identityId,\n\t\t\t[...matched.roles],\n\t\t\tnull,\n\t\t\tfalse,\n\t\t)\n\t}\n\n\tstatic createRootApiKey(tokenHash: TokenHash): UnpersistedApiKey {\n\t\treturn {\n\t\t\ttokenHash,\n\t\t\tidentityId: UNPERSISTED_ROOT_IDENTITY_ID,\n\t\t\troles: [TenantRole.SUPER_ADMIN],\n\t\t}\n\t}\n\n\t/**\n\t * Builds a manager for unpersisted super-admin root tokens from configured\n\t * plain tokens and/or pre-computed token hashes.\n\t */\n\tstatic createForRootTokens(input: {\n\t\ttokens?: readonly string[]\n\t\ttokenHashes?: readonly string[]\n\t}): UnpersistedApiKeyManager {\n\t\tconst hashes: TokenHash[] = []\n\t\tfor (const token of input.tokens ?? []) {\n\t\t\tif (token.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thashes.push(computeTokenHash(token))\n\t\t}\n\t\tfor (const hash of input.tokenHashes ?? []) {\n\t\t\tif (hash.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst normalized = hash.toLowerCase()\n\t\t\tif (!isTokenHash(normalized)) {\n\t\t\t\tthrow new Error(`Invalid unpersisted root token hash: expected a 64-character sha256 hex string.`)\n\t\t\t}\n\t\t\thashes.push(normalized)\n\t\t}\n\t\treturn new UnpersistedApiKeyManager(hashes.map(it => UnpersistedApiKeyManager.createRootApiKey(it)))\n\t}\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAUO,MAAM,+BAA+B;AAgBrC,MAAM,yBAAyB;AAAA,EAGrC,YAAY,UAAwC,IAAI;AAFxD,kBAAA,MAAiB,SAAA;AAGhB,SAAK,UAAU;AAAA,EAChB;AAAA,EAEA,IAAI,UAAmB;AACtB,WAAO,KAAK,QAAQ,WAAW;AAAA,EAChC;AAAA,EAEA,OAAO,OAAoC;AAC1C,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,aAAO;AAAA,IACR;AACA,UAAM,YAAY,iBAAiB,KAAK;AAGxC,QAAI,UAAoC;AACxC,eAAW,UAAU,KAAK,SAAS;AAClC,UAAI,sBAAsB,WAAW,OAAO,SAAS,GAAG;AACvD,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,YAAY,MAAM;AACrB,aAAO;AAAA,IACR;AACA,WAAO,IAAI;AAAA,MACV,QAAQ;AAAA;AAAA,MAER,QAAQ;AAAA,MACR,CAAC,GAAG,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAEA,OAAO,iBAAiB,WAAyC;AAChE,WAAO;AAAA,MACN;AAAA,MACA,YAAY;AAAA,MACZ,OAAO,CAAC,WAAW,WAAW;AAAA,IAAA;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,oBAAoB,OAGE;AAC5B,UAAM,SAAsB,CAAA;AAC5B,eAAW,SAAS,MAAM,UAAU,CAAA,GAAI;AACvC,UAAI,MAAM,WAAW,GAAG;AACvB;AAAA,MACD;AACA,aAAO,KAAK,iBAAiB,KAAK,CAAC;AAAA,IACpC;AACA,eAAW,QAAQ,MAAM,eAAe,CAAA,GAAI;AAC3C,UAAI,KAAK,WAAW,GAAG;AACtB;AAAA,MACD;AACA,YAAM,aAAa,KAAK,YAAA;AACxB,UAAI,CAAC,YAAY,UAAU,GAAG;AAC7B,cAAM,IAAI,MAAM,iFAAiF;AAAA,MAClG;AACA,aAAO,KAAK,UAAU;AAAA,IACvB;AACA,WAAO,IAAI,yBAAyB,OAAO,IAAI,QAAM,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;AAAA,EACpG;AACD;"}
1
+ {"version":3,"file":"UnpersistedApiKeyManager.js","sources":["../../../../../../../packages/engine-tenant-api/src/model/service/apiKey/UnpersistedApiKeyManager.ts"],"sourcesContent":["import { computeTokenHash, isTokenHash, timingSafeCompareHash, TokenHash } from '../../utils/index.js'\nimport { TenantRole } from '../../authorization/index.js'\nimport { VerifyResult } from './ApiKeyManager.js'\n\n/**\n * Well-known identity id used for super-admin identities authenticated via an\n * unpersisted (configured) root token. The id is stable so that audit logs and\n * created entities consistently reference the same virtual identity, even\n * though no row exists in the `identity` table.\n */\nexport const UNPERSISTED_ROOT_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000001'\n\n/**\n * Well-known identity id for the pre-sign-in caller of a first-party UI served by the engine (the\n * management panel's login screen). It holds exactly {@link TenantRole.LOGIN} and exists only in\n * memory, so the surface it opens is fixed by this file rather than by whatever token an operator\n * happened to configure.\n */\nexport const UNPERSISTED_LOGIN_IDENTITY_ID = 'f3b2c4d6-0000-4000-8000-000000000002'\n\nconst unpersistedIdentityIds: ReadonlySet<string> = new Set([UNPERSISTED_ROOT_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID])\n\n/**\n * Whether an identity id is one of the virtual identities above, i.e. has no row in `identity`.\n * Anything writing a foreign key to `identity` must ask first — see `AuthLogService`.\n */\nexport const isUnpersistedIdentityId = (identityId: string | undefined): boolean => identityId !== undefined && unpersistedIdentityIds.has(identityId)\n\n/**\n * The pre-sign-in caller of an engine-served UI, as an already-verified result. There is no token to\n * present: the surface is reached by being routed there, so nothing is compared and nothing is read\n * from the database.\n */\nexport const createUnpersistedLoginVerifyResult = (): VerifyResult =>\n\tnew VerifyResult(UNPERSISTED_LOGIN_IDENTITY_ID, UNPERSISTED_LOGIN_IDENTITY_ID, [TenantRole.LOGIN], null, false)\n\nexport interface UnpersistedApiKey {\n\treadonly tokenHash: TokenHash\n\treadonly identityId: string\n\treadonly roles: readonly string[]\n}\n\n/**\n * Verifies incoming tokens against a fixed set of root tokens configured out of\n * band (env/config) instead of being stored in the database. This enables token\n * rotation without database writes and avoids persisting the token hash.\n *\n * Token hashes are compared in constant time to avoid leaking information about\n * the configured secret.\n */\nexport class UnpersistedApiKeyManager {\n\tprivate readonly apiKeys: readonly UnpersistedApiKey[]\n\n\tconstructor(apiKeys: readonly UnpersistedApiKey[] = []) {\n\t\tthis.apiKeys = apiKeys\n\t}\n\n\tget isEmpty(): boolean {\n\t\treturn this.apiKeys.length === 0\n\t}\n\n\tverify(token: string): VerifyResult | null {\n\t\tif (this.apiKeys.length === 0) {\n\t\t\treturn null\n\t\t}\n\t\tconst tokenHash = computeTokenHash(token)\n\t\t// Iterate over all configured keys (no early return) so that matching\n\t\t// remains independent of the position of the matching key.\n\t\tlet matched: UnpersistedApiKey | null = null\n\t\tfor (const apiKey of this.apiKeys) {\n\t\t\tif (timingSafeCompareHash(tokenHash, apiKey.tokenHash)) {\n\t\t\t\tmatched = apiKey\n\t\t\t}\n\t\t}\n\t\tif (matched === null) {\n\t\t\treturn null\n\t\t}\n\t\treturn new VerifyResult(\n\t\t\tmatched.identityId,\n\t\t\t// There is no persisted api_key row, so there is no api key id.\n\t\t\tmatched.identityId,\n\t\t\t[...matched.roles],\n\t\t\tnull,\n\t\t\tfalse,\n\t\t)\n\t}\n\n\tstatic createRootApiKey(tokenHash: TokenHash): UnpersistedApiKey {\n\t\treturn {\n\t\t\ttokenHash,\n\t\t\tidentityId: UNPERSISTED_ROOT_IDENTITY_ID,\n\t\t\troles: [TenantRole.SUPER_ADMIN],\n\t\t}\n\t}\n\n\t/**\n\t * Builds a manager for unpersisted super-admin root tokens from configured\n\t * plain tokens and/or pre-computed token hashes.\n\t */\n\tstatic createForRootTokens(input: {\n\t\ttokens?: readonly string[]\n\t\ttokenHashes?: readonly string[]\n\t}): UnpersistedApiKeyManager {\n\t\tconst hashes: TokenHash[] = []\n\t\tfor (const token of input.tokens ?? []) {\n\t\t\tif (token.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thashes.push(computeTokenHash(token))\n\t\t}\n\t\tfor (const hash of input.tokenHashes ?? []) {\n\t\t\tif (hash.length === 0) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst normalized = hash.toLowerCase()\n\t\t\tif (!isTokenHash(normalized)) {\n\t\t\t\tthrow new Error(`Invalid unpersisted root token hash: expected a 64-character sha256 hex string.`)\n\t\t\t}\n\t\t\thashes.push(normalized)\n\t\t}\n\t\treturn new UnpersistedApiKeyManager(hashes.map(it => UnpersistedApiKeyManager.createRootApiKey(it)))\n\t}\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAUO,MAAM,+BAA+B;AAQrC,MAAM,gCAAgC;AAE7C,MAAM,yBAA8C,oBAAI,IAAI,CAAC,8BAA8B,6BAA6B,CAAC;AAMlH,MAAM,0BAA0B,CAAC,eAA4C,eAAe,UAAa,uBAAuB,IAAI,UAAU;AAO9I,MAAM,qCAAqC,MACjD,IAAI,aAAa,+BAA+B,+BAA+B,CAAC,WAAW,KAAK,GAAG,MAAM,KAAK;AAgBxG,MAAM,yBAAyB;AAAA,EAGrC,YAAY,UAAwC,IAAI;AAFxD,kBAAA,MAAiB,SAAA;AAGhB,SAAK,UAAU;AAAA,EAChB;AAAA,EAEA,IAAI,UAAmB;AACtB,WAAO,KAAK,QAAQ,WAAW;AAAA,EAChC;AAAA,EAEA,OAAO,OAAoC;AAC1C,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC9B,aAAO;AAAA,IACR;AACA,UAAM,YAAY,iBAAiB,KAAK;AAGxC,QAAI,UAAoC;AACxC,eAAW,UAAU,KAAK,SAAS;AAClC,UAAI,sBAAsB,WAAW,OAAO,SAAS,GAAG;AACvD,kBAAU;AAAA,MACX;AAAA,IACD;AACA,QAAI,YAAY,MAAM;AACrB,aAAO;AAAA,IACR;AACA,WAAO,IAAI;AAAA,MACV,QAAQ;AAAA;AAAA,MAER,QAAQ;AAAA,MACR,CAAC,GAAG,QAAQ,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAEA,OAAO,iBAAiB,WAAyC;AAChE,WAAO;AAAA,MACN;AAAA,MACA,YAAY;AAAA,MACZ,OAAO,CAAC,WAAW,WAAW;AAAA,IAAA;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,oBAAoB,OAGE;AAC5B,UAAM,SAAsB,CAAA;AAC5B,eAAW,SAAS,MAAM,UAAU,CAAA,GAAI;AACvC,UAAI,MAAM,WAAW,GAAG;AACvB;AAAA,MACD;AACA,aAAO,KAAK,iBAAiB,KAAK,CAAC;AAAA,IACpC;AACA,eAAW,QAAQ,MAAM,eAAe,CAAA,GAAI;AAC3C,UAAI,KAAK,WAAW,GAAG;AACtB;AAAA,MACD;AACA,YAAM,aAAa,KAAK,YAAA;AACxB,UAAI,CAAC,YAAY,UAAU,GAAG;AAC7B,cAAM,IAAI,MAAM,iFAAiF;AAAA,MAClG;AACA,aAAO,KAAK,UAAU;AAAA,IACvB;AACA,WAAO,IAAI,yBAAyB,OAAO,IAAI,QAAM,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;AAAA,EACpG;AACD;"}