@sixb/core 0.1.12 → 0.1.14

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 (538) hide show
  1. package/dist/actions/index.d.ts +2 -2
  2. package/dist/actions/index.d.ts.map +1 -1
  3. package/dist/actions/index.js +28 -26
  4. package/dist/actions/index.js.map +1 -1
  5. package/dist/actions/read-facade.d.ts +14 -16
  6. package/dist/actions/read-facade.d.ts.map +1 -1
  7. package/dist/actions/request.d.ts.map +1 -1
  8. package/dist/actions/run-dispatch.d.ts +2 -0
  9. package/dist/actions/run-dispatch.d.ts.map +1 -1
  10. package/dist/actions/types/context.d.ts +18 -31
  11. package/dist/actions/types/context.d.ts.map +1 -1
  12. package/dist/actions/types/params.d.ts +2 -1
  13. package/dist/actions/types/params.d.ts.map +1 -1
  14. package/dist/actions/worker.js +1 -1
  15. package/dist/agents/index.js +41 -39
  16. package/dist/agents/index.js.map +1 -1
  17. package/dist/agents/streams/index.js +2 -2
  18. package/dist/agents/streams/internal.js +2 -2
  19. package/dist/assert-never.d.ts +6 -0
  20. package/dist/assert-never.d.ts.map +1 -0
  21. package/dist/auth/client-address.d.ts +3 -0
  22. package/dist/auth/client-address.d.ts.map +1 -0
  23. package/dist/auth/csrf.d.ts +5 -1
  24. package/dist/auth/csrf.d.ts.map +1 -1
  25. package/dist/auth/errors.d.ts +12 -0
  26. package/dist/auth/errors.d.ts.map +1 -1
  27. package/dist/auth/index.d.ts +4 -3
  28. package/dist/auth/index.d.ts.map +1 -1
  29. package/dist/auth/index.js +16 -8
  30. package/dist/auth/index.js.map +1 -1
  31. package/dist/auth/runtime.d.ts +58 -41
  32. package/dist/auth/runtime.d.ts.map +1 -1
  33. package/dist/auth/session-cache.d.ts +10 -3
  34. package/dist/auth/session-cache.d.ts.map +1 -1
  35. package/dist/auth/sessions.d.ts +12 -0
  36. package/dist/auth/sessions.d.ts.map +1 -1
  37. package/dist/auth/strategy.d.ts +2 -0
  38. package/dist/auth/strategy.d.ts.map +1 -1
  39. package/dist/auth/strategy.js +4 -2
  40. package/dist/auth/strategy.js.map +1 -1
  41. package/dist/auth/types.d.ts +16 -15
  42. package/dist/auth/types.d.ts.map +1 -1
  43. package/dist/authorization/index.js +3 -3
  44. package/dist/bootstrap/index.d.ts +1 -1
  45. package/dist/bootstrap/index.d.ts.map +1 -1
  46. package/dist/bootstrap/index.js +45 -43
  47. package/dist/bootstrap/index.js.map +1 -1
  48. package/dist/bootstrap/ontology-type-manifest.d.ts +8 -0
  49. package/dist/bootstrap/ontology-type-manifest.d.ts.map +1 -1
  50. package/dist/{chunk-m7cx05vp.js → chunk-00pfcqh4.js} +126 -59
  51. package/dist/{chunk-m7cx05vp.js.map → chunk-00pfcqh4.js.map} +13 -12
  52. package/dist/{chunk-nzsy3xrp.js → chunk-16jenmzw.js} +69 -44
  53. package/dist/{chunk-nzsy3xrp.js.map → chunk-16jenmzw.js.map} +7 -7
  54. package/dist/{chunk-y3ev53cq.js → chunk-2fv8sajb.js} +2 -2
  55. package/dist/{chunk-q5gz3g2j.js → chunk-2jntasmh.js} +13 -16
  56. package/dist/{chunk-q5gz3g2j.js.map → chunk-2jntasmh.js.map} +3 -3
  57. package/dist/chunk-3a338wrp.js +191 -0
  58. package/dist/chunk-3a338wrp.js.map +11 -0
  59. package/dist/{chunk-ybhz3j9g.js → chunk-3fpgxpsq.js} +104 -148
  60. package/dist/{chunk-ybhz3j9g.js.map → chunk-3fpgxpsq.js.map} +7 -8
  61. package/dist/{chunk-eyjzgav0.js → chunk-3nx8vj5a.js} +6 -4
  62. package/dist/chunk-3nx8vj5a.js.map +12 -0
  63. package/dist/{chunk-5tetw4hn.js → chunk-4tnrzqqh.js} +1 -1
  64. package/dist/{chunk-3ghw2t5q.js → chunk-4ypbe95r.js} +10 -16
  65. package/dist/chunk-4ypbe95r.js.map +10 -0
  66. package/dist/{chunk-2cxv584m.js → chunk-6rzb1g6h.js} +358 -248
  67. package/dist/chunk-6rzb1g6h.js.map +44 -0
  68. package/dist/{chunk-7zgecwy1.js → chunk-6x0pqh32.js} +12 -1
  69. package/dist/{chunk-7zgecwy1.js.map → chunk-6x0pqh32.js.map} +3 -3
  70. package/dist/{chunk-6ma8dvzc.js → chunk-728fp090.js} +49 -42
  71. package/dist/chunk-728fp090.js.map +15 -0
  72. package/dist/chunk-96yw3pwd.js.map +2 -2
  73. package/dist/{chunk-4k53sm9e.js → chunk-a0073121.js} +59 -59
  74. package/dist/{chunk-4k53sm9e.js.map → chunk-a0073121.js.map} +5 -5
  75. package/dist/{chunk-y1ahv8b7.js → chunk-bdhfn0ww.js} +1 -1
  76. package/dist/{chunk-kept9cdd.js → chunk-bxx3ms5t.js} +1 -1
  77. package/dist/{chunk-xtfryrfb.js → chunk-cbn96bve.js} +2 -2
  78. package/dist/{chunk-vsg0g7h9.js → chunk-d8yes9yx.js} +304 -45
  79. package/dist/chunk-d8yes9yx.js.map +20 -0
  80. package/dist/{chunk-vpxss818.js → chunk-drz59ve0.js} +9 -8
  81. package/dist/chunk-drz59ve0.js.map +10 -0
  82. package/dist/{chunk-7c2g3sz9.js → chunk-dsfw1bdr.js} +5 -5
  83. package/dist/{chunk-7c2g3sz9.js.map → chunk-dsfw1bdr.js.map} +1 -1
  84. package/dist/{chunk-qm535afa.js → chunk-dv4ct8mp.js} +66 -24
  85. package/dist/chunk-dv4ct8mp.js.map +15 -0
  86. package/dist/{chunk-t7dzd1tm.js → chunk-e2c5yxfe.js} +2 -2
  87. package/dist/{chunk-p6r1ex7t.js → chunk-ffwvdgk1.js} +5 -13
  88. package/dist/chunk-ffwvdgk1.js.map +10 -0
  89. package/dist/{chunk-tg4g4zh0.js → chunk-gct3m1da.js} +9 -9
  90. package/dist/{chunk-tg4g4zh0.js.map → chunk-gct3m1da.js.map} +3 -3
  91. package/dist/chunk-h89wz928.js +42 -0
  92. package/dist/chunk-h89wz928.js.map +10 -0
  93. package/dist/{chunk-b768c93y.js → chunk-hj0n3c2p.js} +83 -28
  94. package/dist/{chunk-b768c93y.js.map → chunk-hj0n3c2p.js.map} +5 -5
  95. package/dist/{chunk-hkw8rykk.js → chunk-ht4a7mjg.js} +74 -4
  96. package/dist/chunk-ht4a7mjg.js.map +15 -0
  97. package/dist/{chunk-7qnvhtcd.js → chunk-hxgj4kzd.js} +17 -17
  98. package/dist/{chunk-7qnvhtcd.js.map → chunk-hxgj4kzd.js.map} +5 -5
  99. package/dist/{chunk-bg2bs62d.js → chunk-jfdggg1p.js} +5 -5
  100. package/dist/{chunk-f06sf7rr.js → chunk-k4kkp58d.js} +18 -5
  101. package/dist/chunk-k4kkp58d.js.map +11 -0
  102. package/dist/{chunk-e1f1sqba.js → chunk-kxea54yg.js} +3 -3
  103. package/dist/{chunk-2h63nrde.js → chunk-ky7zpbtt.js} +5 -7
  104. package/dist/{chunk-2h63nrde.js.map → chunk-ky7zpbtt.js.map} +3 -4
  105. package/dist/chunk-m4ehq95f.js +9 -0
  106. package/dist/chunk-m4ehq95f.js.map +10 -0
  107. package/dist/{chunk-0mza4xb2.js → chunk-m8zkvep1.js} +2 -2
  108. package/dist/{chunk-xn686q2e.js → chunk-ma1mhh7w.js} +75 -57
  109. package/dist/{chunk-xn686q2e.js.map → chunk-ma1mhh7w.js.map} +3 -3
  110. package/dist/{chunk-yxb4c62x.js → chunk-mr60an99.js} +3 -2
  111. package/dist/{chunk-yxb4c62x.js.map → chunk-mr60an99.js.map} +3 -3
  112. package/dist/{chunk-g50nxqs5.js → chunk-pvgpckm1.js} +63 -20
  113. package/dist/chunk-pvgpckm1.js.map +20 -0
  114. package/dist/{chunk-jjwhy3rs.js → chunk-q2xghyft.js} +5 -5
  115. package/dist/chunk-rdghfsss.js.map +2 -2
  116. package/dist/{chunk-bbaa1fet.js → chunk-rzcjp5m2.js} +7 -3
  117. package/dist/chunk-rzcjp5m2.js.map +25 -0
  118. package/dist/{chunk-hzw7yv5g.js → chunk-rzkr70gr.js} +5 -1
  119. package/dist/{chunk-hzw7yv5g.js.map → chunk-rzkr70gr.js.map} +3 -3
  120. package/dist/{chunk-axndsymr.js → chunk-tw03bsmb.js} +2 -2
  121. package/dist/{chunk-axndsymr.js.map → chunk-tw03bsmb.js.map} +3 -3
  122. package/dist/{chunk-y0v18c2j.js → chunk-vef6qa14.js} +2 -2
  123. package/dist/{chunk-gzt6v1ja.js → chunk-vn3yh259.js} +1 -1
  124. package/dist/{chunk-sfypc7mk.js → chunk-wn7mznxc.js} +4 -4
  125. package/dist/{chunk-r5zythxp.js → chunk-wnxabtn4.js} +7 -7
  126. package/dist/{chunk-4ph94nhd.js → chunk-ww185kt6.js} +2 -2
  127. package/dist/{chunk-3h7ctayx.js → chunk-xgptywp0.js} +4 -3
  128. package/dist/chunk-xgptywp0.js.map +11 -0
  129. package/dist/{chunk-gswwr35f.js → chunk-y487ywrv.js} +4 -4
  130. package/dist/{chunk-bet1rb9h.js → chunk-y6vnvayj.js} +1 -1
  131. package/dist/{chunk-x9tmx3fc.js → chunk-ypj2wqtj.js} +3 -6
  132. package/dist/{chunk-x9tmx3fc.js.map → chunk-ypj2wqtj.js.map} +4 -4
  133. package/dist/{chunk-3bnt2wna.js → chunk-yvf2jqj8.js} +1 -1
  134. package/dist/{chunk-g2gezsg5.js → chunk-z9h698s8.js} +1 -1
  135. package/dist/{chunk-wjffn5m4.js → chunk-zdkcdshk.js} +3 -36
  136. package/dist/{chunk-wjffn5m4.js.map → chunk-zdkcdshk.js.map} +3 -4
  137. package/dist/{chunk-bfjv1adk.js → chunk-znw0zvy4.js} +1 -1
  138. package/dist/edits/types.d.ts +19 -19
  139. package/dist/edits/types.d.ts.map +1 -1
  140. package/dist/error-reporting/internal.js +3 -3
  141. package/dist/errors/catalog.d.ts +4 -0
  142. package/dist/errors/catalog.d.ts.map +1 -1
  143. package/dist/errors/internal.js +1 -1
  144. package/dist/events/envelope.d.ts +0 -12
  145. package/dist/events/envelope.d.ts.map +1 -1
  146. package/dist/events/index.d.ts +1 -1
  147. package/dist/events/index.d.ts.map +1 -1
  148. package/dist/events/index.js +3 -3
  149. package/dist/events/service.d.ts +1 -2
  150. package/dist/events/service.d.ts.map +1 -1
  151. package/dist/events/toStoredEvent.d.ts +1 -2
  152. package/dist/events/toStoredEvent.d.ts.map +1 -1
  153. package/dist/events/types/index.d.ts +2 -1
  154. package/dist/events/types/index.d.ts.map +1 -1
  155. package/dist/execution/agent.d.ts +1 -2
  156. package/dist/execution/agent.d.ts.map +1 -1
  157. package/dist/execution/agent.js +39 -37
  158. package/dist/execution/agent.js.map +1 -1
  159. package/dist/execution/authorized-object-reader.d.ts.map +1 -1
  160. package/dist/execution/primitive.d.ts +1 -2
  161. package/dist/execution/primitive.d.ts.map +1 -1
  162. package/dist/execution/primitive.js +38 -36
  163. package/dist/execution/primitive.js.map +3 -3
  164. package/dist/execution/request.d.ts +1 -2
  165. package/dist/execution/request.d.ts.map +1 -1
  166. package/dist/execution/request.js +39 -37
  167. package/dist/execution/request.js.map +3 -3
  168. package/dist/index.d.ts +6 -6
  169. package/dist/index.d.ts.map +1 -1
  170. package/dist/index.js +56 -50
  171. package/dist/index.js.map +1 -1
  172. package/dist/maintenance/ontology-maintenance.d.ts +11 -1
  173. package/dist/maintenance/ontology-maintenance.d.ts.map +1 -1
  174. package/dist/materialization/errors.d.ts +3 -1
  175. package/dist/materialization/errors.d.ts.map +1 -1
  176. package/dist/materialization/events.d.ts +27 -2
  177. package/dist/materialization/events.d.ts.map +1 -1
  178. package/dist/materializer/edits/commit.d.ts.map +1 -1
  179. package/dist/materializer/edits/operations.d.ts +2 -1
  180. package/dist/materializer/edits/operations.d.ts.map +1 -1
  181. package/dist/materializer/edits/plan.d.ts +2 -2
  182. package/dist/materializer/edits/plan.d.ts.map +1 -1
  183. package/dist/materializer/edits/user-refs.d.ts +30 -0
  184. package/dist/materializer/edits/user-refs.d.ts.map +1 -0
  185. package/dist/materializer/effective/build-events.d.ts +2 -2
  186. package/dist/materializer/effective/build-events.d.ts.map +1 -1
  187. package/dist/materializer/execution/scope.d.ts +11 -3
  188. package/dist/materializer/execution/scope.d.ts.map +1 -1
  189. package/dist/materializer/index.js +23 -21
  190. package/dist/materializer/index.js.map +1 -1
  191. package/dist/materializer/projections/replacement-plan.d.ts +2 -0
  192. package/dist/materializer/projections/replacement-plan.d.ts.map +1 -1
  193. package/dist/materializer/telemetry/plan.d.ts +2 -2
  194. package/dist/materializer/telemetry/plan.d.ts.map +1 -1
  195. package/dist/models/execution/index.js +23 -21
  196. package/dist/models/execution/index.js.map +1 -1
  197. package/dist/models/index.js +1 -1
  198. package/dist/objects/execution.d.ts +21 -27
  199. package/dist/objects/execution.d.ts.map +1 -1
  200. package/dist/objects/index.js +28 -26
  201. package/dist/objects/index.js.map +1 -1
  202. package/dist/objects/query/executor.d.ts.map +1 -1
  203. package/dist/objects/query/index.js +27 -25
  204. package/dist/objects/query/index.js.map +1 -1
  205. package/dist/objects/query/ir.d.ts +8 -1
  206. package/dist/objects/query/ir.d.ts.map +1 -1
  207. package/dist/objects/query/scalar-values.d.ts.map +1 -1
  208. package/dist/objects/query/validate.d.ts.map +1 -1
  209. package/dist/objects/sdk/object-handle.d.ts +1 -2
  210. package/dist/objects/sdk/object-handle.d.ts.map +1 -1
  211. package/dist/objects/sdk/object-set.d.ts +2 -3
  212. package/dist/objects/sdk/object-set.d.ts.map +1 -1
  213. package/dist/objects/sdk/query-builder.d.ts +8 -10
  214. package/dist/objects/sdk/query-builder.d.ts.map +1 -1
  215. package/dist/objects/sdk/telemetry-channel.d.ts +1 -2
  216. package/dist/objects/sdk/telemetry-channel.d.ts.map +1 -1
  217. package/dist/objects/sdk/where.d.ts +2 -3
  218. package/dist/objects/sdk/where.d.ts.map +1 -1
  219. package/dist/ontology/builders.d.ts +11 -10
  220. package/dist/ontology/builders.d.ts.map +1 -1
  221. package/dist/ontology/index.d.ts +4 -2
  222. package/dist/ontology/index.d.ts.map +1 -1
  223. package/dist/ontology/index.js +6 -4
  224. package/dist/ontology/index.js.map +1 -1
  225. package/dist/ontology/inference/index.d.ts +34 -22
  226. package/dist/ontology/inference/index.d.ts.map +1 -1
  227. package/dist/ontology/internal.d.ts +1 -0
  228. package/dist/ontology/internal.d.ts.map +1 -1
  229. package/dist/ontology/internal.js +9 -7
  230. package/dist/ontology/internal.js.map +1 -1
  231. package/dist/ontology/primitives.d.ts +38 -0
  232. package/dist/ontology/primitives.d.ts.map +1 -0
  233. package/dist/ontology/query-capabilities.d.ts +38 -0
  234. package/dist/ontology/query-capabilities.d.ts.map +1 -0
  235. package/dist/ontology/refs.d.ts +23 -2
  236. package/dist/ontology/refs.d.ts.map +1 -1
  237. package/dist/ontology/registered.d.ts +16 -0
  238. package/dist/ontology/registered.d.ts.map +1 -0
  239. package/dist/ontology/registry.d.ts.map +1 -1
  240. package/dist/ontology/tokens.d.ts +35 -1
  241. package/dist/ontology/tokens.d.ts.map +1 -1
  242. package/dist/ontology/types.d.ts +18 -4
  243. package/dist/ontology/types.d.ts.map +1 -1
  244. package/dist/ontology/user-ref.d.ts +35 -0
  245. package/dist/ontology/user-ref.d.ts.map +1 -0
  246. package/dist/ontology/validation/definition.d.ts.map +1 -1
  247. package/dist/ontology/validation/index.d.ts +1 -1
  248. package/dist/ontology/validation/index.d.ts.map +1 -1
  249. package/dist/ontology/validation/normalize.d.ts.map +1 -1
  250. package/dist/ontology/validation/properties.d.ts +6 -1
  251. package/dist/ontology/validation/properties.d.ts.map +1 -1
  252. package/dist/ontology/validation/query.d.ts.map +1 -1
  253. package/dist/ontology/validation/schema.d.ts.map +1 -1
  254. package/dist/pipelines/index.js +26 -24
  255. package/dist/pipelines/index.js.map +1 -1
  256. package/dist/predicates/types.d.ts +53 -3
  257. package/dist/predicates/types.d.ts.map +1 -1
  258. package/dist/projections/builders.d.ts +19 -1
  259. package/dist/projections/builders.d.ts.map +1 -1
  260. package/dist/projections/internal.js +23 -21
  261. package/dist/projections/internal.js.map +1 -1
  262. package/dist/projections/validation.d.ts.map +1 -1
  263. package/dist/query.js +2 -2
  264. package/dist/queues/in-memory.d.ts +1 -1
  265. package/dist/queues/in-memory.d.ts.map +1 -1
  266. package/dist/runtime/create.d.ts +1 -1
  267. package/dist/runtime/create.d.ts.map +1 -1
  268. package/dist/runtime/host.d.ts +5 -5
  269. package/dist/runtime/host.d.ts.map +1 -1
  270. package/dist/runtime/index.d.ts +1 -1
  271. package/dist/runtime/index.d.ts.map +1 -1
  272. package/dist/runtime/internal.js +27 -25
  273. package/dist/runtime/internal.js.map +1 -1
  274. package/dist/runtime/sixb.d.ts +5 -5
  275. package/dist/runtime/sixb.d.ts.map +1 -1
  276. package/dist/runtime/types.d.ts +171 -188
  277. package/dist/runtime/types.d.ts.map +1 -1
  278. package/dist/sandboxes/index.js +5 -5
  279. package/dist/sandboxes/sandbox.d.ts +4 -1
  280. package/dist/sandboxes/sandbox.d.ts.map +1 -1
  281. package/dist/shared/params/types.d.ts +4 -3
  282. package/dist/shared/params/types.d.ts.map +1 -1
  283. package/dist/shared/params/user-refs.d.ts +19 -0
  284. package/dist/shared/params/user-refs.d.ts.map +1 -0
  285. package/dist/shares/compiler.d.ts.map +1 -1
  286. package/dist/shares/internal.js +24 -22
  287. package/dist/shares/internal.js.map +2 -2
  288. package/dist/storage/action-runs/failure.js +2 -2
  289. package/dist/storage/action-runs/types.d.ts +1 -1
  290. package/dist/storage/action-runs/types.d.ts.map +1 -1
  291. package/dist/storage/agents/provider.js +1 -1
  292. package/dist/storage/ai-limits/enforcement.js +2 -2
  293. package/dist/storage/auth/errors.d.ts +1 -1
  294. package/dist/storage/auth/errors.d.ts.map +1 -1
  295. package/dist/storage/auth/in-memory/access-tokens.d.ts.map +1 -1
  296. package/dist/storage/auth/in-memory/auth-storage.d.ts +2 -1
  297. package/dist/storage/auth/in-memory/auth-storage.d.ts.map +1 -1
  298. package/dist/storage/auth/in-memory/device-authorizations.d.ts.map +1 -1
  299. package/dist/storage/auth/in-memory/sessions.d.ts +8 -1
  300. package/dist/storage/auth/in-memory/sessions.d.ts.map +1 -1
  301. package/dist/storage/auth/in-memory/shared.d.ts.map +1 -1
  302. package/dist/storage/auth/index.d.ts +1 -1
  303. package/dist/storage/auth/index.d.ts.map +1 -1
  304. package/dist/storage/auth/types.d.ts +77 -20
  305. package/dist/storage/auth/types.d.ts.map +1 -1
  306. package/dist/storage/connector-connections/provider.js +1 -1
  307. package/dist/storage/executions/provider.js +1 -1
  308. package/dist/storage/file-upload-sessions/in-memory.d.ts +9 -18
  309. package/dist/storage/file-upload-sessions/in-memory.d.ts.map +1 -1
  310. package/dist/storage/file-upload-sessions/index.d.ts +1 -1
  311. package/dist/storage/file-upload-sessions/index.d.ts.map +1 -1
  312. package/dist/storage/file-upload-sessions/provider.d.ts +52 -0
  313. package/dist/storage/file-upload-sessions/provider.d.ts.map +1 -0
  314. package/dist/storage/file-upload-sessions/provider.js +24 -0
  315. package/dist/storage/file-upload-sessions/provider.js.map +9 -0
  316. package/dist/storage/file-upload-sessions/types.d.ts +19 -1
  317. package/dist/storage/file-upload-sessions/types.d.ts.map +1 -1
  318. package/dist/storage/file-upload-sessions/utils.d.ts +8 -2
  319. package/dist/storage/file-upload-sessions/utils.d.ts.map +1 -1
  320. package/dist/storage/index.d.ts +3 -3
  321. package/dist/storage/index.d.ts.map +1 -1
  322. package/dist/storage/index.js +29 -25
  323. package/dist/storage/index.js.map +1 -1
  324. package/dist/storage/internal.js +11 -9
  325. package/dist/storage/internal.js.map +1 -1
  326. package/dist/storage/objects/in-memory/query.d.ts.map +1 -1
  327. package/dist/storage/ontology/commits.d.ts +6 -2
  328. package/dist/storage/ontology/commits.d.ts.map +1 -1
  329. package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
  330. package/dist/storage/ontology/provider-work.d.ts.map +1 -1
  331. package/dist/storage/ontology/provider.d.ts +14 -1
  332. package/dist/storage/ontology/provider.d.ts.map +1 -1
  333. package/dist/storage/ontology/provider.js +8 -2
  334. package/dist/storage/ontology/provider.js.map +1 -1
  335. package/dist/storage/operation-scope.d.ts.map +1 -1
  336. package/dist/storage/operation-scope.js +1 -1
  337. package/dist/storage/projection-runs/provider.js +2 -2
  338. package/dist/storage/types.d.ts +1 -1
  339. package/dist/storage/types.d.ts.map +1 -1
  340. package/dist/storage/workflow-interventions/index.d.ts +1 -1
  341. package/dist/storage/workflow-interventions/index.d.ts.map +1 -1
  342. package/dist/storage/workflow-interventions/types.d.ts +5 -8
  343. package/dist/storage/workflow-interventions/types.d.ts.map +1 -1
  344. package/dist/storage/workflow-runs/provider.js +8 -8
  345. package/dist/syncs/index.js +27 -25
  346. package/dist/syncs/index.js.map +1 -1
  347. package/dist/testing/action-execution.d.ts +4 -0
  348. package/dist/testing/action-execution.d.ts.map +1 -1
  349. package/dist/testing/auth-storage-contract.d.ts.map +1 -1
  350. package/dist/testing/execution.d.ts +1 -2
  351. package/dist/testing/execution.d.ts.map +1 -1
  352. package/dist/testing/file-upload-session-storage-contract.d.ts +12 -0
  353. package/dist/testing/file-upload-session-storage-contract.d.ts.map +1 -0
  354. package/dist/testing/index.d.ts +2 -0
  355. package/dist/testing/index.d.ts.map +1 -1
  356. package/dist/testing/index.js +2035 -1358
  357. package/dist/testing/index.js.map +15 -13
  358. package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
  359. package/dist/testing/object-query-contract.d.ts.map +1 -1
  360. package/dist/testing/ontology-contract-fixture.d.ts +8 -0
  361. package/dist/testing/ontology-contract-fixture.d.ts.map +1 -1
  362. package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
  363. package/dist/testing/sandboxes-contract.d.ts.map +1 -1
  364. package/dist/testing/workflow-intervention-storage-contract.d.ts +8 -0
  365. package/dist/testing/workflow-intervention-storage-contract.d.ts.map +1 -0
  366. package/dist/webhooks/internal.js +24 -22
  367. package/dist/webhooks/internal.js.map +2 -2
  368. package/dist/webhooks/types.d.ts +2 -2
  369. package/dist/webhooks/types.d.ts.map +1 -1
  370. package/dist/workers/index.js +1 -1
  371. package/dist/workflows/index.js +42 -40
  372. package/dist/workflows/index.js.map +1 -1
  373. package/dist/workflows/run-dispatch.d.ts.map +1 -1
  374. package/dist/workflows/types.d.ts +2 -2
  375. package/dist/workflows/types.d.ts.map +1 -1
  376. package/package.json +7 -1
  377. package/src/actions/index.ts +0 -3
  378. package/src/actions/read-facade.ts +102 -62
  379. package/src/actions/request.ts +13 -0
  380. package/src/actions/run-dispatch.ts +3 -0
  381. package/src/actions/types/context.ts +17 -65
  382. package/src/actions/types/params.ts +2 -1
  383. package/src/agents/authority.ts +1 -1
  384. package/src/assert-never.ts +7 -0
  385. package/src/auth/client-address.ts +12 -0
  386. package/src/auth/csrf.ts +14 -5
  387. package/src/auth/errors.ts +23 -0
  388. package/src/auth/index.ts +4 -3
  389. package/src/auth/runtime.ts +353 -297
  390. package/src/auth/session-cache.ts +18 -6
  391. package/src/auth/sessions.ts +30 -0
  392. package/src/auth/strategy.ts +2 -0
  393. package/src/auth/types.ts +15 -17
  394. package/src/bootstrap/index.ts +1 -0
  395. package/src/bootstrap/ontology-type-manifest.ts +112 -60
  396. package/src/edits/types.ts +47 -63
  397. package/src/errors/catalog.ts +4 -0
  398. package/src/events/envelope.ts +0 -13
  399. package/src/events/index.ts +1 -1
  400. package/src/events/service.ts +1 -11
  401. package/src/events/toStoredEvent.ts +1 -3
  402. package/src/events/types/index.ts +1 -1
  403. package/src/execution/agent.ts +1 -2
  404. package/src/execution/authorized-object-reader.ts +5 -7
  405. package/src/execution/primitive.ts +1 -2
  406. package/src/execution/request.ts +1 -2
  407. package/src/index.ts +8 -7
  408. package/src/maintenance/ontology-maintenance.ts +77 -1
  409. package/src/materialization/errors.ts +2 -0
  410. package/src/materialization/events.ts +27 -2
  411. package/src/materializer/README.md +2 -0
  412. package/src/materializer/edits/commit.ts +21 -12
  413. package/src/materializer/edits/operations.ts +32 -4
  414. package/src/materializer/edits/plan.ts +4 -5
  415. package/src/materializer/edits/user-refs.ts +115 -0
  416. package/src/materializer/effective/build-events.ts +4 -5
  417. package/src/materializer/execution/scope.ts +40 -7
  418. package/src/materializer/projections/replace.ts +2 -0
  419. package/src/materializer/projections/replacement-plan.ts +4 -0
  420. package/src/materializer/telemetry/append.ts +4 -12
  421. package/src/materializer/telemetry/plan.ts +4 -5
  422. package/src/objects/execution.ts +33 -62
  423. package/src/objects/query/executor.ts +12 -3
  424. package/src/objects/query/ir.ts +7 -0
  425. package/src/objects/query/planner.ts +1 -0
  426. package/src/objects/query/scalar-values.ts +19 -0
  427. package/src/objects/query/validate.ts +55 -44
  428. package/src/objects/sdk/object-handle.ts +8 -8
  429. package/src/objects/sdk/object-set.ts +8 -15
  430. package/src/objects/sdk/query-builder.ts +59 -71
  431. package/src/objects/sdk/telemetry-channel.ts +6 -7
  432. package/src/objects/sdk/where.ts +6 -13
  433. package/src/ontology/builders.ts +15 -11
  434. package/src/ontology/index.ts +4 -0
  435. package/src/ontology/inference/index.ts +56 -36
  436. package/src/ontology/internal.ts +1 -0
  437. package/src/ontology/json-schema.ts +12 -0
  438. package/src/ontology/primitives.ts +91 -0
  439. package/src/ontology/query-capabilities.ts +136 -0
  440. package/src/ontology/refs.ts +33 -6
  441. package/src/ontology/registered.ts +22 -0
  442. package/src/ontology/registry.ts +6 -3
  443. package/src/ontology/tokens.ts +41 -2
  444. package/src/ontology/types.ts +19 -3
  445. package/src/ontology/user-ref.ts +120 -0
  446. package/src/ontology/validation/definition.ts +2 -13
  447. package/src/ontology/validation/index.ts +1 -0
  448. package/src/ontology/validation/normalize.ts +39 -3
  449. package/src/ontology/validation/properties.ts +95 -19
  450. package/src/ontology/validation/query.ts +11 -42
  451. package/src/ontology/validation/schema.ts +16 -0
  452. package/src/predicates/types.ts +43 -33
  453. package/src/projections/builders.ts +24 -3
  454. package/src/projections/validation.ts +15 -1
  455. package/src/runtime/create.ts +2 -2
  456. package/src/runtime/host.ts +10 -12
  457. package/src/runtime/index.ts +3 -2
  458. package/src/runtime/sixb.ts +13 -23
  459. package/src/runtime/types.ts +277 -479
  460. package/src/sandboxes/sandbox.ts +4 -1
  461. package/src/shared/params/types.ts +4 -3
  462. package/src/shared/params/user-refs.ts +48 -0
  463. package/src/shares/compiler.ts +6 -16
  464. package/src/storage/action-runs/types.ts +1 -0
  465. package/src/storage/auth/errors.ts +0 -1
  466. package/src/storage/auth/in-memory/access-tokens.ts +5 -2
  467. package/src/storage/auth/in-memory/auth-storage.ts +49 -73
  468. package/src/storage/auth/in-memory/device-authorizations.ts +0 -2
  469. package/src/storage/auth/in-memory/sessions.ts +69 -1
  470. package/src/storage/auth/in-memory/shared.ts +13 -7
  471. package/src/storage/auth/index.ts +2 -1
  472. package/src/storage/auth/types.ts +72 -20
  473. package/src/storage/executions/validation.ts +1 -1
  474. package/src/storage/file-upload-sessions/in-memory.ts +64 -113
  475. package/src/storage/file-upload-sessions/index.ts +1 -0
  476. package/src/storage/file-upload-sessions/provider.ts +258 -0
  477. package/src/storage/file-upload-sessions/types.ts +20 -1
  478. package/src/storage/file-upload-sessions/utils.ts +25 -9
  479. package/src/storage/index.ts +3 -2
  480. package/src/storage/objects/in-memory/query.ts +13 -3
  481. package/src/storage/ontology/commits.ts +6 -2
  482. package/src/storage/ontology/in-memory/materializations.ts +6 -34
  483. package/src/storage/ontology/provider-header-validation.ts +57 -0
  484. package/src/storage/ontology/provider-work.ts +15 -3
  485. package/src/storage/ontology/provider.ts +59 -1
  486. package/src/storage/operation-scope.ts +1 -0
  487. package/src/storage/types.ts +0 -1
  488. package/src/storage/workflow-interventions/index.ts +0 -1
  489. package/src/storage/workflow-interventions/types.ts +5 -9
  490. package/src/testing/action-execution.ts +13 -3
  491. package/src/testing/auth-storage-contract.ts +253 -192
  492. package/src/testing/execution-storage-contract.ts +1 -2
  493. package/src/testing/execution.ts +4 -7
  494. package/src/testing/file-upload-session-storage-contract.ts +335 -0
  495. package/src/testing/index.ts +8 -0
  496. package/src/testing/materializer-storage-contract.ts +86 -4
  497. package/src/testing/object-query-contract.ts +121 -2
  498. package/src/testing/ontology-contract-fixture.ts +23 -3
  499. package/src/testing/ontology-storage-contract.ts +58 -0
  500. package/src/testing/sandboxes-contract.ts +27 -0
  501. package/src/testing/workflow-intervention-storage-contract.ts +203 -0
  502. package/src/webhooks/types.ts +2 -2
  503. package/src/workflows/run-dispatch.ts +12 -1
  504. package/src/workflows/snapshot.ts +16 -7
  505. package/src/workflows/types.ts +2 -2
  506. package/dist/chunk-2cxv584m.js.map +0 -43
  507. package/dist/chunk-3ghw2t5q.js.map +0 -10
  508. package/dist/chunk-3h7ctayx.js.map +0 -11
  509. package/dist/chunk-6ma8dvzc.js.map +0 -15
  510. package/dist/chunk-bbaa1fet.js.map +0 -25
  511. package/dist/chunk-eyjzgav0.js.map +0 -12
  512. package/dist/chunk-f06sf7rr.js.map +0 -11
  513. package/dist/chunk-g50nxqs5.js.map +0 -18
  514. package/dist/chunk-hkw8rykk.js.map +0 -15
  515. package/dist/chunk-p6r1ex7t.js.map +0 -10
  516. package/dist/chunk-qm535afa.js.map +0 -15
  517. package/dist/chunk-vpxss818.js.map +0 -10
  518. package/dist/chunk-vsg0g7h9.js.map +0 -17
  519. /package/dist/{chunk-y3ev53cq.js.map → chunk-2fv8sajb.js.map} +0 -0
  520. /package/dist/{chunk-5tetw4hn.js.map → chunk-4tnrzqqh.js.map} +0 -0
  521. /package/dist/{chunk-y1ahv8b7.js.map → chunk-bdhfn0ww.js.map} +0 -0
  522. /package/dist/{chunk-kept9cdd.js.map → chunk-bxx3ms5t.js.map} +0 -0
  523. /package/dist/{chunk-xtfryrfb.js.map → chunk-cbn96bve.js.map} +0 -0
  524. /package/dist/{chunk-t7dzd1tm.js.map → chunk-e2c5yxfe.js.map} +0 -0
  525. /package/dist/{chunk-bg2bs62d.js.map → chunk-jfdggg1p.js.map} +0 -0
  526. /package/dist/{chunk-e1f1sqba.js.map → chunk-kxea54yg.js.map} +0 -0
  527. /package/dist/{chunk-0mza4xb2.js.map → chunk-m8zkvep1.js.map} +0 -0
  528. /package/dist/{chunk-jjwhy3rs.js.map → chunk-q2xghyft.js.map} +0 -0
  529. /package/dist/{chunk-y0v18c2j.js.map → chunk-vef6qa14.js.map} +0 -0
  530. /package/dist/{chunk-gzt6v1ja.js.map → chunk-vn3yh259.js.map} +0 -0
  531. /package/dist/{chunk-sfypc7mk.js.map → chunk-wn7mznxc.js.map} +0 -0
  532. /package/dist/{chunk-r5zythxp.js.map → chunk-wnxabtn4.js.map} +0 -0
  533. /package/dist/{chunk-4ph94nhd.js.map → chunk-ww185kt6.js.map} +0 -0
  534. /package/dist/{chunk-gswwr35f.js.map → chunk-y487ywrv.js.map} +0 -0
  535. /package/dist/{chunk-bet1rb9h.js.map → chunk-y6vnvayj.js.map} +0 -0
  536. /package/dist/{chunk-3bnt2wna.js.map → chunk-yvf2jqj8.js.map} +0 -0
  537. /package/dist/{chunk-g2gezsg5.js.map → chunk-z9h698s8.js.map} +0 -0
  538. /package/dist/{chunk-bfjv1adk.js.map → chunk-znw0zvy4.js.map} +0 -0
@@ -18,6 +18,8 @@ import {
18
18
  type AccessTokenRecord,
19
19
  type AuthStorage,
20
20
  AuthStorageError,
21
+ type CompleteDeviceAuthorizationInput,
22
+ type GroupMembershipRecord,
21
23
  type InvitationRecord,
22
24
  type ServiceAccountGroupMembershipRecord,
23
25
  type ServiceAccountRecord,
@@ -31,7 +33,8 @@ import {
31
33
  hashAccessTokenSecret,
32
34
  parseAccessTokenValue,
33
35
  } from "./access-tokens"
34
- import type { AuthSessionAudience } from "./audience"
36
+ import { type AuthSessionAudience, DEFAULT_AUTH_SESSION_AUDIENCE } from "./audience"
37
+ import { getRequestClientAddress } from "./client-address"
35
38
  import {
36
39
  getCookie,
37
40
  type ResolvedAuthCookieOptions,
@@ -39,7 +42,15 @@ import {
39
42
  } from "./cookies"
40
43
  import { AuthRuntimeError } from "./errors"
41
44
  import { SessionCache } from "./session-cache"
42
- import { hashSessionSecret, parseSessionCookieValue } from "./sessions"
45
+ import {
46
+ type BearerSessionTokens,
47
+ createBearerSessionTokens,
48
+ hashSessionSecret,
49
+ parseBearerSessionToken,
50
+ parseSessionCookieValue,
51
+ SESSION_ACCESS_TOKEN_PREFIX,
52
+ SESSION_REFRESH_TOKEN_PREFIX,
53
+ } from "./sessions"
43
54
  import type {
44
55
  AuthenticatedAuthSession,
45
56
  AuthenticatedRequestAuthSession,
@@ -59,12 +70,12 @@ import type {
59
70
  GetInvitationOptionsResult,
60
71
  GetMembershipOptionsResult,
61
72
  InvitationDeliveryAuthStrategy,
73
+ InvitationDestination,
62
74
  InvitationGroupOption,
63
75
  InvitationRecipientStatus,
64
76
  InviteDeliveryResult,
65
77
  InviteDeliveryStatus,
66
78
  InviteUserInput,
67
- InviteUserOptions,
68
79
  InviteUserResult,
69
80
  ListInvitationsInput,
70
81
  ListInvitationsResult,
@@ -76,7 +87,6 @@ import type {
76
87
  ListServiceAccountsResult,
77
88
  MemberSummary,
78
89
  MembershipCapabilities,
79
- Principal,
80
90
  ReactivateMemberInput,
81
91
  ReactivateMemberResult,
82
92
  ResolvedAuthConfig,
@@ -86,10 +96,10 @@ import type {
86
96
  RevokePersonalAccessTokenInput,
87
97
  RevokeServiceAccountAccessTokenInput,
88
98
  RevokeServiceAccountAccessTokenResult,
89
- SecurityContext,
90
99
  SixbAuthConfig,
91
100
  SuspendMemberInput,
92
101
  SuspendMemberResult,
102
+ UnauthenticatedAuthSession,
93
103
  UpdateMemberGroupsInput,
94
104
  UpdateMemberGroupsResult,
95
105
  } from "./types"
@@ -100,7 +110,6 @@ import {
100
110
  resolveAuthConfig,
101
111
  resolveAuthSessionAudience,
102
112
  resolveInvitationExpiresAt,
103
- sanitizeReturnTo,
104
113
  } from "./validation"
105
114
 
106
115
  export {
@@ -117,6 +126,12 @@ export {
117
126
  const SESSION_TOUCH_INTERVAL_MS = 60_000
118
127
  const ACCESS_TOKEN_TOUCH_INTERVAL_MS = 60_000
119
128
 
129
+ // A native client's access token is sent on every request, so a leaked one must stop working soon.
130
+ const BEARER_ACCESS_TOKEN_TTL_MS = 15 * 60_000
131
+ // A replaced refresh token still rotates this long after its replacement, so a client whose refresh
132
+ // response was lost can retry. Later, it means a copy is in use and the session is revoked.
133
+ const REFRESH_REUSE_GRACE_MS = 60_000
134
+
120
135
  export interface AuthRuntimeOptions {
121
136
  readonly projectId: string
122
137
  readonly storage: Storage
@@ -211,7 +226,7 @@ export class AuthRuntime {
211
226
  ): Promise<AuthSessionResult>
212
227
  async getSession(
213
228
  request: Request,
214
- options: AuthSessionResolutionOptions & { readonly credentialSource: "accessToken" | "any" }
229
+ options: AuthSessionResolutionOptions & { readonly credentialSource: "any" }
215
230
  ): Promise<AuthRequestResult>
216
231
  async getSession(
217
232
  request: Request,
@@ -226,22 +241,20 @@ export class AuthRuntime {
226
241
  }
227
242
 
228
243
  const audience = resolveAuthSessionAudience(options.audience)
229
- const credentialSource = options.credentialSource ?? "session"
230
244
 
231
- if (credentialSource !== "session") {
232
- const authorizationHeader = request.headers.get("authorization")
245
+ if (options.credentialSource === "any") {
233
246
  const tokenValue = getBearerAccessTokenValue(request)
247
+ if (tokenValue?.startsWith(SESSION_ACCESS_TOKEN_PREFIX)) {
248
+ return this.resolveBearerSession(tokenValue)
249
+ }
234
250
  if (tokenValue) {
235
251
  return this.resolveAccessTokenSession(request, tokenValue)
236
252
  }
237
253
 
238
- if (authorizationHeader) {
254
+ // An Authorization header that is not a usable token never falls back to the cookie.
255
+ if (request.headers.get("authorization")) {
239
256
  return { authenticated: false, reason: "invalid_access_token" }
240
257
  }
241
-
242
- if (credentialSource === "accessToken") {
243
- return { authenticated: false, reason: "missing_access_token" }
244
- }
245
258
  }
246
259
 
247
260
  return this.resolveCookieSession(request, audience, options.sessionActivity)
@@ -269,6 +282,7 @@ export class AuthRuntime {
269
282
  const cached = this.sessionCache?.get({
270
283
  sessionId: parts.sessionId,
271
284
  tokenHash,
285
+ transport: "cookie",
272
286
  audience,
273
287
  nowMs,
274
288
  })
@@ -311,48 +325,142 @@ export class AuthRuntime {
311
325
  sessionRenewed = true
312
326
  }
313
327
 
314
- const user = await storage.users.getById({
315
- projectId: this.projectId,
316
- id: session.userId,
317
- })
328
+ if (!sessionRenewed) {
329
+ await this.touchSessionLastSeen(storage, session, now)
330
+ }
331
+ const result = await this.authenticateSession(storage, session, tokenHash, now)
332
+ return result.authenticated && sessionRenewed ? { ...result, sessionRenewed: true } : result
333
+ }
318
334
 
319
- if (!user) {
320
- return { authenticated: false, reason: "missing_user" }
335
+ private async resolveBearerSession(tokenValue: string): Promise<AuthSessionResult> {
336
+ const parts = parseBearerSessionToken(SESSION_ACCESS_TOKEN_PREFIX, tokenValue)
337
+ if (!parts) {
338
+ return { authenticated: false, reason: "invalid_access_token" }
321
339
  }
322
340
 
323
- if (user.status === "suspended") {
324
- return { authenticated: false, reason: "suspended_user" }
341
+ const tokenHash = hashSessionSecret(parts.sessionSecret)
342
+ const now = new Date()
343
+ const cached = this.sessionCache?.get({
344
+ sessionId: parts.sessionId,
345
+ tokenHash,
346
+ transport: "bearer",
347
+ nowMs: now.getTime(),
348
+ })
349
+ if (cached) {
350
+ return cached
325
351
  }
326
352
 
327
- const memberships = await storage.groupMemberships.listForUser({
353
+ const storage = this.requireAuthStorage()
354
+ const session = await storage.sessions.findValidByAccessTokenHash({
328
355
  projectId: this.projectId,
329
- userId: user.id,
356
+ id: parts.sessionId,
357
+ tokenHash,
358
+ now,
330
359
  })
360
+ if (!session) {
361
+ return { authenticated: false, reason: "invalid_access_token" }
362
+ }
331
363
 
332
- if (!sessionRenewed) {
333
- await this.touchSessionLastSeen(storage, session, now)
364
+ await this.touchSessionLastSeen(storage, session, now)
365
+ return this.authenticateSession(storage, session, tokenHash, now)
366
+ }
367
+
368
+ /** Resolve a live session's user and groups, the same way for cookies and bearer tokens. */
369
+ private async authenticateSession(
370
+ storage: AuthStorage,
371
+ session: SessionRecord,
372
+ tokenHash: string,
373
+ now: Date
374
+ ): Promise<AuthSessionResult> {
375
+ const member = await this.loadActiveUser(storage, session.userId)
376
+ if (!member.authenticated) {
377
+ return member
334
378
  }
335
379
 
336
380
  const result: AuthenticatedAuthSession = {
337
381
  authenticated: true,
338
382
  credentialSource: "session",
339
- principal: { type: "user", id: user.id },
340
- user,
383
+ principal: { type: "user", id: member.user.id },
384
+ user: member.user,
341
385
  session,
342
- groupIds: memberships.map((membership) => membership.groupId),
386
+ groupIds: member.groupIds,
343
387
  }
344
-
345
388
  this.sessionCache?.set({
346
- sessionId: parts.sessionId,
389
+ sessionId: session.id,
347
390
  tokenHash,
348
- audience,
391
+ audience: session.audience,
349
392
  session: result,
350
- nowMs,
393
+ nowMs: now.getTime(),
351
394
  sessionExpiresAtMs: session.expiresAt.getTime(),
352
395
  sessionAbsoluteExpiresAtMs: session.absoluteExpiresAt?.getTime(),
353
396
  })
397
+ return result
398
+ }
399
+
400
+ /**
401
+ * The session a native client starts when the user approves it, and its first tokens. The caller
402
+ * stores the session in the same transaction that consumes the approval.
403
+ */
404
+ prepareBearerSession(input: {
405
+ readonly userId: string
406
+ readonly clientName: string
407
+ readonly now: Date
408
+ }): {
409
+ readonly session: CompleteDeviceAuthorizationInput["session"]
410
+ readonly tokens: BearerSessionTokens
411
+ } {
412
+ const sessionId = `ses_${randomUUID()}`
413
+ const tokens = createBearerSessionTokens(sessionId)
414
+ return {
415
+ tokens,
416
+ session: {
417
+ id: sessionId,
418
+ projectId: this.projectId,
419
+ userId: input.userId,
420
+ strategyId: "device",
421
+ // A native session belongs to no web app. The column needs a value; nothing reads it.
422
+ audience: DEFAULT_AUTH_SESSION_AUDIENCE,
423
+ tokenHash: tokens.tokenHash,
424
+ createdAt: input.now,
425
+ ...this.createSessionDeadlines(input.now),
426
+ bearer: {
427
+ clientName: input.clientName,
428
+ accessExpiresAt: new Date(input.now.getTime() + BEARER_ACCESS_TOKEN_TTL_MS),
429
+ refreshTokenHash: tokens.refreshTokenHash,
430
+ },
431
+ },
432
+ }
433
+ }
354
434
 
355
- return sessionRenewed ? { ...result, sessionRenewed: true } : result
435
+ /**
436
+ * Trade a native client's refresh token for new tokens. Returns null when the token is not
437
+ * accepted; a replaced token presented after the grace window also revokes its session.
438
+ */
439
+ async refreshSession(
440
+ refreshToken: string
441
+ ): Promise<{ readonly tokens: BearerSessionTokens; readonly session: SessionRecord } | null> {
442
+ const parts = parseBearerSessionToken(SESSION_REFRESH_TOKEN_PREFIX, refreshToken)
443
+ if (!parts || !this.isEnabled()) {
444
+ return null
445
+ }
446
+
447
+ const now = new Date()
448
+ const tokens = createBearerSessionTokens(parts.sessionId)
449
+ const result = await this.requireAuthStorage().sessions.rotateRefreshToken({
450
+ projectId: this.projectId,
451
+ id: parts.sessionId,
452
+ refreshTokenHash: hashSessionSecret(parts.sessionSecret),
453
+ now,
454
+ reuseGraceMs: REFRESH_REUSE_GRACE_MS,
455
+ next: {
456
+ tokenHash: tokens.tokenHash,
457
+ accessExpiresAt: new Date(now.getTime() + BEARER_ACCESS_TOKEN_TTL_MS),
458
+ refreshTokenHash: tokens.refreshTokenHash,
459
+ expiresAt: new Date(now.getTime() + this.config.session.idleTimeoutMs),
460
+ },
461
+ })
462
+ this.sessionCache?.invalidate(parts.sessionId)
463
+ return result.status === "rotated" ? { tokens, session: result.session } : null
356
464
  }
357
465
 
358
466
  private canRenewSession(session: SessionRecord, nowMs: number): boolean {
@@ -397,41 +505,25 @@ export class AuthRuntime {
397
505
 
398
506
  await this.touchAccessTokenLastUsed(storage, accessToken, request, now)
399
507
 
400
- if (accessToken.subjectType === "user") {
401
- const user = await storage.users.getById({
402
- projectId: this.projectId,
403
- id: accessToken.subjectId,
404
- })
405
-
406
- if (!user) {
407
- return { authenticated: false, reason: "missing_user" }
508
+ if (accessToken.subject.type === "user") {
509
+ const member = await this.loadActiveUser(storage, accessToken.subject.id)
510
+ if (!member.authenticated) {
511
+ return member
408
512
  }
409
513
 
410
- if (user.status === "suspended") {
411
- return { authenticated: false, reason: "suspended_user" }
412
- }
413
-
414
- const memberships = await storage.groupMemberships.listForUser({
415
- projectId: this.projectId,
416
- userId: user.id,
417
- })
418
-
419
514
  return {
420
515
  authenticated: true,
421
516
  credentialSource: "accessToken",
422
- principal: { type: "user", id: user.id },
423
- user,
517
+ principal: { type: "user", id: member.user.id },
518
+ user: member.user,
424
519
  accessToken,
425
- groupIds: constrainTokenGroupIds(
426
- memberships.map((membership) => membership.groupId),
427
- accessToken
428
- ),
520
+ groupIds: constrainTokenGroupIds(member.groupIds, accessToken),
429
521
  }
430
522
  }
431
523
 
432
524
  const serviceAccount = await storage.serviceAccounts.getById({
433
525
  projectId: this.projectId,
434
- id: accessToken.subjectId,
526
+ id: accessToken.subject.id,
435
527
  })
436
528
 
437
529
  if (!serviceAccount) {
@@ -460,12 +552,32 @@ export class AuthRuntime {
460
552
  }
461
553
  }
462
554
 
463
- /**
464
- * Evict a session from the in-process cache. Call this when a session is revoked
465
- * (e.g. sign-out) so the cached result is dropped before its TTL would expire.
466
- */
467
- invalidateSession(sessionId: string): void {
468
- this.sessionCache?.invalidate(sessionId)
555
+ /** Resolve a user and their current groups the same way for every credential. */
556
+ private async loadActiveUser(
557
+ storage: AuthStorage,
558
+ userId: string
559
+ ): Promise<
560
+ | UnauthenticatedAuthSession
561
+ | { readonly authenticated: true; readonly user: UserRecord; readonly groupIds: string[] }
562
+ > {
563
+ const user = await storage.users.getById({ projectId: this.projectId, id: userId })
564
+ if (!user) {
565
+ return { authenticated: false, reason: "missing_user" }
566
+ }
567
+
568
+ if (user.status === "suspended") {
569
+ return { authenticated: false, reason: "suspended_user" }
570
+ }
571
+
572
+ const memberships = await storage.groupMemberships.listForUser({
573
+ projectId: this.projectId,
574
+ userId,
575
+ })
576
+ return {
577
+ authenticated: true,
578
+ user,
579
+ groupIds: memberships.map((membership) => membership.groupId),
580
+ }
469
581
  }
470
582
 
471
583
  // Best-effort refresh of `lastSeenAt` for the active-sessions view. Throttled,
@@ -503,109 +615,34 @@ export class AuthRuntime {
503
615
  id: accessToken.id,
504
616
  lastUsedAt: now,
505
617
  userAgent: request.headers.get("user-agent")?.trim() || undefined,
506
- ipAddress: resolveRequestIpAddress(request),
618
+ ipAddress: getRequestClientAddress(request),
507
619
  })
508
620
  } catch {
509
621
  // Touch is non-critical; ignore failures so auth still succeeds.
510
622
  }
511
623
  }
512
624
 
513
- async requirePrincipal(
514
- request: Request,
515
- options: AuthSessionResolutionOptions = {}
516
- ): Promise<Principal> {
517
- const session = await this.getSession(request, options)
518
- if (!session.authenticated) {
519
- throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
520
- }
521
-
522
- return session.principal
523
- }
524
-
525
- async requireUser(
526
- request: Request,
527
- options: AuthSessionResolutionOptions = {}
528
- ): Promise<AuthenticatedAuthSession> {
529
- const session = await this.getSession(request, { ...options, credentialSource: "session" })
530
- if (!session.authenticated) {
531
- throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
532
- }
533
-
534
- return session
535
- }
536
-
537
- async requireUserRequest(
538
- request: Request,
539
- options: AuthSessionResolutionOptions = {}
540
- ): Promise<AuthenticatedUserRequestSession> {
541
- const session = await this.getSession(request, { ...options, credentialSource: "any" })
542
- if (!session.authenticated) {
543
- throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
544
- }
545
-
546
- if (!isAuthenticatedUserRequestSession(session)) {
547
- throw new AuthRuntimeError("authorization_denied", "[Sixb] User authentication is required.")
548
- }
549
-
550
- return session
551
- }
552
-
553
- async createSecurityContext(
554
- request: Request,
555
- options: AuthSessionResolutionOptions = {}
556
- ): Promise<SecurityContext> {
557
- const session = await this.requireUser(request, options)
558
- return {
559
- principal: session.principal,
560
- sessionId: session.session.id,
561
- projectId: this.projectId,
562
- correlationId: resolveCorrelationId(request),
563
- }
564
- }
565
-
566
- /**
567
- * Resolve the authenticated principal's authorization context for a request.
568
- *
569
- * Grants resolve eagerly (`groups -> roles -> grants`, subtype-expanded), so the returned
570
- * context supports synchronous checks when Core binds a request execution SDK.
571
- */
572
- async createAuthorizationContext(
573
- request: Request,
574
- options: AuthSessionResolutionOptions = {}
575
- ): Promise<AuthorizationContext> {
576
- const session = await this.getSession(request, options)
577
- if (!session.authenticated) {
578
- throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
579
- }
580
-
581
- return this.contextFromSession(session)
582
- }
583
-
584
625
  /**
585
- * Build an authorization context from an already-resolved session, so callers
586
- * that resolve the session themselves (e.g. the server auth guard) don't read
587
- * the request twice.
626
+ * Resolve a caller's authorization context. Grants resolve eagerly
627
+ * (`groups -> roles -> grants`, subtype-expanded), so checks against it are synchronous.
588
628
  */
589
629
  contextFromSession(session: AuthenticatedRequestAuthSession): AuthorizationContext {
590
630
  return resolveAuthorizationContext({
591
631
  principal: session.principal,
592
- sessionId: session.credentialSource === "session" ? session.session.id : undefined,
632
+ sessionId: callerSessionId(session),
593
633
  groupIds: session.groupIds,
594
634
  roles: this.security.listResolvedRoles(),
595
635
  })
596
636
  }
597
637
 
598
638
  async listPersonalAccessTokens(
599
- request: Request,
600
- options: AuthSessionResolutionOptions = {}
639
+ caller: AuthenticatedUserRequestSession
601
640
  ): Promise<ListPersonalAccessTokensResult> {
602
- const session = await this.requireUserRequest(request, options)
603
641
  const storage = this.requireAuthStorage()
604
642
  const result = await storage.accessTokens.list({
605
643
  projectId: this.projectId,
606
644
  kind: "personal",
607
- subjectType: "user",
608
- subjectId: session.user.id,
645
+ subject: { type: "user", id: caller.user.id },
609
646
  includeRevoked: true,
610
647
  order: "desc",
611
648
  limit: 100,
@@ -615,14 +652,12 @@ export class AuthRuntime {
615
652
  }
616
653
 
617
654
  async createPersonalAccessToken(
618
- request: Request,
619
- input: CreatePersonalAccessTokenInput,
620
- options: AuthSessionResolutionOptions = {}
655
+ caller: AuthenticatedUserRequestSession,
656
+ input: CreatePersonalAccessTokenInput
621
657
  ): Promise<CreateAccessTokenResult> {
622
- const session = await this.requireUserRequest(request, options)
623
658
  const storage = this.requireAuthStorage()
624
659
  // A personal token can only carry groups the caller currently belongs to.
625
- const groupIds = constrainRequestedGroupIds(input.groupIds, session.groupIds, {
660
+ const groupIds = constrainRequestedGroupIds(input.groupIds, caller.groupIds, {
626
661
  subject: "personal access token",
627
662
  })
628
663
  const credential = createAccessTokenCredential("personal")
@@ -631,12 +666,11 @@ export class AuthRuntime {
631
666
  projectId: this.projectId,
632
667
  name: input.name,
633
668
  kind: "personal",
634
- subjectType: "user",
635
- subjectId: session.user.id,
669
+ subject: { type: "user", id: caller.user.id },
636
670
  tokenHash: credential.tokenHash,
637
671
  groupIds,
638
- createdByPrincipal: session.principal,
639
- createdBySessionId: session.credentialSource === "session" ? session.session.id : undefined,
672
+ createdByPrincipal: caller.principal,
673
+ createdBySessionId: callerSessionId(caller),
640
674
  createdAt: new Date(),
641
675
  expiresAt: input.expiresAt,
642
676
  })
@@ -645,11 +679,9 @@ export class AuthRuntime {
645
679
  }
646
680
 
647
681
  async revokePersonalAccessToken(
648
- request: Request,
649
- input: RevokePersonalAccessTokenInput,
650
- options: AuthSessionResolutionOptions = {}
682
+ caller: AuthenticatedUserRequestSession,
683
+ input: RevokePersonalAccessTokenInput
651
684
  ): Promise<RevokeAccessTokenResult> {
652
- const session = await this.requireUserRequest(request, options)
653
685
  const storage = this.requireAuthStorage()
654
686
  const token = await storage.accessTokens.getById({
655
687
  projectId: this.projectId,
@@ -660,8 +692,8 @@ export class AuthRuntime {
660
692
  if (
661
693
  !token ||
662
694
  token.kind !== "personal" ||
663
- token.subjectType !== "user" ||
664
- token.subjectId !== session.user.id
695
+ token.subject.type !== "user" ||
696
+ token.subject.id !== caller.user.id
665
697
  ) {
666
698
  throw missingAccessTokenError(input.tokenId, this.projectId)
667
699
  }
@@ -676,10 +708,8 @@ export class AuthRuntime {
676
708
  }
677
709
 
678
710
  async listServiceAccounts(
679
- request: Request,
680
- options: AuthSessionResolutionOptions = {}
711
+ caller: AuthenticatedUserRequestSession
681
712
  ): Promise<ListServiceAccountsResult> {
682
- const session = await this.requireUserRequest(request, options)
683
713
  const storage = this.requireAuthStorage()
684
714
  const result = await storage.serviceAccounts.list({
685
715
  projectId: this.projectId,
@@ -697,21 +727,19 @@ export class AuthRuntime {
697
727
  // listing never leaks the groups of more-privileged accounts.
698
728
  return {
699
729
  serviceAccounts: withGroups.filter(({ groupIds }) =>
700
- callerCanManageServiceAccount(session.groupIds, groupIds)
730
+ callerCanManageServiceAccount(caller.groupIds, groupIds)
701
731
  ),
702
732
  }
703
733
  }
704
734
 
705
735
  async createServiceAccount(
706
- request: Request,
707
- input: CreateServiceAccountInput,
708
- options: AuthSessionResolutionOptions = {}
736
+ caller: AuthenticatedUserRequestSession,
737
+ input: CreateServiceAccountInput
709
738
  ): Promise<CreateServiceAccountResult> {
710
- const session = await this.requireUserRequest(request, options)
711
739
  const storage = this.requireAuthStorage()
712
740
  // A caller can only place a service account in groups it itself belongs to.
713
741
  const groupIds =
714
- constrainRequestedGroupIds(input.groupIds, session.groupIds, {
742
+ constrainRequestedGroupIds(input.groupIds, caller.groupIds, {
715
743
  subject: "service account",
716
744
  }) ?? []
717
745
  const now = new Date()
@@ -720,8 +748,8 @@ export class AuthRuntime {
720
748
  projectId: this.projectId,
721
749
  name: input.name,
722
750
  description: input.description,
723
- createdByPrincipal: session.principal,
724
- createdBySessionId: session.credentialSource === "session" ? session.session.id : undefined,
751
+ createdByPrincipal: caller.principal,
752
+ createdBySessionId: callerSessionId(caller),
725
753
  createdAt: now,
726
754
  updatedAt: now,
727
755
  })
@@ -742,15 +770,13 @@ export class AuthRuntime {
742
770
  }
743
771
 
744
772
  async disableServiceAccount(
745
- request: Request,
746
- input: DisableServiceAccountInput,
747
- options: AuthSessionResolutionOptions = {}
773
+ caller: AuthenticatedUserRequestSession,
774
+ input: DisableServiceAccountInput
748
775
  ): Promise<DisableServiceAccountResult> {
749
- const session = await this.requireUserRequest(request, options)
750
776
  const storage = this.requireAuthStorage()
751
777
  const { serviceAccount, groupIds } = await this.requireManageableServiceAccount(
752
778
  storage,
753
- session.groupIds,
779
+ caller.groupIds,
754
780
  input.serviceAccountId
755
781
  )
756
782
  const updated = await storage.serviceAccounts.update({
@@ -764,22 +790,19 @@ export class AuthRuntime {
764
790
  }
765
791
 
766
792
  async listServiceAccountAccessTokens(
767
- request: Request,
768
- input: ListServiceAccountAccessTokensInput,
769
- options: AuthSessionResolutionOptions = {}
793
+ caller: AuthenticatedUserRequestSession,
794
+ input: ListServiceAccountAccessTokensInput
770
795
  ): Promise<ListServiceAccountAccessTokensResult> {
771
- const session = await this.requireUserRequest(request, options)
772
796
  const storage = this.requireAuthStorage()
773
797
  const { serviceAccount } = await this.requireManageableServiceAccount(
774
798
  storage,
775
- session.groupIds,
799
+ caller.groupIds,
776
800
  input.serviceAccountId
777
801
  )
778
802
  const result = await storage.accessTokens.list({
779
803
  projectId: this.projectId,
780
804
  kind: "serviceAccount",
781
- subjectType: "serviceAccount",
782
- subjectId: serviceAccount.id,
805
+ subject: { type: "serviceAccount", id: serviceAccount.id },
783
806
  includeRevoked: true,
784
807
  order: "desc",
785
808
  limit: 100,
@@ -789,14 +812,12 @@ export class AuthRuntime {
789
812
  }
790
813
 
791
814
  async createServiceAccountAccessToken(
792
- request: Request,
793
- input: CreateServiceAccountAccessTokenInput,
794
- options: AuthSessionResolutionOptions = {}
815
+ caller: AuthenticatedUserRequestSession,
816
+ input: CreateServiceAccountAccessTokenInput
795
817
  ): Promise<CreateServiceAccountAccessTokenResult> {
796
- const session = await this.requireUserRequest(request, options)
797
818
  const storage = this.requireAuthStorage()
798
819
  const { serviceAccount, groupIds: serviceAccountGroupIds } =
799
- await this.requireManageableServiceAccount(storage, session.groupIds, input.serviceAccountId)
820
+ await this.requireManageableServiceAccount(storage, caller.groupIds, input.serviceAccountId)
800
821
 
801
822
  if (serviceAccount.status === "suspended") {
802
823
  throw new AuthStorageError(
@@ -817,12 +838,11 @@ export class AuthRuntime {
817
838
  projectId: this.projectId,
818
839
  name: input.name,
819
840
  kind: "serviceAccount",
820
- subjectType: "serviceAccount",
821
- subjectId: serviceAccount.id,
841
+ subject: { type: "serviceAccount", id: serviceAccount.id },
822
842
  tokenHash: credential.tokenHash,
823
843
  groupIds,
824
- createdByPrincipal: session.principal,
825
- createdBySessionId: session.credentialSource === "session" ? session.session.id : undefined,
844
+ createdByPrincipal: caller.principal,
845
+ createdBySessionId: callerSessionId(caller),
826
846
  createdAt: new Date(),
827
847
  expiresAt: input.expiresAt,
828
848
  })
@@ -831,15 +851,13 @@ export class AuthRuntime {
831
851
  }
832
852
 
833
853
  async revokeServiceAccountAccessToken(
834
- request: Request,
835
- input: RevokeServiceAccountAccessTokenInput,
836
- options: AuthSessionResolutionOptions = {}
854
+ caller: AuthenticatedUserRequestSession,
855
+ input: RevokeServiceAccountAccessTokenInput
837
856
  ): Promise<RevokeServiceAccountAccessTokenResult> {
838
- const session = await this.requireUserRequest(request, options)
839
857
  const storage = this.requireAuthStorage()
840
858
  const { serviceAccount } = await this.requireManageableServiceAccount(
841
859
  storage,
842
- session.groupIds,
860
+ caller.groupIds,
843
861
  input.serviceAccountId
844
862
  )
845
863
  const token = await storage.accessTokens.getById({
@@ -850,8 +868,8 @@ export class AuthRuntime {
850
868
  if (
851
869
  !token ||
852
870
  token.kind !== "serviceAccount" ||
853
- token.subjectType !== "serviceAccount" ||
854
- token.subjectId !== serviceAccount.id
871
+ token.subject.type !== "serviceAccount" ||
872
+ token.subject.id !== serviceAccount.id
855
873
  ) {
856
874
  throw missingAccessTokenError(input.tokenId, this.projectId)
857
875
  }
@@ -913,11 +931,9 @@ export class AuthRuntime {
913
931
  }
914
932
 
915
933
  async getInvitationOptions(
916
- request: Request,
917
- options: AuthSessionResolutionOptions = {}
934
+ caller: AuthenticatedUserRequestSession
918
935
  ): Promise<GetInvitationOptionsResult> {
919
- const session = await this.requireUser(request, options)
920
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
936
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
921
937
  const inviteScope = scope.operations.invite
922
938
  const groups = this.scopedGroupOptions(inviteScope.groupIds)
923
939
  const hasInviteMembershipPolicy = inviteScope.policyIds.length > 0
@@ -950,11 +966,9 @@ export class AuthRuntime {
950
966
  }
951
967
 
952
968
  async getMembershipOptions(
953
- request: Request,
954
- options: AuthSessionResolutionOptions = {}
969
+ caller: AuthenticatedUserRequestSession
955
970
  ): Promise<GetMembershipOptionsResult> {
956
- const session = await this.requireUser(request, options)
957
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
971
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
958
972
 
959
973
  return {
960
974
  // The member-admin edit-groups dialog assigns from the `assignGroups` scope.
@@ -964,13 +978,11 @@ export class AuthRuntime {
964
978
  }
965
979
 
966
980
  async listMembers(
967
- request: Request,
968
- input: ListMembersInput = {},
969
- options: AuthSessionResolutionOptions = {}
981
+ caller: AuthenticatedUserRequestSession,
982
+ input: ListMembersInput = {}
970
983
  ): Promise<ListMembersResult> {
971
- const session = await this.requireUser(request, options)
972
984
  const storage = this.requireAuthStorage()
973
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
985
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
974
986
  const { limit, offset } = normalizePagination(input)
975
987
 
976
988
  // First slice loads all users, attaches groups, then filters and paginates in
@@ -980,9 +992,9 @@ export class AuthRuntime {
980
992
 
981
993
  const members: MemberSummary[] = []
982
994
  for (const user of result.users) {
983
- const groupIds = (
995
+ const { groupIds, managedGroupIds } = memberGroups(
984
996
  await storage.groupMemberships.listForUser({ projectId: this.projectId, userId: user.id })
985
- ).map((membership) => membership.groupId)
997
+ )
986
998
 
987
999
  // Visibility is scope-based: a member is listed when the caller can assign
988
1000
  // groups or suspend over the member's current groups. This keeps out-of-scope
@@ -993,10 +1005,11 @@ export class AuthRuntime {
993
1005
  continue
994
1006
  }
995
1007
 
996
- const isSelf = user.id === session.user.id
1008
+ const isSelf = user.id === caller.user.id
997
1009
  members.push({
998
1010
  user,
999
1011
  groupIds,
1012
+ managedGroupIds,
1000
1013
  capabilities: {
1001
1014
  assignGroups: canAssignGroups,
1002
1015
  // A caller cannot suspend themselves; reactivation only applies to a
@@ -1012,24 +1025,21 @@ export class AuthRuntime {
1012
1025
  }
1013
1026
 
1014
1027
  async updateMemberGroups(
1015
- request: Request,
1016
- input: UpdateMemberGroupsInput,
1017
- options: AuthSessionResolutionOptions = {}
1028
+ caller: AuthenticatedUserRequestSession,
1029
+ input: UpdateMemberGroupsInput
1018
1030
  ): Promise<UpdateMemberGroupsResult> {
1019
- const session = await this.requireUser(request, options)
1020
1031
  const storage = this.requireAuthStorage()
1021
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
1032
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
1022
1033
  const userId = assertNonEmpty(input.userId, "User id")
1023
1034
 
1024
1035
  // The target must exist and every group it currently holds must be assignable
1025
1036
  // by the caller. Missing and out-of-scope targets raise the same error so a
1026
1037
  // caller cannot probe which users exist.
1027
- const { user, groupIds: currentGroupIds } = await this.requireManageableMember(
1028
- storage,
1029
- scope,
1030
- "assignGroups",
1031
- userId
1032
- )
1038
+ const {
1039
+ user,
1040
+ groupIds: currentGroupIds,
1041
+ managedGroupIds: currentManagedGroupIds,
1042
+ } = await this.requireManageableMember(storage, scope, "assignGroups", userId)
1033
1043
 
1034
1044
  const requestedGroupIds = this.resolveMemberGroupIds(input.groupIds)
1035
1045
  // Every requested group must fall within the caller's assign scope.
@@ -1046,9 +1056,18 @@ export class AuthRuntime {
1046
1056
  const additions = requestedGroupIds.filter((groupId) => !current.has(groupId))
1047
1057
  const removals = currentGroupIds.filter((groupId) => !requested.has(groupId))
1048
1058
 
1059
+ // The identity provider would restore these at the user's next sign-in.
1060
+ const managedRemovals = removals.filter((groupId) => currentManagedGroupIds.includes(groupId))
1061
+ if (managedRemovals.length > 0) {
1062
+ throw new AuthRuntimeError(
1063
+ "invalid_auth_input",
1064
+ `[Sixb] Group(s) ${managedRemovals.join(", ")} are managed by the identity provider. Change them there; they sync at the user's next sign-in.`
1065
+ )
1066
+ }
1067
+
1049
1068
  // Self-protection: a caller may add in-scope groups to themselves but may not
1050
1069
  // remove any of their own current groups, so they cannot lock themselves out.
1051
- if (user.id === session.user.id && removals.length > 0) {
1070
+ if (user.id === caller.user.id && removals.length > 0) {
1052
1071
  throw new AuthRuntimeError(
1053
1072
  "authorization_denied",
1054
1073
  "[Sixb] The current user cannot remove their own groups."
@@ -1075,27 +1094,30 @@ export class AuthRuntime {
1075
1094
 
1076
1095
  // The user's cached session carries its old groups; drop it so the next
1077
1096
  // request resolves the updated membership.
1078
- this.invalidateUserSessions(user.id)
1097
+ this.sessionCache?.invalidateUser(user.id)
1079
1098
 
1080
- const groupIds = (
1099
+ const { groupIds, managedGroupIds } = memberGroups(
1081
1100
  await storage.groupMemberships.listForUser({ projectId: this.projectId, userId: user.id })
1082
- ).map((membership) => membership.groupId)
1101
+ )
1083
1102
 
1084
- return { user, groupIds }
1103
+ return { user, groupIds, managedGroupIds }
1085
1104
  }
1086
1105
 
1087
1106
  async suspendMember(
1088
- request: Request,
1089
- input: SuspendMemberInput,
1090
- options: AuthSessionResolutionOptions = {}
1107
+ caller: AuthenticatedUserRequestSession,
1108
+ input: SuspendMemberInput
1091
1109
  ): Promise<SuspendMemberResult> {
1092
- const session = await this.requireUser(request, options)
1093
1110
  const storage = this.requireAuthStorage()
1094
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
1111
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
1095
1112
  const userId = assertNonEmpty(input.userId, "User id")
1096
- const { user, groupIds } = await this.requireManageableMember(storage, scope, "suspend", userId)
1113
+ const { user, groupIds, managedGroupIds } = await this.requireManageableMember(
1114
+ storage,
1115
+ scope,
1116
+ "suspend",
1117
+ userId
1118
+ )
1097
1119
 
1098
- if (user.id === session.user.id) {
1120
+ if (user.id === caller.user.id) {
1099
1121
  throw new AuthRuntimeError(
1100
1122
  "authorization_denied",
1101
1123
  "[Sixb] The current user cannot suspend themselves."
@@ -1109,21 +1131,24 @@ export class AuthRuntime {
1109
1131
  })
1110
1132
  // Storage revoked the user's sessions; drop cached copies so the suspended
1111
1133
  // user stops authenticating immediately.
1112
- this.invalidateUserSessions(user.id)
1134
+ this.sessionCache?.invalidateUser(user.id)
1113
1135
 
1114
- return { user: suspended, groupIds }
1136
+ return { user: suspended, groupIds, managedGroupIds }
1115
1137
  }
1116
1138
 
1117
1139
  async reactivateMember(
1118
- request: Request,
1119
- input: ReactivateMemberInput,
1120
- options: AuthSessionResolutionOptions = {}
1140
+ caller: AuthenticatedUserRequestSession,
1141
+ input: ReactivateMemberInput
1121
1142
  ): Promise<ReactivateMemberResult> {
1122
- const session = await this.requireUser(request, options)
1123
1143
  const storage = this.requireAuthStorage()
1124
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
1144
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
1125
1145
  const userId = assertNonEmpty(input.userId, "User id")
1126
- const { user, groupIds } = await this.requireManageableMember(storage, scope, "suspend", userId)
1146
+ const { user, groupIds, managedGroupIds } = await this.requireManageableMember(
1147
+ storage,
1148
+ scope,
1149
+ "suspend",
1150
+ userId
1151
+ )
1127
1152
 
1128
1153
  // Reactivation restores access but not sessions; the user signs in again.
1129
1154
  const reactivated = await storage.users.updateStatus({
@@ -1133,12 +1158,52 @@ export class AuthRuntime {
1133
1158
  updatedAt: new Date(),
1134
1159
  })
1135
1160
 
1136
- return { user: reactivated, groupIds }
1161
+ return { user: reactivated, groupIds, managedGroupIds }
1162
+ }
1163
+
1164
+ /** The caller's active sessions across every audience, most recently active first. */
1165
+ async listSessions(caller: AuthenticatedUserRequestSession): Promise<readonly SessionRecord[]> {
1166
+ return this.requireAuthStorage().sessions.listActiveByUserId({
1167
+ projectId: this.projectId,
1168
+ userId: caller.user.id,
1169
+ now: new Date(),
1170
+ })
1171
+ }
1172
+
1173
+ /**
1174
+ * End one of the caller's own sessions. Returns false for a missing session or one that belongs
1175
+ * to another user, so a caller cannot probe other accounts.
1176
+ */
1177
+ async revokeSession(
1178
+ caller: AuthenticatedUserRequestSession,
1179
+ sessionId: string
1180
+ ): Promise<boolean> {
1181
+ const storage = this.requireAuthStorage()
1182
+ const session = await storage.sessions.getById({ projectId: this.projectId, id: sessionId })
1183
+ if (!session || session.userId !== caller.user.id) {
1184
+ return false
1185
+ }
1186
+
1187
+ await storage.sessions.revoke({
1188
+ projectId: this.projectId,
1189
+ id: sessionId,
1190
+ revokedAt: new Date(),
1191
+ })
1192
+ this.sessionCache?.invalidate(sessionId)
1193
+ return true
1137
1194
  }
1138
1195
 
1139
- /** Drop cached sessions for a user after a membership or status change. */
1140
- invalidateUserSessions(userId: string): void {
1141
- this.sessionCache?.invalidateUser(userId)
1196
+ /** End every active session of the caller, across audiences. Returns how many ended. */
1197
+ async revokeAllSessions(caller: AuthenticatedUserRequestSession): Promise<number> {
1198
+ const revoked = await this.requireAuthStorage().sessions.revokeActiveForUser({
1199
+ projectId: this.projectId,
1200
+ userId: caller.user.id,
1201
+ revokedAt: new Date(),
1202
+ })
1203
+ for (const session of revoked) {
1204
+ this.sessionCache?.invalidate(session.id)
1205
+ }
1206
+ return revoked.length
1142
1207
  }
1143
1208
 
1144
1209
  private scopedGroupOptions(groupIds: ReadonlySet<string>): InvitationGroupOption[] {
@@ -1165,13 +1230,15 @@ export class AuthRuntime {
1165
1230
  scope: MembershipPolicyScope,
1166
1231
  operation: MembershipOperation,
1167
1232
  userId: string
1168
- ): Promise<{ readonly user: UserRecord; readonly groupIds: readonly string[] }> {
1233
+ ): Promise<{
1234
+ readonly user: UserRecord
1235
+ readonly groupIds: readonly string[]
1236
+ readonly managedGroupIds: readonly string[]
1237
+ }> {
1169
1238
  const user = await storage.users.getById({ projectId: this.projectId, id: userId })
1170
- const groupIds = user
1171
- ? (await storage.groupMemberships.listForUser({ projectId: this.projectId, userId })).map(
1172
- (membership) => membership.groupId
1173
- )
1174
- : []
1239
+ const { groupIds, managedGroupIds } = memberGroups(
1240
+ user ? await storage.groupMemberships.listForUser({ projectId: this.projectId, userId }) : []
1241
+ )
1175
1242
 
1176
1243
  if (!user || !canPerformMembershipOperation(scope, operation, groupIds)) {
1177
1244
  throw new AuthStorageError(
@@ -1180,7 +1247,7 @@ export class AuthRuntime {
1180
1247
  )
1181
1248
  }
1182
1249
 
1183
- return { user, groupIds }
1250
+ return { user, groupIds, managedGroupIds }
1184
1251
  }
1185
1252
 
1186
1253
  private resolveMemberGroupIds(input: readonly string[]): readonly string[] {
@@ -1197,15 +1264,14 @@ export class AuthRuntime {
1197
1264
  }
1198
1265
 
1199
1266
  async invite(
1200
- request: Request,
1267
+ caller: AuthenticatedUserRequestSession,
1201
1268
  input: InviteUserInput,
1202
- options: InviteUserOptions = {}
1269
+ destination: InvitationDestination
1203
1270
  ): Promise<InviteUserResult> {
1204
- const session = await this.requireUser(request, options)
1205
1271
  const authStorage = this.requireAuthStorage()
1206
1272
  const now = new Date()
1207
1273
  const groupIds = this.resolveInviteGroupIds(input)
1208
- this.assertCanManageInvitationGroups(session.groupIds, groupIds)
1274
+ this.assertCanManageInvitationGroups(caller.groupIds, groupIds)
1209
1275
 
1210
1276
  const strategy = this.getStrategy()
1211
1277
  if (!isInvitationDeliveryAuthStrategy(strategy)) {
@@ -1216,18 +1282,15 @@ export class AuthRuntime {
1216
1282
  }
1217
1283
 
1218
1284
  await this.assertCanInviteRecipient(strategy, authStorage, input.email, now)
1219
- const deliveryAudience = resolveAuthSessionAudience(
1220
- options.delivery?.audience ?? options.audience
1221
- )
1222
- this.assertInvitationApplicationAccess(groupIds, deliveryAudience)
1285
+ this.assertInvitationApplicationAccess(groupIds, destination.audience)
1223
1286
 
1224
1287
  const invitation = await authStorage.invitations.createOrUpdateActive({
1225
1288
  id: `inv_${randomUUID()}`,
1226
1289
  projectId: this.projectId,
1227
1290
  email: input.email,
1228
1291
  groupIds,
1229
- createdByPrincipal: session.principal,
1230
- createdBySessionId: session.session.id,
1292
+ createdByPrincipal: caller.principal,
1293
+ createdBySessionId: callerSessionId(caller),
1231
1294
  createdAt: now,
1232
1295
  updatedAt: now,
1233
1296
  expiresAt: resolveInvitationExpiresAt(input.expiresAt, now),
@@ -1239,9 +1302,9 @@ export class AuthRuntime {
1239
1302
  projectId: this.projectId,
1240
1303
  authStorage,
1241
1304
  invitation,
1242
- audience: deliveryAudience,
1243
- returnTo: options.delivery?.returnTo ?? sanitizeReturnTo(input.returnTo),
1244
- requestOrigin: options.delivery?.requestOrigin ?? new URL(request.url).origin,
1305
+ audience: destination.audience,
1306
+ returnTo: destination.returnTo,
1307
+ requestOrigin: destination.requestOrigin,
1245
1308
  revealLink: input.revealLink,
1246
1309
  now,
1247
1310
  })
@@ -1270,11 +1333,9 @@ export class AuthRuntime {
1270
1333
  }
1271
1334
 
1272
1335
  async listInvitations(
1273
- request: Request,
1274
- input: ListInvitationsInput = {},
1275
- options: AuthSessionResolutionOptions = {}
1336
+ caller: AuthenticatedUserRequestSession,
1337
+ input: ListInvitationsInput = {}
1276
1338
  ): Promise<ListInvitationsResult> {
1277
- const session = await this.requireUser(request, options)
1278
1339
  const authStorage = this.requireAuthStorage()
1279
1340
  const { limit, offset } = normalizePagination(input)
1280
1341
  const result = await authStorage.invitations.list({
@@ -1283,7 +1344,7 @@ export class AuthRuntime {
1283
1344
  statuses: input.statuses,
1284
1345
  order: input.order,
1285
1346
  })
1286
- const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
1347
+ const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
1287
1348
  const manageable = result.invitations.filter((invitation) =>
1288
1349
  canPerformMembershipOperation(scope, "invite", invitation.groupIds)
1289
1350
  )
@@ -1297,11 +1358,9 @@ export class AuthRuntime {
1297
1358
  }
1298
1359
 
1299
1360
  async revokeInvitation(
1300
- request: Request,
1301
- input: RevokeInvitationInput,
1302
- options: AuthSessionResolutionOptions = {}
1361
+ caller: AuthenticatedUserRequestSession,
1362
+ input: RevokeInvitationInput
1303
1363
  ): Promise<RevokeInvitationResult> {
1304
- const session = await this.requireUser(request, options)
1305
1364
  const authStorage = this.requireAuthStorage()
1306
1365
  const invitationId = assertNonEmpty(input.invitationId, "Invitation id")
1307
1366
  const invitation = await authStorage.invitations.getById({
@@ -1316,7 +1375,7 @@ export class AuthRuntime {
1316
1375
  )
1317
1376
  }
1318
1377
 
1319
- this.assertCanManageInvitationGroups(session.groupIds, invitation.groupIds)
1378
+ this.assertCanManageInvitationGroups(caller.groupIds, invitation.groupIds)
1320
1379
 
1321
1380
  if (invitation.status !== "pending") {
1322
1381
  throw new AuthRuntimeError(
@@ -1488,24 +1547,21 @@ export class AuthRuntime {
1488
1547
  }
1489
1548
  }
1490
1549
 
1491
- function resolveCorrelationId(request: Request): string {
1492
- return (
1493
- request.headers.get("x-correlation-id") ?? request.headers.get("x-request-id") ?? randomUUID()
1494
- )
1495
- }
1496
-
1497
- function isAuthenticatedUserRequestSession(
1498
- session: AuthenticatedRequestAuthSession
1499
- ): session is AuthenticatedUserRequestSession {
1500
- return session.principal.type === "user"
1550
+ function memberGroups(memberships: readonly GroupMembershipRecord[]): {
1551
+ readonly groupIds: readonly string[]
1552
+ readonly managedGroupIds: readonly string[]
1553
+ } {
1554
+ return {
1555
+ groupIds: memberships.map((membership) => membership.groupId),
1556
+ managedGroupIds: memberships
1557
+ .filter((membership) => membership.source === "directory")
1558
+ .map((membership) => membership.groupId),
1559
+ }
1501
1560
  }
1502
1561
 
1503
- function resolveRequestIpAddress(request: Request): string | undefined {
1504
- return (
1505
- request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
1506
- request.headers.get("x-real-ip")?.trim() ||
1507
- undefined
1508
- )
1562
+ /** The interactive session a caller authenticated with, when it used one. */
1563
+ function callerSessionId(caller: AuthenticatedRequestAuthSession): string | undefined {
1564
+ return caller.credentialSource === "session" ? caller.session.id : undefined
1509
1565
  }
1510
1566
 
1511
1567
  function constrainTokenGroupIds(