@hilbras/keystone 1.6.0 → 2.5.0

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 (604) hide show
  1. package/CHANGELOG.md +625 -0
  2. package/README.md +198 -50
  3. package/dist/cli.js +25 -27
  4. package/dist/cli.js.map +1 -1
  5. package/dist/config.d.ts +23 -1
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +53 -8
  8. package/dist/config.js.map +1 -1
  9. package/dist/container.d.ts +4 -1
  10. package/dist/container.d.ts.map +1 -1
  11. package/dist/container.js.map +1 -1
  12. package/dist/db/migrations/0008_flashy_firestar.sql +5 -0
  13. package/dist/db/migrations/0009_daffy_changeling.sql +1 -0
  14. package/dist/db/migrations/0010_wealthy_shadow_king.sql +37 -0
  15. package/dist/db/migrations/0011_overrated_vector.sql +18 -0
  16. package/dist/db/migrations/0012_brave_darkhawk.sql +28 -0
  17. package/dist/db/migrations/0013_wonderful_chimera.sql +5 -0
  18. package/dist/db/migrations/0014_magical_morph.sql +48 -0
  19. package/dist/db/migrations/0015_chief_vargas.sql +7 -0
  20. package/dist/db/migrations/0016_gorgeous_charles_xavier.sql +5 -0
  21. package/dist/db/migrations/0017_flashy_nekra.sql +1 -0
  22. package/{src/db/migrations/meta/0007_snapshot.json → dist/db/migrations/meta/0008_snapshot.json} +136 -32
  23. package/{src/db/migrations/meta/0006_snapshot.json → dist/db/migrations/meta/0009_snapshot.json} +359 -30
  24. package/{src/db/migrations/meta/0005_snapshot.json → dist/db/migrations/meta/0010_snapshot.json} +459 -29
  25. package/{src/db/migrations/meta/0004_snapshot.json → dist/db/migrations/meta/0011_snapshot.json} +594 -30
  26. package/dist/db/migrations/meta/0012_snapshot.json +3891 -0
  27. package/dist/db/migrations/meta/0013_snapshot.json +3919 -0
  28. package/dist/db/migrations/meta/0014_snapshot.json +4311 -0
  29. package/dist/db/migrations/meta/0015_snapshot.json +4340 -0
  30. package/dist/db/migrations/meta/0016_snapshot.json +4363 -0
  31. package/dist/db/migrations/meta/0017_snapshot.json +4370 -0
  32. package/dist/db/migrations/meta/_journal.json +70 -0
  33. package/dist/db/reencryptOidcSecrets.d.ts +2 -0
  34. package/dist/db/reencryptOidcSecrets.d.ts.map +1 -0
  35. package/dist/db/reencryptOidcSecrets.js +41 -0
  36. package/dist/db/reencryptOidcSecrets.js.map +1 -0
  37. package/dist/db/schema.d.ts +2693 -88
  38. package/dist/db/schema.d.ts.map +1 -1
  39. package/dist/db/schema.js +157 -4
  40. package/dist/db/schema.js.map +1 -1
  41. package/dist/db/seed.js +67 -40
  42. package/dist/db/seed.js.map +1 -1
  43. package/dist/di.d.ts.map +1 -1
  44. package/dist/di.js +11 -3
  45. package/dist/di.js.map +1 -1
  46. package/dist/index.d.ts +2 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +34 -3
  49. package/dist/index.js.map +1 -1
  50. package/dist/lib/errors.d.ts +2 -1
  51. package/dist/lib/errors.d.ts.map +1 -1
  52. package/dist/lib/errors.js +10 -0
  53. package/dist/lib/errors.js.map +1 -1
  54. package/dist/plugins/appContext.d.ts.map +1 -1
  55. package/dist/plugins/appContext.js +3 -12
  56. package/dist/plugins/appContext.js.map +1 -1
  57. package/dist/plugins/audit.d.ts.map +1 -1
  58. package/dist/plugins/audit.js +26 -6
  59. package/dist/plugins/audit.js.map +1 -1
  60. package/dist/plugins/auth.d.ts.map +1 -1
  61. package/dist/plugins/auth.js +32 -7
  62. package/dist/plugins/auth.js.map +1 -1
  63. package/dist/plugins/headerSanitization.d.ts +15 -0
  64. package/dist/plugins/headerSanitization.d.ts.map +1 -0
  65. package/dist/plugins/headerSanitization.js +39 -0
  66. package/dist/plugins/headerSanitization.js.map +1 -0
  67. package/dist/plugins/metrics.d.ts.map +1 -1
  68. package/dist/plugins/mtls.d.ts +21 -2
  69. package/dist/plugins/mtls.d.ts.map +1 -1
  70. package/dist/plugins/mtls.js +94 -47
  71. package/dist/plugins/mtls.js.map +1 -1
  72. package/dist/plugins/permissions.d.ts.map +1 -1
  73. package/dist/plugins/permissions.js +29 -9
  74. package/dist/plugins/permissions.js.map +1 -1
  75. package/dist/plugins/rateLimit.d.ts +16 -0
  76. package/dist/plugins/rateLimit.d.ts.map +1 -1
  77. package/dist/plugins/rateLimit.js +16 -3
  78. package/dist/plugins/rateLimit.js.map +1 -1
  79. package/dist/plugins/requestLogger.d.ts.map +1 -1
  80. package/dist/repositories/apiKey.d.ts.map +1 -1
  81. package/dist/repositories/application.d.ts +1 -1
  82. package/dist/repositories/application.d.ts.map +1 -1
  83. package/dist/repositories/application.js +9 -3
  84. package/dist/repositories/application.js.map +1 -1
  85. package/dist/repositories/audit.d.ts.map +1 -1
  86. package/dist/repositories/index.d.ts +3 -0
  87. package/dist/repositories/index.d.ts.map +1 -1
  88. package/dist/repositories/index.js +3 -0
  89. package/dist/repositories/index.js.map +1 -1
  90. package/dist/repositories/mfaChallenge.d.ts +23 -0
  91. package/dist/repositories/mfaChallenge.d.ts.map +1 -0
  92. package/dist/repositories/mfaChallenge.js +71 -0
  93. package/dist/repositories/mfaChallenge.js.map +1 -0
  94. package/dist/repositories/oidcConnection.d.ts +27 -43
  95. package/dist/repositories/oidcConnection.d.ts.map +1 -1
  96. package/dist/repositories/oidcConnection.js +15 -6
  97. package/dist/repositories/oidcConnection.js.map +1 -1
  98. package/dist/repositories/organization.d.ts +9 -9
  99. package/dist/repositories/organization.d.ts.map +1 -1
  100. package/dist/repositories/organization.js +91 -37
  101. package/dist/repositories/organization.js.map +1 -1
  102. package/dist/repositories/permission.d.ts.map +1 -1
  103. package/dist/repositories/permission.js +27 -5
  104. package/dist/repositories/permission.js.map +1 -1
  105. package/dist/repositories/samlConnection.d.ts +28 -28
  106. package/dist/repositories/samlConnection.d.ts.map +1 -1
  107. package/dist/repositories/samlConnection.js +12 -4
  108. package/dist/repositories/samlConnection.js.map +1 -1
  109. package/dist/repositories/scimConnection.d.ts +38 -0
  110. package/dist/repositories/scimConnection.d.ts.map +1 -0
  111. package/dist/repositories/scimConnection.js +123 -0
  112. package/dist/repositories/scimConnection.js.map +1 -0
  113. package/dist/repositories/scimGroup.d.ts +36 -0
  114. package/dist/repositories/scimGroup.d.ts.map +1 -0
  115. package/dist/repositories/scimGroup.js +113 -0
  116. package/dist/repositories/scimGroup.js.map +1 -0
  117. package/dist/repositories/session.d.ts +24 -13
  118. package/dist/repositories/session.d.ts.map +1 -1
  119. package/dist/repositories/session.js +13 -1
  120. package/dist/repositories/session.js.map +1 -1
  121. package/dist/repositories/types.d.ts +117 -6
  122. package/dist/repositories/types.d.ts.map +1 -1
  123. package/dist/repositories/types.js +7 -1
  124. package/dist/repositories/types.js.map +1 -1
  125. package/dist/repositories/user.d.ts +23 -1
  126. package/dist/repositories/user.d.ts.map +1 -1
  127. package/dist/repositories/user.js +180 -4
  128. package/dist/repositories/user.js.map +1 -1
  129. package/dist/routes/admin/billing.js +4 -4
  130. package/dist/routes/admin/billing.js.map +1 -1
  131. package/dist/routes/admin/helpers.d.ts +16 -20
  132. package/dist/routes/admin/helpers.d.ts.map +1 -1
  133. package/dist/routes/admin/helpers.js +53 -10
  134. package/dist/routes/admin/helpers.js.map +1 -1
  135. package/dist/routes/admin/organizations.d.ts.map +1 -1
  136. package/dist/routes/admin/organizations.js +105 -69
  137. package/dist/routes/admin/organizations.js.map +1 -1
  138. package/dist/routes/admin/permissions.d.ts.map +1 -1
  139. package/dist/routes/admin/permissions.js +48 -14
  140. package/dist/routes/admin/permissions.js.map +1 -1
  141. package/dist/routes/admin/platform.d.ts.map +1 -1
  142. package/dist/routes/admin/platform.js +86 -35
  143. package/dist/routes/admin/platform.js.map +1 -1
  144. package/dist/routes/admin/sso.d.ts.map +1 -1
  145. package/dist/routes/admin/sso.js +143 -12
  146. package/dist/routes/admin/sso.js.map +1 -1
  147. package/dist/routes/admin/webhooks.js +8 -8
  148. package/dist/routes/admin/webhooks.js.map +1 -1
  149. package/dist/routes/apiKeys.d.ts.map +1 -1
  150. package/dist/routes/apiKeys.js +23 -5
  151. package/dist/routes/apiKeys.js.map +1 -1
  152. package/dist/routes/auth.d.ts.map +1 -1
  153. package/dist/routes/auth.js +112 -22
  154. package/dist/routes/auth.js.map +1 -1
  155. package/dist/routes/authz.d.ts.map +1 -1
  156. package/dist/routes/authz.js +16 -4
  157. package/dist/routes/authz.js.map +1 -1
  158. package/dist/routes/config.d.ts.map +1 -1
  159. package/dist/routes/config.js +16 -14
  160. package/dist/routes/config.js.map +1 -1
  161. package/dist/routes/federation.d.ts.map +1 -1
  162. package/dist/routes/federation.js +17 -3
  163. package/dist/routes/federation.js.map +1 -1
  164. package/dist/routes/magicLinks.d.ts.map +1 -1
  165. package/dist/routes/magicLinks.js +15 -3
  166. package/dist/routes/magicLinks.js.map +1 -1
  167. package/dist/routes/oauth.d.ts.map +1 -1
  168. package/dist/routes/oauth.js +60 -5
  169. package/dist/routes/oauth.js.map +1 -1
  170. package/dist/routes/oauth2.d.ts.map +1 -1
  171. package/dist/routes/oauth2.js +188 -22
  172. package/dist/routes/oauth2.js.map +1 -1
  173. package/dist/routes/oidcEnterprise.d.ts.map +1 -1
  174. package/dist/routes/oidcEnterprise.js +67 -21
  175. package/dist/routes/oidcEnterprise.js.map +1 -1
  176. package/dist/routes/password.d.ts.map +1 -1
  177. package/dist/routes/password.js.map +1 -1
  178. package/dist/routes/profile.js +4 -4
  179. package/dist/routes/profile.js.map +1 -1
  180. package/dist/routes/saml.d.ts +33 -0
  181. package/dist/routes/saml.d.ts.map +1 -1
  182. package/dist/routes/saml.js +217 -12
  183. package/dist/routes/saml.js.map +1 -1
  184. package/dist/routes/scim.d.ts +13 -0
  185. package/dist/routes/scim.d.ts.map +1 -1
  186. package/dist/routes/scim.js +885 -110
  187. package/dist/routes/scim.js.map +1 -1
  188. package/dist/routes/sdk.d.ts.map +1 -1
  189. package/dist/routes/sdk.js +24 -8
  190. package/dist/routes/sdk.js.map +1 -1
  191. package/dist/routes/serviceAccounts.d.ts.map +1 -1
  192. package/dist/routes/serviceAccounts.js +63 -2
  193. package/dist/routes/serviceAccounts.js.map +1 -1
  194. package/dist/routes/setup.d.ts.map +1 -1
  195. package/dist/routes/setup.js +15 -16
  196. package/dist/routes/setup.js.map +1 -1
  197. package/dist/routes/smsOtp.js +1 -1
  198. package/dist/routes/smsOtp.js.map +1 -1
  199. package/dist/routes/totp.d.ts.map +1 -1
  200. package/dist/routes/totp.js +112 -23
  201. package/dist/routes/totp.js.map +1 -1
  202. package/dist/routes/webauthn.d.ts.map +1 -1
  203. package/dist/routes/webauthn.js +44 -8
  204. package/dist/routes/webauthn.js.map +1 -1
  205. package/dist/routes/workflows.d.ts.map +1 -1
  206. package/dist/routes/workflows.js +58 -25
  207. package/dist/routes/workflows.js.map +1 -1
  208. package/dist/sdk/index.d.ts.map +1 -1
  209. package/dist/sdk/index.js +141 -40
  210. package/dist/sdk/index.js.map +1 -1
  211. package/dist/sdk/types.d.ts +64 -30
  212. package/dist/sdk/types.d.ts.map +1 -1
  213. package/dist/services/application/authentication.d.ts +39 -1
  214. package/dist/services/application/authentication.d.ts.map +1 -1
  215. package/dist/services/application/authentication.js +43 -1
  216. package/dist/services/application/authentication.js.map +1 -1
  217. package/dist/services/application/identity.d.ts +10 -7
  218. package/dist/services/application/identity.d.ts.map +1 -1
  219. package/dist/services/application/identity.js +13 -10
  220. package/dist/services/application/identity.js.map +1 -1
  221. package/dist/services/application/organization.d.ts +16 -8
  222. package/dist/services/application/organization.d.ts.map +1 -1
  223. package/dist/services/application/organization.js +160 -12
  224. package/dist/services/application/organization.js.map +1 -1
  225. package/dist/services/applications.js +2 -2
  226. package/dist/services/applications.js.map +1 -1
  227. package/dist/services/audit.d.ts +1 -0
  228. package/dist/services/audit.d.ts.map +1 -1
  229. package/dist/services/audit.js +13 -0
  230. package/dist/services/audit.js.map +1 -1
  231. package/dist/services/cache.d.ts.map +1 -1
  232. package/dist/services/configuration/profiles.d.ts +4 -0
  233. package/dist/services/configuration/profiles.d.ts.map +1 -1
  234. package/dist/services/configuration/profiles.js +50 -0
  235. package/dist/services/configuration/profiles.js.map +1 -1
  236. package/dist/services/configuration.d.ts.map +1 -1
  237. package/dist/services/configuration.js.map +1 -1
  238. package/dist/services/connectors/google.d.ts +18 -1
  239. package/dist/services/connectors/google.d.ts.map +1 -1
  240. package/dist/services/connectors/google.js +27 -4
  241. package/dist/services/connectors/google.js.map +1 -1
  242. package/dist/services/connectors/oidc.d.ts +14 -2
  243. package/dist/services/connectors/oidc.d.ts.map +1 -1
  244. package/dist/services/connectors/oidc.js +35 -6
  245. package/dist/services/connectors/oidc.js.map +1 -1
  246. package/dist/services/connectors/types.d.ts +13 -1
  247. package/dist/services/connectors/types.d.ts.map +1 -1
  248. package/dist/services/connectors/zitadel.d.ts.map +1 -1
  249. package/dist/services/connectors/zitadel.js +4 -3
  250. package/dist/services/connectors/zitadel.js.map +1 -1
  251. package/dist/services/domain/authentication.d.ts +43 -4
  252. package/dist/services/domain/authentication.d.ts.map +1 -1
  253. package/dist/services/domain/authentication.js +119 -40
  254. package/dist/services/domain/authentication.js.map +1 -1
  255. package/dist/services/domain/authorization.d.ts +11 -0
  256. package/dist/services/domain/authorization.d.ts.map +1 -1
  257. package/dist/services/domain/authorization.js +36 -1
  258. package/dist/services/domain/authorization.js.map +1 -1
  259. package/dist/services/domain/identity.d.ts +21 -9
  260. package/dist/services/domain/identity.d.ts.map +1 -1
  261. package/dist/services/domain/identity.js +187 -10
  262. package/dist/services/domain/identity.js.map +1 -1
  263. package/dist/services/domain/organization.d.ts +11 -10
  264. package/dist/services/domain/organization.d.ts.map +1 -1
  265. package/dist/services/domain/organization.js +48 -30
  266. package/dist/services/domain/organization.js.map +1 -1
  267. package/dist/services/emailVerification.d.ts +2 -2
  268. package/dist/services/enterpriseSso.d.ts +13 -6
  269. package/dist/services/enterpriseSso.d.ts.map +1 -1
  270. package/dist/services/enterpriseSso.js +75 -6
  271. package/dist/services/enterpriseSso.js.map +1 -1
  272. package/dist/services/events/subscribers/auditLog.js +1 -1
  273. package/dist/services/events/subscribers/auditLog.js.map +1 -1
  274. package/dist/services/events/types.d.ts +7 -1
  275. package/dist/services/events/types.d.ts.map +1 -1
  276. package/dist/services/events/types.js.map +1 -1
  277. package/dist/services/events/validate.d.ts.map +1 -1
  278. package/dist/services/events/validate.js +33 -0
  279. package/dist/services/events/validate.js.map +1 -1
  280. package/dist/services/features.d.ts.map +1 -1
  281. package/dist/services/features.js.map +1 -1
  282. package/dist/services/federation.d.ts +6 -1
  283. package/dist/services/federation.d.ts.map +1 -1
  284. package/dist/services/federation.js +31 -4
  285. package/dist/services/federation.js.map +1 -1
  286. package/dist/services/ipControls.js +2 -2
  287. package/dist/services/ipControls.js.map +1 -1
  288. package/dist/services/magicLinks.d.ts +2 -2
  289. package/dist/services/magicLinks.d.ts.map +1 -1
  290. package/dist/services/magicLinks.js +13 -9
  291. package/dist/services/magicLinks.js.map +1 -1
  292. package/dist/services/mfa.d.ts +53 -0
  293. package/dist/services/mfa.d.ts.map +1 -0
  294. package/dist/services/mfa.js +157 -0
  295. package/dist/services/mfa.js.map +1 -0
  296. package/dist/services/oauth2.d.ts +88 -14
  297. package/dist/services/oauth2.d.ts.map +1 -1
  298. package/dist/services/oauth2.js +125 -16
  299. package/dist/services/oauth2.js.map +1 -1
  300. package/dist/services/oidcSecretFormat.d.ts +3 -0
  301. package/dist/services/oidcSecretFormat.d.ts.map +1 -0
  302. package/dist/services/oidcSecretFormat.js +5 -0
  303. package/dist/services/oidcSecretFormat.js.map +1 -0
  304. package/dist/services/organizations.d.ts +4 -16
  305. package/dist/services/organizations.d.ts.map +1 -1
  306. package/dist/services/organizations.js +0 -62
  307. package/dist/services/organizations.js.map +1 -1
  308. package/dist/services/permissions.d.ts.map +1 -1
  309. package/dist/services/permissions.js +9 -1
  310. package/dist/services/permissions.js.map +1 -1
  311. package/dist/services/plugins/registry.d.ts.map +1 -1
  312. package/dist/services/plugins/registry.js.map +1 -1
  313. package/dist/services/plugins/types.d.ts.map +1 -1
  314. package/dist/services/queue/bullmq.d.ts.map +1 -1
  315. package/dist/services/queue/inProcess.d.ts.map +1 -1
  316. package/dist/services/redirectUri.d.ts +56 -0
  317. package/dist/services/redirectUri.d.ts.map +1 -0
  318. package/dist/services/redirectUri.js +129 -0
  319. package/dist/services/redirectUri.js.map +1 -0
  320. package/dist/services/redis.d.ts +1 -1
  321. package/dist/services/redis.d.ts.map +1 -1
  322. package/dist/services/scimCredentials.d.ts +46 -0
  323. package/dist/services/scimCredentials.d.ts.map +1 -0
  324. package/dist/services/scimCredentials.js +123 -0
  325. package/dist/services/scimCredentials.js.map +1 -0
  326. package/dist/services/scimLegacyMigration.d.ts +12 -0
  327. package/dist/services/scimLegacyMigration.d.ts.map +1 -0
  328. package/dist/services/scimLegacyMigration.js +44 -0
  329. package/dist/services/scimLegacyMigration.js.map +1 -0
  330. package/dist/services/secrets/awsKms.d.ts +2 -2
  331. package/dist/services/secrets/awsKms.d.ts.map +1 -1
  332. package/dist/services/secrets/awsKms.js +3 -5
  333. package/dist/services/secrets/awsKms.js.map +1 -1
  334. package/dist/services/secrets/azureKeyVault.d.ts.map +1 -1
  335. package/dist/services/secrets/database.d.ts.map +1 -1
  336. package/dist/services/secrets/database.js.map +1 -1
  337. package/dist/services/secrets/environment.d.ts.map +1 -1
  338. package/dist/services/secrets/environment.js.map +1 -1
  339. package/dist/services/secrets/index.d.ts.map +1 -1
  340. package/dist/services/secrets/password.d.ts.map +1 -1
  341. package/dist/services/secrets/password.js +0 -4
  342. package/dist/services/secrets/password.js.map +1 -1
  343. package/dist/services/secrets/provider.d.ts +2 -3
  344. package/dist/services/secrets/provider.d.ts.map +1 -1
  345. package/dist/services/secrets/vault.d.ts.map +1 -1
  346. package/dist/services/serviceAccounts.d.ts +20 -0
  347. package/dist/services/serviceAccounts.d.ts.map +1 -1
  348. package/dist/services/serviceAccounts.js +47 -1
  349. package/dist/services/serviceAccounts.js.map +1 -1
  350. package/dist/services/sessionRevocation.d.ts +76 -0
  351. package/dist/services/sessionRevocation.d.ts.map +1 -0
  352. package/dist/services/sessionRevocation.js +86 -0
  353. package/dist/services/sessionRevocation.js.map +1 -0
  354. package/dist/services/setup/configWriter.d.ts.map +1 -1
  355. package/dist/services/setup/configWriter.js.map +1 -1
  356. package/dist/services/singleUse.d.ts +68 -0
  357. package/dist/services/singleUse.d.ts.map +1 -0
  358. package/dist/services/singleUse.js +54 -0
  359. package/dist/services/singleUse.js.map +1 -0
  360. package/dist/services/smsOtp.d.ts.map +1 -1
  361. package/dist/services/smsOtp.js +12 -8
  362. package/dist/services/smsOtp.js.map +1 -1
  363. package/dist/services/ssoEndpointPolicy.d.ts +8 -0
  364. package/dist/services/ssoEndpointPolicy.d.ts.map +1 -0
  365. package/dist/services/ssoEndpointPolicy.js +136 -0
  366. package/dist/services/ssoEndpointPolicy.js.map +1 -0
  367. package/dist/services/stepUp.d.ts +35 -0
  368. package/dist/services/stepUp.d.ts.map +1 -0
  369. package/dist/services/stepUp.js +65 -0
  370. package/dist/services/stepUp.js.map +1 -0
  371. package/dist/services/tokens.d.ts +39 -11
  372. package/dist/services/tokens.d.ts.map +1 -1
  373. package/dist/services/tokens.js +80 -14
  374. package/dist/services/tokens.js.map +1 -1
  375. package/dist/services/totp.d.ts +46 -0
  376. package/dist/services/totp.d.ts.map +1 -1
  377. package/dist/services/totp.js +178 -41
  378. package/dist/services/totp.js.map +1 -1
  379. package/dist/services/trustedProxies.d.ts +90 -0
  380. package/dist/services/trustedProxies.d.ts.map +1 -0
  381. package/dist/services/trustedProxies.js +283 -0
  382. package/dist/services/trustedProxies.js.map +1 -0
  383. package/dist/services/users.d.ts +20 -12
  384. package/dist/services/users.d.ts.map +1 -1
  385. package/dist/services/users.js +24 -51
  386. package/dist/services/users.js.map +1 -1
  387. package/dist/services/webauthn.d.ts +8 -4
  388. package/dist/services/webauthn.d.ts.map +1 -1
  389. package/dist/services/webauthn.js +4 -4
  390. package/dist/services/webauthn.js.map +1 -1
  391. package/dist/services/webhooks.d.ts +20 -20
  392. package/dist/services/workflows/engine.d.ts +4 -2
  393. package/dist/services/workflows/engine.d.ts.map +1 -1
  394. package/dist/services/workflows/engine.js +181 -12
  395. package/dist/services/workflows/engine.js.map +1 -1
  396. package/dist/services/workflows/steps.d.ts +2 -0
  397. package/dist/services/workflows/steps.d.ts.map +1 -1
  398. package/dist/services/workflows/steps.js +8 -75
  399. package/dist/services/workflows/steps.js.map +1 -1
  400. package/dist/setup-server.js +2 -1
  401. package/dist/setup-server.js.map +1 -1
  402. package/dist/types.d.ts +33 -4
  403. package/dist/types.d.ts.map +1 -1
  404. package/dist/types.js +26 -2
  405. package/dist/types.js.map +1 -1
  406. package/package.json +25 -3
  407. package/dist/tests/errors.test.d.ts +0 -2
  408. package/dist/tests/errors.test.d.ts.map +0 -1
  409. package/dist/tests/errors.test.js +0 -64
  410. package/dist/tests/errors.test.js.map +0 -1
  411. package/dist/tests/events.test.d.ts +0 -2
  412. package/dist/tests/events.test.d.ts.map +0 -1
  413. package/dist/tests/events.test.js +0 -45
  414. package/dist/tests/events.test.js.map +0 -1
  415. package/dist/tests/featureFlags.test.d.ts +0 -2
  416. package/dist/tests/featureFlags.test.d.ts.map +0 -1
  417. package/dist/tests/featureFlags.test.js +0 -24
  418. package/dist/tests/featureFlags.test.js.map +0 -1
  419. package/dist/tests/integration/queue.test.d.ts +0 -2
  420. package/dist/tests/integration/queue.test.d.ts.map +0 -1
  421. package/dist/tests/integration/queue.test.js +0 -49
  422. package/dist/tests/integration/queue.test.js.map +0 -1
  423. package/dist/tests/plugins.test.d.ts +0 -2
  424. package/dist/tests/plugins.test.d.ts.map +0 -1
  425. package/dist/tests/plugins.test.js +0 -56
  426. package/dist/tests/plugins.test.js.map +0 -1
  427. package/dist/tests/result.test.d.ts +0 -2
  428. package/dist/tests/result.test.d.ts.map +0 -1
  429. package/dist/tests/result.test.js +0 -46
  430. package/dist/tests/result.test.js.map +0 -1
  431. package/dist/tests/setup/configWriter.test.d.ts +0 -2
  432. package/dist/tests/setup/configWriter.test.d.ts.map +0 -1
  433. package/dist/tests/setup/configWriter.test.js +0 -55
  434. package/dist/tests/setup/configWriter.test.js.map +0 -1
  435. package/dist/tests/setup/validation.test.d.ts +0 -2
  436. package/dist/tests/setup/validation.test.d.ts.map +0 -1
  437. package/dist/tests/setup/validation.test.js +0 -26
  438. package/dist/tests/setup/validation.test.js.map +0 -1
  439. package/dist/tests/smoke.test.d.ts +0 -2
  440. package/dist/tests/smoke.test.d.ts.map +0 -1
  441. package/dist/tests/smoke.test.js +0 -124
  442. package/dist/tests/smoke.test.js.map +0 -1
  443. package/src/cli.ts +0 -172
  444. package/src/config.ts +0 -143
  445. package/src/container.ts +0 -48
  446. package/src/db/index.ts +0 -53
  447. package/src/db/migrations/0000_skinny_ben_parker.sql +0 -553
  448. package/src/db/migrations/0001_brief_joseph.sql +0 -10
  449. package/src/db/migrations/0002_add_account_lockout.sql +0 -3
  450. package/src/db/migrations/0003_steep_rachel_grey.sql +0 -1
  451. package/src/db/migrations/0004_clever_bill_hollister.sql +0 -20
  452. package/src/db/migrations/0005_steep_the_call.sql +0 -2
  453. package/src/db/migrations/0006_ambitious_boomer.sql +0 -17
  454. package/src/db/migrations/0007_greedy_vindicator.sql +0 -40
  455. package/src/db/migrations/meta/0000_snapshot.json +0 -2964
  456. package/src/db/migrations/meta/0001_snapshot.json +0 -3031
  457. package/src/db/migrations/meta/0003_snapshot.json +0 -3051
  458. package/src/db/migrations/meta/_journal.json +0 -62
  459. package/src/db/schema.ts +0 -676
  460. package/src/db/seed.ts +0 -146
  461. package/src/di.ts +0 -81
  462. package/src/index.ts +0 -326
  463. package/src/lib/errors.ts +0 -139
  464. package/src/lib/result.ts +0 -44
  465. package/src/lib/webhookSignature.ts +0 -41
  466. package/src/plugins/appContext.ts +0 -76
  467. package/src/plugins/audit.ts +0 -42
  468. package/src/plugins/auth.ts +0 -230
  469. package/src/plugins/metrics.ts +0 -41
  470. package/src/plugins/mtls.ts +0 -94
  471. package/src/plugins/permissions.ts +0 -29
  472. package/src/plugins/rateLimit.ts +0 -133
  473. package/src/plugins/requestLogger.ts +0 -25
  474. package/src/plugins/tracing.ts +0 -22
  475. package/src/repositories/apiKey.ts +0 -158
  476. package/src/repositories/application.ts +0 -70
  477. package/src/repositories/audit.ts +0 -32
  478. package/src/repositories/index.ts +0 -9
  479. package/src/repositories/oidcConnection.ts +0 -111
  480. package/src/repositories/organization.ts +0 -167
  481. package/src/repositories/permission.ts +0 -153
  482. package/src/repositories/samlConnection.ts +0 -96
  483. package/src/repositories/session.ts +0 -86
  484. package/src/repositories/types.ts +0 -134
  485. package/src/repositories/user.ts +0 -161
  486. package/src/routes/admin/billing.ts +0 -48
  487. package/src/routes/admin/helpers.ts +0 -50
  488. package/src/routes/admin/index.ts +0 -16
  489. package/src/routes/admin/organizations.ts +0 -299
  490. package/src/routes/admin/permissions.ts +0 -80
  491. package/src/routes/admin/platform.ts +0 -335
  492. package/src/routes/admin/sso.ts +0 -138
  493. package/src/routes/admin/webhooks.ts +0 -73
  494. package/src/routes/admin.ts +0 -1
  495. package/src/routes/apiKeys.ts +0 -60
  496. package/src/routes/auth.ts +0 -214
  497. package/src/routes/authz.ts +0 -38
  498. package/src/routes/config.ts +0 -67
  499. package/src/routes/emailVerification.ts +0 -73
  500. package/src/routes/federation.ts +0 -156
  501. package/src/routes/helpers.ts +0 -17
  502. package/src/routes/magicLinks.ts +0 -107
  503. package/src/routes/oauth.ts +0 -167
  504. package/src/routes/oauth2.ts +0 -284
  505. package/src/routes/oidcEnterprise.ts +0 -166
  506. package/src/routes/password.ts +0 -90
  507. package/src/routes/profile.ts +0 -44
  508. package/src/routes/saml.ts +0 -209
  509. package/src/routes/scim.ts +0 -199
  510. package/src/routes/sdk.ts +0 -141
  511. package/src/routes/serviceAccounts.ts +0 -124
  512. package/src/routes/sessions.ts +0 -62
  513. package/src/routes/setup.ts +0 -315
  514. package/src/routes/smsOtp.ts +0 -67
  515. package/src/routes/totp.ts +0 -122
  516. package/src/routes/webauthn.ts +0 -147
  517. package/src/routes/workflows.ts +0 -157
  518. package/src/sdk/index.ts +0 -152
  519. package/src/sdk/types.ts +0 -54
  520. package/src/services/anomalyDetection.ts +0 -73
  521. package/src/services/application/authentication.ts +0 -75
  522. package/src/services/application/identity.ts +0 -56
  523. package/src/services/application/index.ts +0 -3
  524. package/src/services/application/organization.ts +0 -77
  525. package/src/services/applications.ts +0 -89
  526. package/src/services/audit.ts +0 -41
  527. package/src/services/auditExport.ts +0 -59
  528. package/src/services/billing.ts +0 -87
  529. package/src/services/cache.ts +0 -131
  530. package/src/services/configuration/profiles.ts +0 -115
  531. package/src/services/configuration.ts +0 -120
  532. package/src/services/connectors/google.ts +0 -92
  533. package/src/services/connectors/oidc.ts +0 -111
  534. package/src/services/connectors/registry.ts +0 -124
  535. package/src/services/connectors/types.ts +0 -38
  536. package/src/services/connectors/zitadel.ts +0 -77
  537. package/src/services/devices.ts +0 -76
  538. package/src/services/domain/authentication.ts +0 -279
  539. package/src/services/domain/authorization.ts +0 -49
  540. package/src/services/domain/identity.ts +0 -108
  541. package/src/services/domain/index.ts +0 -4
  542. package/src/services/domain/organization.ts +0 -114
  543. package/src/services/email.ts +0 -167
  544. package/src/services/emailVerification.ts +0 -75
  545. package/src/services/enterpriseSso.ts +0 -58
  546. package/src/services/events/bus.ts +0 -43
  547. package/src/services/events/subscribers/anomaly.ts +0 -13
  548. package/src/services/events/subscribers/auditLog.ts +0 -21
  549. package/src/services/events/subscribers/webhook.ts +0 -39
  550. package/src/services/events/subscribers/webhookDispatch.ts +0 -14
  551. package/src/services/events/types.ts +0 -106
  552. package/src/services/events/validate.ts +0 -118
  553. package/src/services/featureFlags.ts +0 -90
  554. package/src/services/features.ts +0 -22
  555. package/src/services/federation.ts +0 -30
  556. package/src/services/hibp.ts +0 -28
  557. package/src/services/ipControls.ts +0 -99
  558. package/src/services/magicLinks.ts +0 -51
  559. package/src/services/oauth2.ts +0 -216
  560. package/src/services/organizations.ts +0 -171
  561. package/src/services/permissions.ts +0 -146
  562. package/src/services/plugins/registry.ts +0 -242
  563. package/src/services/plugins/types.ts +0 -98
  564. package/src/services/queue/bullmq.ts +0 -104
  565. package/src/services/queue/inProcess.ts +0 -75
  566. package/src/services/queue/index.ts +0 -19
  567. package/src/services/queue/types.ts +0 -26
  568. package/src/services/redis.ts +0 -16
  569. package/src/services/secrets/awsKms.ts +0 -97
  570. package/src/services/secrets/azureKeyVault.ts +0 -171
  571. package/src/services/secrets/database.ts +0 -166
  572. package/src/services/secrets/environment.ts +0 -117
  573. package/src/services/secrets/index.ts +0 -50
  574. package/src/services/secrets/password.ts +0 -73
  575. package/src/services/secrets/provider.ts +0 -36
  576. package/src/services/secrets/vault.ts +0 -149
  577. package/src/services/secrets.ts +0 -2
  578. package/src/services/serviceAccounts.ts +0 -74
  579. package/src/services/setup/configWriter.ts +0 -200
  580. package/src/services/setup/diagnostics.ts +0 -76
  581. package/src/services/setup/token.ts +0 -42
  582. package/src/services/setup/types.ts +0 -40
  583. package/src/services/setup/validation.ts +0 -205
  584. package/src/services/sms.ts +0 -81
  585. package/src/services/smsOtp.ts +0 -74
  586. package/src/services/tokens.ts +0 -287
  587. package/src/services/totp.ts +0 -113
  588. package/src/services/users.ts +0 -239
  589. package/src/services/webauthn.ts +0 -193
  590. package/src/services/webhooks.ts +0 -190
  591. package/src/services/workflows/engine.ts +0 -125
  592. package/src/services/workflows/steps.ts +0 -129
  593. package/src/services/zitadel.ts +0 -277
  594. package/src/setup-server.ts +0 -71
  595. package/src/tests/errors.test.ts +0 -74
  596. package/src/tests/events.test.ts +0 -52
  597. package/src/tests/featureFlags.test.ts +0 -26
  598. package/src/tests/integration/queue.test.ts +0 -54
  599. package/src/tests/plugins.test.ts +0 -73
  600. package/src/tests/result.test.ts +0 -52
  601. package/src/tests/setup/configWriter.test.ts +0 -69
  602. package/src/tests/setup/validation.test.ts +0 -29
  603. package/src/tests/smoke.test.ts +0 -136
  604. package/src/types.ts +0 -70
package/CHANGELOG.md CHANGED
@@ -5,6 +5,630 @@ All notable changes to Hilbras Keystone are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.5.0] - 2026-09-27
9
+
10
+ ### Security
11
+
12
+ - **The OIDC nonce never reached the Google connector.**
13
+ `GoogleConnector.exchangeCode` overrode the base method and called
14
+ `super.exchangeCode(code, redirectUri)` without forwarding its options, so the
15
+ nonce added in 2.4.0 was discarded. Every other OIDC provider validated the
16
+ nonce; Google — the default, and therefore the most likely to be deployed — did
17
+ not. An ID token minted for a different user or session would have been
18
+ accepted on that path.
19
+ - **The unsigned SAML `Issuer` was never validated.** The response-level
20
+ `<saml:Issuer>` sits outside both signed regions, so rewriting it does not
21
+ invalidate the signature, and neither samlify nor Keystone compared it to the
22
+ registered IdP. SAML 2.0 §2.5.1.5 requires a relying party to verify an
23
+ unsigned issuer against trusted metadata. An assertion could claim to have been
24
+ issued by a different identity provider. The assertion's own issuer is inside
25
+ the signed region and was always covered; this closes the element the signature
26
+ cannot.
27
+
28
+ ### Fixed
29
+
30
+ - `verifyRelayState` returned by throwing on a missing or non-string signature,
31
+ turning a malformed RelayState — a bad request an attacker fully controls —
32
+ into a 500 rather than a 400. It now returns false for anything malformed.
33
+ - A missing `userinfoEndpoint` was passed to the fetcher behind a non-null
34
+ assertion, producing `userinfoEndpoint must be a valid URL` for a URL that was
35
+ never configured. It is now reported as unconfigured so enrichment is skipped.
36
+
37
+ ### Added
38
+
39
+ - 24 adversarial SAML tests covering tampered signatures, untrusted signing keys,
40
+ rotated-out certificates, unsigned assertions, XML signature wrapping, issuer
41
+ and audience substitution, destination and recipient prefix / superstring / case
42
+ variants, expired assertions, `NotBefore` violations, `InResponseTo` mismatch,
43
+ transaction replay, and five RelayState tampering scenarios.
44
+ - 10 tests for OIDC userinfo endpoint resolution, Google nonce forwarding, and
45
+ organization-scoped membership.
46
+ - `docs/security/enterprise-sso.md` — SAML and OIDC setup, every check applied to
47
+ an assertion or ID token, certificate rotation, endpoint SSRF policy,
48
+ organization scoping, and recommendations.
49
+
50
+ ### Already sound, verified rather than assumed
51
+
52
+ Membership is keyed on `(orgId, userId)` throughout, with a unique constraint on
53
+ that pair and provisioning via `ON CONFLICT DO NOTHING` — so two simultaneous
54
+ logins cannot create duplicate memberships. SAML connections are resolved by
55
+ `(connectionId, orgId)`, the transaction is consumed atomically for replay
56
+ protection, RelayState is HMAC-signed and bound to a browser nonce compared in
57
+ constant time, and assertions and messages are both required to be signed. A test
58
+ now pins the membership behaviour rather than leaving it to inspection.
59
+
60
+ ## [2.4.0] - 2026-09-27
61
+
62
+ ### Security
63
+
64
+ - **The `authorization_code` grant did not authenticate the client.** It looked
65
+ the application up by `client_id` and went straight to redeeming the code,
66
+ never calling `verifyClientSecret`. RFC 6749 §3.2.1 requires a confidential
67
+ client to authenticate at the token endpoint. The code and its PKCE verifier
68
+ were the only factors, so an intercepted code was redeemable by whoever
69
+ intercepted it. Confidential clients must now present `client_secret`; public
70
+ clients are exempt because they have none, and PKCE is what authenticates them.
71
+ - **Redirect URIs accepted script-bearing schemes.** Registration validated with
72
+ `z.string().url()`, which accepts anything the URL parser accepts — verified to
73
+ include `javascript:alert(1)` and
74
+ `data:text/html,<script>alert(1)</script>`. A redirect URI becomes a `Location`
75
+ header that the identity provider itself emits, so an organization admin could
76
+ register one and hand any user who authorized their application a redirect
77
+ toward script execution on the auth domain. Browser policy against top-level
78
+ `javascript:` navigation limits the practical impact, but on an identity
79
+ provider this is not an acceptable input. Registration now also rejects
80
+ wildcards, fragments, embedded credentials, and plaintext HTTP to non-loopback
81
+ hosts. A test records that `z.string().url()` accepted each of these.
82
+ - **OIDC federation sent no nonce and verified none.** `state` proved the callback
83
+ belonged to a login this browser started, but nothing bound the returned ID
84
+ token to that login. Any ID token the provider considered valid was accepted,
85
+ including one minted for a different user or session. A nonce is now generated
86
+ per authorization request, kept in an httpOnly cookie, sent to the provider, and
87
+ required to match.
88
+ - **ID token verification inferred rather than required.** Algorithms are now
89
+ pinned to RS256/ES256/PS256 instead of being derived from the key material, and
90
+ `exp`, `iat`, `iss`, `aud`, `sub` are required rather than validated only when
91
+ present — a token with no expiry was previously accepted indefinitely.
92
+
93
+ ### Changed
94
+
95
+ - **Effective scopes are intersected, not trusted.** The client's `scope`
96
+ parameter was stored verbatim, with consent as the only filter. The effective
97
+ set is now registered ∩ requested ∩ consented, and a scope outside the
98
+ registration is refused with `invalid_scope` rather than silently dropped, so a
99
+ client asking for authority it was never granted is visible instead of quietly
100
+ downgraded. An empty `allowed_scopes` preserves existing behaviour.
101
+ - **Public clients.** A `client_type` column distinguishes `confidential` from
102
+ `public`; a public client is issued no secret rather than a secret it is
103
+ expected to ignore, and a check constraint keeps the two halves consistent.
104
+ `client_secret_hash` is now nullable. PKCE is mandatory for a secretless client
105
+ at both `/authorize` and `/token`; the `verifyPKCE` branch that returned true
106
+ when no challenge was registered is gone.
107
+ - **Redirect URIs are compared with one shared helper** at registration and at
108
+ use, so the two cannot drift. Exact string comparison throughout — no prefix
109
+ matching, no normalization, no case folding. The token endpoint's dead
110
+ `redirect_uri IS NULL` tolerance was removed: `redirect_uri` is required at
111
+ `/authorize`, so the branch was unreachable, and it would have accepted any
112
+ redirect URI had the field ever become optional.
113
+ - **Refresh tokens carry the granted scope set** in a new `scopes` column, so the
114
+ authorization context survives rotation instead of being dropped at the first
115
+ refresh. A refresh may narrow the grant but never widen it.
116
+ - **PKCE comparison is constant-time**, so a verifier cannot be recovered byte by
117
+ byte.
118
+
119
+ ### Already sound, verified rather than assumed
120
+
121
+ Authorization code consumption was already atomic — a conditional `UPDATE` with
122
+ `used_at IS NULL` and a required returned row — and is now covered by a
123
+ concurrency test (20 parallel redemptions, exactly one winner). PKCE was already
124
+ required at `/authorize` by the request schema, so the dead branch in
125
+ `verifyPKCE` was a latent weakness rather than a live bypass. The refresh grant
126
+ already validated client binding, MFA context, and organization membership.
127
+
128
+ ### Added
129
+
130
+ - 42 tests: redirect URI registration and exact matching, PKCE verification,
131
+ scope intersection, client authentication at the token endpoint, public client
132
+ invariants, atomic code consumption, ID token verification against a locally
133
+ signed key (missing / wrong / replayed / expired nonce, no-expiry, wrong issuer,
134
+ wrong audience, foreign signing key), and scope preservation across rotation.
135
+
136
+ ## [2.3.0] - 2026-09-27
137
+
138
+ ### Security
139
+
140
+ **Completing a password reset did not remove existing access.** It changed the
141
+ password and left every session, every refresh token, and every other
142
+ outstanding reset token working. A password reset is the standard response to a
143
+ suspected compromise, so the previous behaviour defeated its own purpose: an
144
+ attacker who triggered the reset kept their session and kept their access, while
145
+ the victim believed they had locked the intruder out.
146
+
147
+ - A successful reset now invalidates all sessions, all refresh tokens, and all
148
+ outstanding recovery credentials for the account.
149
+ - Reset tokens issued alongside the one used are now spent, so a reset email
150
+ captured earlier cannot be completed after the user has already recovered.
151
+ - Revocation is centralized in `src/services/sessionRevocation.ts`
152
+ (`revokeUserSessions`, `revokeRefreshTokens`,
153
+ `revokeAuthenticationSessions`, `revokeRecoveryCredentials`). It was
154
+ previously open-coded at each call site, which is how the most important site
155
+ came to omit it. The MFA-enablement path now routes through the same function,
156
+ so the rule cannot drift between the two.
157
+ - Revocation is scoped to one user, is idempotent, and honours an exclusion for
158
+ a change the user makes to their own account.
159
+
160
+ API keys are deliberately **not** revoked by a password reset. They are
161
+ separately issued, long-lived credentials belonging to integrations rather than
162
+ to the person, and killing them silently breaks deployments. The residual gap is
163
+ real — a key minted by an attacker who already held the password survives — and
164
+ key expiry and rotation is the right answer rather than coupling key lifetime to
165
+ a human's password.
166
+
167
+ ### Already sound, verified rather than assumed
168
+
169
+ - Recovery credentials are 384-bit `crypto.randomBytes`, stored only as a SHA-256
170
+ digest, single-use (since 2.2.0), valid for one hour, rate-limited to 5 per 15
171
+ minutes, and audited.
172
+ - `POST /auth/forgot-password` returns `{ success: true }` on both the found and
173
+ not-found paths, so the response does not disclose whether an account exists.
174
+ (A residual timing difference remains, since the found path sends mail.)
175
+
176
+ ### Added
177
+
178
+ - 9 tests, including that an attacker's session and refresh token do not survive
179
+ a reset, that an intercepted earlier reset token is dead, that a bystander's
180
+ credentials are untouched, and that the recovered user can still log in.
181
+
182
+ ## [2.2.0] - 2026-09-26
183
+
184
+ ### Security
185
+
186
+ Three single-use credentials were validated with a conditional `SELECT` and then
187
+ marked used with an **unconditional** `UPDATE`:
188
+
189
+ ```text
190
+ SELECT ... WHERE used_at IS NULL <- conditional
191
+ if (!row) return
192
+ UPDATE ... SET used_at = now() <- UNCONDITIONAL: the race
193
+ ```
194
+
195
+ Between those two statements, any number of concurrent requests pass the same
196
+ check. Every one of them then succeeds.
197
+
198
+ - **Magic links** could be redeemed by any number of parallel requests, each
199
+ producing a full login. A link that was meant to be usable once was usable
200
+ indefinitely under concurrency.
201
+ - **Password reset tokens** could be spent by parallel requests, each writing a
202
+ different password, last writer winning. This was the most consequential of
203
+ the three: whoever won the race held the account, and an attacker racing the
204
+ legitimate user could take it over.
205
+ - **SMS OTP codes** could be verified more than once concurrently, so a
206
+ six-digit code was not single-use.
207
+
208
+ The fix is to make the write the gate rather than a follow-up:
209
+
210
+ ```text
211
+ UPDATE ... SET used_at = now()
212
+ WHERE token_hash = ? AND expires_at > now() AND used_at IS NULL
213
+ RETURNING ...
214
+ ```
215
+
216
+ PostgreSQL evaluates that predicate while holding a row lock, so exactly one
217
+ transaction updates the row and observes a returned row. The claim and the
218
+ validation become one statement with no window between them.
219
+
220
+ The four other single-use credentials in scope were already atomic and were
221
+ verified rather than assumed: refresh token rotation, MFA challenges, OAuth2
222
+ authorization codes, and TOTP backup codes all perform a conditional update and
223
+ require a returned row.
224
+
225
+ ### Added
226
+
227
+ - `src/services/singleUse.ts` — one atomic claim and refusal-classification
228
+ primitive, used by all three credentials. Consumption now lives in one place,
229
+ so a credential cannot drift back into a hand-rolled read-then-write.
230
+ - Replay detection. A credential presented after it was already spent now emits
231
+ `magic_link_replayed`, `sms_otp_replayed`, or
232
+ `password_reset_token_replayed`, all of which reach the audit log through the
233
+ event bus. Previously a replay was indistinguishable from a typo, so a leaked
234
+ token returning was invisible to an operator. An **expired** credential is
235
+ deliberately not reported as a replay, because that is not a leak.
236
+ - 25 tests, including the plan's 10 / 50 / 100 concurrent-request levels against
237
+ every affected credential, and the same levels through the service entry points
238
+ a route actually calls.
239
+
240
+ ### Changed
241
+
242
+ - `resetPasswordWithToken` now spends the token before doing any work, and
243
+ reports an expired link distinctly from an invalid one. Spending other live
244
+ reset tokens for the same user after a successful reset, since any were issued
245
+ alongside the one just used.
246
+
247
+ ## [2.1.0] - 2026-09-26
248
+
249
+ ### Security
250
+
251
+ Four moderate advisories in the development tree, from `drizzle-kit` pulling
252
+ `@esbuild-kit/esm-loader`, which pinned its own copy of `esbuild@0.18.20`
253
+ (`GHSA-67mh-4wv8-2f99`, fixed in 0.25.0).
254
+
255
+ The advisory allows a website to send requests to an esbuild **dev server** and
256
+ read the response. Keystone never calls esbuild's `serve()` API, the packages
257
+ are `devDependencies`, and `npm audit --omit=dev` was already clean, so this was
258
+ not exploitable here. It was still a real advisory in the tree that builds and
259
+ publishes the artifact, and `npm audit fix --force` offered only a downgrade of
260
+ `drizzle-kit` to 0.18.1, which is a breaking change.
261
+
262
+ Resolved with an `overrides` entry forcing `esbuild >= 0.25.0`, which collapses
263
+ all three copies to 0.28.2 and clears the audit for production and development
264
+ trees alike. Verified that `db:generate`, `db:migrate`, and `db:seed` all still
265
+ work against the forced version, and that no non-dev package resolves esbuild.
266
+
267
+ ### Fixed
268
+
269
+ - The published container image shipped 8 HIGH-severity advisories that no
270
+ JavaScript scanner can detect. `npm audit` and OSV both read
271
+ `package-lock.json` and correctly reported zero, because the vulnerable
272
+ packages are not in Keystone's dependency tree: they are the ones bundled
273
+ inside the base image's `npm@10.9.9` (`brace-expansion@2.0.2`,
274
+ `ip-address@10.1.0`, `pacote@19.0.2`, `picomatch@4.0.3`, `sigstore@3.1.0`).
275
+ The runtime image never invokes npm — `CMD` is `node dist/index.js`, and the
276
+ development compose override builds the `builder` target, which keeps its own
277
+ npm — so it is now removed from the production stage. This clears all 8 and
278
+ reduces the image from 600 MB to 550 MB.
279
+
280
+ Only container scanning finds this class of problem, which is why the gate
281
+ exists. Keystones own `brace-expansion@5.0.12` is already above the fixed
282
+ version and was never affected.
283
+
284
+ ### Added
285
+
286
+ - `.github/dependabot.yml` — weekly updates for npm (root and frontend), GitHub
287
+ Actions, and Docker. Routine patches are grouped; security updates are not, so
288
+ a compromised package lands alone and identifiable.
289
+ - `.github/workflows/supply-chain.yml` — OSV scanning (independent advisory
290
+ source from npm's), enforced `npm audit` over both trees, dependency review on
291
+ pull requests, SBOM generation, container scanning, and a license gate.
292
+ - `npm run verify:release` — fails on a version that disagrees between
293
+ `package.json` and the lockfile, a dependency in one and not the other, a
294
+ missing or malformed license, or a missing `repository` field. Wired into both
295
+ CI and the release workflow so a bad artifact cannot be published.
296
+
297
+ ### Fixed
298
+
299
+ - `package.json` declared no `license` field, despite shipping an MIT `LICENSE`
300
+ file. The published package carried no machine-readable terms.
301
+
302
+ ### Changed
303
+
304
+ - Fastify is at 5.12.5 and `fast-uri` resolves to 3.1.8, both already above the
305
+ 5.12.2 / 3.1.7 targets. No upgrade was required.
306
+ - The license allowlist permits only permissive terms, with an explicit
307
+ exception for the pre-SPDX `MIT*` identifier that older packages emit.
308
+
309
+ ## [2.0.0] - 2026-09-26
310
+
311
+ ### Security
312
+
313
+ The mTLS trust boundary trusted whatever the request said about itself. Any
314
+ client that could reach Keystone could name a service account in a header and
315
+ become it, and could set its own IP address to escape every rate limit. This
316
+ release makes identity come only from values a client cannot forge.
317
+
318
+ - **`x-service-account-id` no longer authenticates.** It previously resolved a
319
+ service account on its own, with no certificate and no credential, so anyone
320
+ who knew or guessed an account ID became that account. It is now read only as a
321
+ hint alongside a valid certificate, and only when the account it names is the
322
+ one that certificate is bound to. A mismatch is refused, not fallen back from.
323
+ - **Client identity is bound to a certificate fingerprint.** A new unique
324
+ `service_accounts.cert_fingerprint` column pins a SHA-256 fingerprint to
325
+ exactly one account. Fingerprints are stored canonicalized, so the hex and
326
+ colon-separated spellings of one certificate cannot become two bindings, and
327
+ malformed values are rejected before reaching the database. Service accounts
328
+ are resolved by fingerprint rather than by their operator-chosen name.
329
+ - **Identity headers are stripped from untrusted peers.** An `onRequest` hook
330
+ registered before every plugin and route removes
331
+ `x-forwarded-client-cert`, `x-client-cert-fingerprint`,
332
+ `x-forwarded-client-cert-chain`, `x-service-account-id`, `x-forwarded-for`,
333
+ `x-real-ip`, and `forwarded` unless the peer is a configured trusted proxy.
334
+ Stripping rather than ignoring means a route added later cannot read a
335
+ spoofed identity by accident.
336
+ - **Rate limits can no longer be escaped.** The server was created with
337
+ `trustProxy: true` and the limiter read `x-forwarded-for` unconditionally, so
338
+ any client could present a fresh address per request and never be limited —
339
+ including against login, password reset, MFA verification, and SCIM. Limit keys
340
+ now come from the peer address unless a trusted proxy forwarded one.
341
+ - **Trust decisions do not use `request.ip`.** With `trustProxy` enabled that
342
+ value is derived from the attacker-controlled header, so the trusted-proxy
343
+ check uses the socket peer address, the only value a client cannot set.
344
+ - **Forwarded values are validated before use.** Certificate headers are
345
+ length-capped, and a fingerprint must be a well-formed SHA-256 digest, so
346
+ garbage cannot be used as a lookup key.
347
+ - **Inactive and revoked service accounts cannot authenticate by certificate.**
348
+
349
+ ### Added
350
+
351
+ - `KEYSTONE_TRUSTED_PROXIES` — comma-separated proxy IPs, IPv4 CIDRs, or IPv6
352
+ prefixes permitted to set client-identity headers. Unset by default, which
353
+ trusts nothing. IPv4-mapped IPv6 peers are normalized before matching, and
354
+ unrecognized input fails closed.
355
+ - `PUT /v1/admin/organizations/:id/service-accounts/:accountId/certificate` —
356
+ bind or clear a client-certificate fingerprint, auditing
357
+ `service_account_certificate_bound` / `service_account_certificate_unbound`.
358
+ A certificate already held by another account returns `409`.
359
+ - `POST /v1/admin/organizations/:id/service-accounts/:accountId/revoke` —
360
+ permanently stop an account authenticating, auditing
361
+ `service_account_revoked`. Revoking an already-revoked account returns `409`
362
+ rather than a silent success.
363
+ - `docs/security/trust-boundaries.md`, `docs/security/proxy-security.md`, and
364
+ `docs/security/mtls.md` — the trust model, proxy requirements with working
365
+ nginx and ALB configuration, and the mTLS identity rules.
366
+ - `docs/MIGRATION-2.0.md` — migration instructions, including the failure mode
367
+ that presents as unrelated clients sharing a rate-limit budget.
368
+
369
+ ### Fixed
370
+
371
+ - The SCIM token-hash unique indexes introduced in 1.9.0 were declared in the
372
+ Drizzle schema but never emitted as a migration, so they did not exist in any
373
+ deployed database. They are created by migration `0015`.
374
+ - The documented nginx configuration used `$proxy_add_x_forwarded_for`, which
375
+ appends to a client-supplied value and lets a client prepend a forged address.
376
+ Corrected to `$remote_addr`, with inbound identity headers stripped.
377
+
378
+ ### Changed
379
+
380
+ - `trustProxy` is derived from `KEYSTONE_TRUSTED_PROXIES` instead of being
381
+ unconditionally `true`.
382
+ - `requireMTLS` distinguishes an untrusted peer (`401 MTLS_UNTRUSTED_PEER`) from
383
+ a missing certificate (`401 MTLS_CERTIFICATE_MISSING`), so a misconfiguration
384
+ is distinguishable from an attack.
385
+
386
+ ### Breaking
387
+
388
+ - mTLS clients that authenticated with `x-service-account-id` alone must bind a
389
+ certificate fingerprint instead.
390
+ - Deployments behind a reverse proxy must set `KEYSTONE_TRUSTED_PROXIES`.
391
+ Without it, forwarded headers are stripped and every client shares one
392
+ rate-limit budget, so unrelated users can rate-limit each other.
393
+
394
+ ## [1.9.0] - 2026-09-25
395
+
396
+ ### Security
397
+
398
+ SCIM was a single global credential. In 1.8.x a deployment could provision
399
+ exactly one organization, the bearer token was stored and compared in plaintext,
400
+ and mutations reached global user records.
401
+
402
+ - SCIM credentials are per-organization. Every connection belongs to exactly one
403
+ organization, at most one is live per organization, and every user and group
404
+ read and write is filtered by it. A cross-tenant target returns `404`, so the
405
+ endpoint is not a tenant oracle.
406
+ - Bearer tokens are stored only as a SHA-256 digest and resolved by that digest,
407
+ so a database dump yields no usable token and the comparison carries no timing
408
+ signal. Connections can expire, be rotated, and be revoked.
409
+ - Issuing, rotating, and revoking a SCIM credential is owner-only. A SCIM token
410
+ provisions and deactivates tenant users, so a mere admin or member cannot mint
411
+ one.
412
+ - Deprovisioning removes the organization's membership and deactivates the
413
+ account only when no membership remains. Previously it deactivated a shared
414
+ account in every organization that user belonged to, without those
415
+ organizations' authorization.
416
+ - SCIM refuses to change the global attributes of a shared user, to reactivate a
417
+ shared account, and to remove the last owner of an organization.
418
+ - SCIM can no longer attach a user who already belongs to another organization,
419
+ and the conflict message no longer names the organization that holds them.
420
+ - Unauthenticated SCIM traffic is budgeted before the authentication hook emits
421
+ audit and webhook events, closing an unauthenticated write-amplification path.
422
+ - SCIM request budgets are keyed per credential, so one noisy identity provider
423
+ cannot exhaust every other tenant's allowance.
424
+
425
+ ### Changed
426
+
427
+ - `SCIM_BEARER_TOKEN` and `SCIM_ORG_ID` are deprecated. They are adopted once
428
+ into a connection at startup and then ignored, so an upgrade does not break an
429
+ existing identity provider. Adoption is one-time in any state, so a restart
430
+ cannot resurrect a revoked credential.
431
+ - Deprovisioning a single-tenant user now removes the membership as well as
432
+ deactivating the account. Deprovisioning a shared user removes only the
433
+ membership, and a follow-up `DELETE` returns `404`.
434
+ - `POST /scim/v2/Users` is create-or-update; profile fields for an existing
435
+ member are applied rather than dropped.
436
+ - A user removed from an organization can be re-provisioned. Previously the
437
+ global email lookup found the inactive row and returned `409` forever.
438
+ - Rotating a credential revokes the previous token immediately
439
+ (`SCIM_ROTATION_GRACE_SECONDS` defaults to `0`). A grace window is now an
440
+ explicit opt-in and is not a revocation mechanism.
441
+ - Groups are real organization-scoped records. The synthetic role-bucket
442
+ projection (`<orgId>:<role>` ids) is removed.
443
+ - Malformed path ids return a SCIM `404` instead of surfacing a driver error, and
444
+ validation and internal failures return SCIM `Error` objects.
445
+
446
+ ### Added
447
+
448
+ - `scim_connections` with rotation, revocation, expiry, and a token hint.
449
+ - `scim_groups` and `scim_group_members`, organization-scoped throughout.
450
+ - `PATCH /scim/v2/Users/:userId` and `POST /scim/v2/Users/.search`.
451
+ - Group create, replace, patch, delete, and member management endpoints.
452
+ - `filter`, `startIndex`, and `count` on list endpoints; unsupported filters are
453
+ rejected rather than silently ignored.
454
+ - `GET /scim/v2/ServiceProviderConfig` and `GET /scim/v2/ResourceTypes`.
455
+ - Owner-only admin API for creating, listing, rotating, and revoking SCIM
456
+ connections, with a dashboard UI for the same.
457
+ - `findByIdInOrg`, `updateInOrg`, `listOrgIdsForUser`, and `removeFromOrg`
458
+ repository methods for organization-scoped user access.
459
+ - Audit events `scim_connection_created`, `scim_connection_rotated`,
460
+ `scim_connection_revoked`, `scim_access_denied`, `scim_authentication_failed`,
461
+ and the `scim_group_*` group events.
462
+ - `docs/MIGRATION-1.9.md`.
463
+
464
+ ## [1.8.0] - 2026-09-25
465
+
466
+ ### Security
467
+
468
+ MFA was advisory in 1.7.x. A user with TOTP enabled could sign in with only a
469
+ password, and when a code was supplied it was verified *after* the access token,
470
+ refresh token, and session had already been created. 1.8.0 makes the second
471
+ factor mandatory.
472
+
473
+ - Password authentication now stops at `requires_mfa` for MFA-enabled accounts.
474
+ No access token, refresh token, or session row is created at that stage.
475
+ - New `POST /auth/mfa/verify` completes the transition. The challenge is opaque,
476
+ stored only as a hash, short-lived, single-use, and bounded by an attempt
477
+ budget enforced in the database.
478
+ - Token issuance is guarded at a single chokepoint. A token cannot be minted for
479
+ an MFA-enabled user without a recorded factor, so no login path bypasses MFA by
480
+ omission.
481
+ - TOTP verification uses the user's own decrypted secret. Each time-step is
482
+ accepted exactly once, so a captured code is rejected even against a freshly
483
+ issued challenge.
484
+ - Enabling MFA revokes every existing refresh token and session for the account.
485
+ Sessions record how MFA was satisfied, and refresh rotation refuses sessions
486
+ with no recorded factor.
487
+ - Backup codes carry 80 bits of entropy, are stored as a keyed (peppered) hash,
488
+ expire after 90 days, and are consumed by a conditional update so concurrent
489
+ use has exactly one winner.
490
+ - TOTP secrets are written with AES-256-GCM. Values written by earlier versions
491
+ used AES-256-CBC and remain readable.
492
+
493
+ ### Changed
494
+
495
+ - `POST /auth/login` and `POST /auth/token-login` return `401` with
496
+ `code: "MFA_REQUIRED"` and a challenge when MFA is required. See
497
+ [MIGRATION-1.8.md](docs/MIGRATION-1.8.md).
498
+ - The undocumented `totp_code` field on the login endpoints is removed and
499
+ ignored.
500
+ - `POST /auth/totp/backup` now regenerates backup codes and requires a current
501
+ TOTP code. `POST /auth/totp/backup/verify` consumes a backup code.
502
+ - `POST /auth/totp/verify` additionally reports `sessionsRevoked`.
503
+ - WebAuthn assertions satisfy MFA on their own. Magic links refuse to downgrade a
504
+ TOTP-protected account, and SAML, enterprise OIDC, federation, and OAuth2
505
+ report a typed `mfa_required` error.
506
+ - OAuth2 authorization codes carry the MFA factor of the session that approved
507
+ them, so the token exchange cannot launder an unverified login.
508
+ - Access tokens for MFA sessions carry `mfa_verified`, `mfa_factor`, and `amr`.
509
+ - Factor management requires step-up: `/auth/totp/enroll`, `/auth/totp/verify`,
510
+ `/auth/totp/backup`, `/auth/totp/disable`, and passkey registration for a
511
+ TOTP-protected account all require the account password in addition to the
512
+ session.
513
+ - A passkey registered after TOTP was enabled is treated as a single factor and
514
+ cannot be used to sign in on its own.
515
+ - Disabling TOTP deletes its backup codes.
516
+ - Failed MFA factor attempts count toward the account lockout.
517
+ - `SDK.authentication.login()` returns a discriminated union; `completeMfa()` is
518
+ new.
519
+
520
+ ### Fixed
521
+
522
+ - `/auth/mfa/verify` wrote session cookies under a name derived from the login
523
+ flow instead of the client id, so MFA-completed sessions were not readable by
524
+ the auth plugin and every application on the cookie domain shared one name.
525
+ - The MFA step no longer accepted accounts that are deactivated, under review,
526
+ or locked out, which the password step already refused.
527
+ - Repeated password steps no longer cancel an MFA challenge created moments
528
+ earlier.
529
+ - `MFA_CHALLENGE_TTL_SECONDS`, `MFA_MAX_ATTEMPTS`, and
530
+ `TOTP_BACKUP_CODE_TTL_SECONDS` are validated at startup and fall back to their
531
+ defaults instead of silently breaking every login.
532
+ - The MFA factor copied out of a verified token into the authorization-code
533
+ table is validated against the column's check constraint.
534
+ - SAML now reports `mfa_required` for MFA-protected accounts instead of
535
+ collapsing the failure into a generic validation error.
536
+
537
+ ### Added
538
+
539
+ - `mfa_challenges` table, `MfaChallengeRepository`, and `MfaService`.
540
+ - `MFA_CHALLENGE_TTL_SECONDS`, `MFA_MAX_ATTEMPTS`, and
541
+ `TOTP_BACKUP_CODE_TTL_SECONDS` configuration.
542
+ - Audit events `mfa_challenge_created`, `mfa_challenge_failed`,
543
+ `mfa_challenge_expired`, `mfa_challenge_rejected`, `mfa_verified`,
544
+ `mfa_bypass_blocked`, and `mfa_backup_code_regenerated`.
545
+ - Dedicated rate limits for MFA verification and every TOTP management endpoint.
546
+ - MFA challenge step in the admin dashboard login form.
547
+ - `docs/MIGRATION-1.8.md`.
548
+ - MFA security regression suite (`src/tests/security/mfa.test.ts`).
549
+
550
+ ## [1.7.0] - 2026-09-24
551
+
552
+ ### Added
553
+
554
+ - Dedicated owner-only platform-role endpoint at `PATCH /v1/admin/platform/users/:userId/role`.
555
+ - Centralized platform and organization authorization guards with explicit organization context.
556
+ - Versioned audit events for platform-role, membership, permission, and denied-authorization transitions.
557
+ - Dedicated authorization regression suite covering privilege escalation, tenant isolation, workflow safety, secret disclosure, and audit metadata.
558
+ - RBAC and authorization-boundary documentation.
559
+
560
+ ### Changed
561
+
562
+ - Platform roles are explicitly limited to `owner` and `user`.
563
+ - Organization roles are explicitly limited to `owner`, `admin`, and `member`.
564
+ - Authorization checks now require an explicit `organizationId`.
565
+ - Frontend administration clients use the dedicated platform-role endpoint and safe workflow definitions.
566
+ - SAML metadata lookups require both connection and organization identifiers.
567
+
568
+ ### Fixed
569
+
570
+ - Organization user routes can no longer mutate global users or deactivate shared accounts.
571
+ - Generic profile and in-process identity contracts can no longer carry a platform role.
572
+ - Organization admins cannot promote themselves or other members to organization owner.
573
+ - The sole organization owner cannot be demoted or removed.
574
+ - Tenant workflows now use a closed safe-step allowlist; plugin aliases, arbitrary webhooks, organization creation, and authorization-mutating steps fail closed.
575
+ - Global workflows require platform-owner access, and workflow execution rechecks organization membership.
576
+ - Organization creation always assigns an owner; actorless global deactivation APIs were removed from the organization domain.
577
+ - Last-owner transitions use database row locks to prevent concurrent demotion/removal.
578
+ - Platform-user deactivation now disables login, invalidates existing sessions, revokes refresh tokens and user API keys, and preserves the last active owner invariant.
579
+ - User-management responses redact application secret hashes, OIDC/API-key credentials, configuration values, password hashes, TOTP secrets, and metadata.
580
+ - SAML/OIDC public lookups require an organization context; new OIDC client secrets are encrypted at rest, and legacy plaintext values are re-encrypted on first callback use.
581
+ - OAuth/OIDC client context no longer places an organization claim in a user token unless the user is a member of that application's organization.
582
+ - Failed authorization attempts and role transitions now produce structured audit evidence.
583
+ - SAML schema validation now has a signed-response regression test and audience/destination/recipient checks, alongside one-time transaction claiming and OIDC ID-token/JWKS verification.
584
+ - Enterprise SSO requires an explicit connection/subject identity link and rejects platform-owner tenant login; generic OAuth no longer auto-links by email.
585
+ - SCIM is scoped to `SCIM_ORG_ID`, cannot re-enable quarantined or platform-owner accounts, attributes audits to the SCIM credential, and deactivates rather than deleting users.
586
+ - OIDC endpoint checks cover private, carrier-grade, benchmarking, dotted/hex IPv4-mapped, DNS-pinned, and redirecting targets.
587
+ - Legacy account migration quarantines ambiguous unverified rows instead of activating them.
588
+ - SAML semantic validation and OIDC/JWKS checks are covered by signed-response tests; OAuth2 refresh success and failure emit audit events.
589
+ - Refresh-token rotation and OAuth authorization-code consumption are atomic and client-bound.
590
+ - Legacy unverified accounts are quarantined for explicit review during the deactivation migration.
591
+ - OIDC endpoint configuration blocks private/redirected targets by default; SCIM reflects account deactivation.
592
+ - Added a blocking `oxlint` gate with warnings denied; CI now runs lint separately from typecheck.
593
+ - Pinned safe transitive versions for `@xmldom/xmldom`, `fast-uri`, and `find-my-way`; the High-severity production audit gate now passes.
594
+ - API-key validation uses public user projections and emits `api_key_used` audit events; the compiled OIDC re-encryption helper closes its database pool before exit.
595
+
596
+ ### Security
597
+
598
+ - Critical organization-admin-to-platform-owner escalation paths are closed at HTTP, application, domain, SDK, and repository boundaries.
599
+ - Cross-tenant authorization context is resolved from authenticated database membership rather than client-controlled application context.
600
+ - Workflow definitions that are malformed or contain blocked authorization steps fail closed.
601
+
602
+ ### Breaking Changes
603
+
604
+ - Organization user PATCH/DELETE endpoints no longer mutate global accounts; they return a migration response. Use platform user administration or organization member endpoints.
605
+ - `/v1/authz/check` requests must include `organizationId`.
606
+ - Custom organization role names are no longer accepted; only `owner`, `admin`, and `member` are supported.
607
+ - Public SAML/OIDC initiation and metadata URLs require `organizationId`.
608
+ - Direct authorization SDK calls now require both actor and organization IDs.
609
+ - OIDC connections require a JWKS URI; OAuth2 application-bound refresh requests must provide the bound `client_id` and `client_secret`.
610
+ - SCIM requires both `SCIM_BEARER_TOKEN` and `SCIM_ORG_ID`, and operates only within that organization.
611
+ - Legacy unverified accounts may be marked `account_review_required` and require explicit review.
612
+ - Tenant workflow definitions containing authorization-mutating, plugin, organization-creation, or arbitrary webhook steps are rejected or blocked.
613
+
614
+ ### Migration
615
+
616
+ - Move platform role changes to `PATCH /v1/admin/platform/users/:userId/role`.
617
+ - Use `/v1/admin/organizations/:id/members/:userId` for organization role changes.
618
+ - Remove unsafe workflow steps before deployment.
619
+ - Update SAML/OIDC URLs to include the organization ID.
620
+ - Update authorization-check clients to send the organization ID explicitly.
621
+
622
+ ### Dependencies
623
+
624
+ - No dependency changes in this release. Existing dependency audit findings remain tracked for the planned supply-chain phase.
625
+
626
+ ### Testing
627
+
628
+ - Backend typecheck and build pass.
629
+ - Backend test suite passes with the security regression suite enabled.
630
+ - Frontend production build passes.
631
+
8
632
  ## [1.1.0] - 2026-09-19
9
633
 
10
634
  Security hardening, architecture improvements, and enterprise SSO enhancements.
@@ -144,6 +768,7 @@ Frontend upgrades — React 19, Vite 8, Tailwind 4, TypeScript 7.
144
768
  - `postcss.config.js` deleted — Tailwind 4 uses Vite plugin directly.
145
769
  - `src/index.css` updated to use `@import "./tailwind.css"` instead of `@tailwind base/components/utilities`.
146
770
 
771
+ [1.7.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.7.0
147
772
  [1.6.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.6.0
148
773
  [1.5.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.5.0
149
774
  [1.4.0]: https://github.com/Hilbras/Keystone/releases/tag/v1.4.0