@nebulr-group/bridge-cli 0.6.6 → 1.0.0-alpha.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 (293) hide show
  1. package/README.md +93 -460
  2. package/dist/api/accounts.d.ts +69 -0
  3. package/dist/api/accounts.d.ts.map +1 -0
  4. package/dist/api/accounts.js +42 -0
  5. package/dist/api/accounts.js.map +1 -0
  6. package/dist/api/apps.d.ts +50 -0
  7. package/dist/api/apps.d.ts.map +1 -0
  8. package/dist/api/apps.js +62 -0
  9. package/dist/api/apps.js.map +1 -0
  10. package/dist/api/billing.d.ts +131 -0
  11. package/dist/api/billing.d.ts.map +1 -0
  12. package/dist/api/billing.js +42 -0
  13. package/dist/api/billing.js.map +1 -0
  14. package/dist/api/flags.d.ts +73 -0
  15. package/dist/api/flags.d.ts.map +1 -0
  16. package/dist/api/flags.js +108 -0
  17. package/dist/api/flags.js.map +1 -0
  18. package/dist/api/keys.d.ts +48 -0
  19. package/dist/api/keys.d.ts.map +1 -0
  20. package/dist/api/keys.js +87 -0
  21. package/dist/api/keys.js.map +1 -0
  22. package/dist/auth/browser-open.d.ts +21 -4
  23. package/dist/auth/browser-open.d.ts.map +1 -1
  24. package/dist/auth/browser-open.js +45 -65
  25. package/dist/auth/browser-open.js.map +1 -1
  26. package/dist/auth/lock.d.ts +8 -0
  27. package/dist/auth/lock.d.ts.map +1 -0
  28. package/dist/auth/lock.js +43 -0
  29. package/dist/auth/lock.js.map +1 -0
  30. package/dist/auth/loopback.d.ts +8 -12
  31. package/dist/auth/loopback.d.ts.map +1 -1
  32. package/dist/auth/loopback.js +40 -45
  33. package/dist/auth/loopback.js.map +1 -1
  34. package/dist/auth/session.d.ts +61 -0
  35. package/dist/auth/session.d.ts.map +1 -0
  36. package/dist/auth/session.js +142 -0
  37. package/dist/auth/session.js.map +1 -0
  38. package/dist/auth/sign-in.d.ts +67 -0
  39. package/dist/auth/sign-in.d.ts.map +1 -0
  40. package/dist/auth/sign-in.js +113 -0
  41. package/dist/auth/sign-in.js.map +1 -0
  42. package/dist/auth/store.d.ts +89 -0
  43. package/dist/auth/store.d.ts.map +1 -0
  44. package/dist/auth/store.js +214 -0
  45. package/dist/auth/store.js.map +1 -0
  46. package/dist/bin.js +0 -2
  47. package/dist/bin.js.map +1 -1
  48. package/dist/cli.d.ts +1 -2
  49. package/dist/cli.d.ts.map +1 -1
  50. package/dist/cli.js +13 -17
  51. package/dist/cli.js.map +1 -1
  52. package/dist/client.d.ts +31 -0
  53. package/dist/client.d.ts.map +1 -0
  54. package/dist/client.js +67 -0
  55. package/dist/client.js.map +1 -0
  56. package/dist/commands/accounts.command.d.ts +4 -0
  57. package/dist/commands/accounts.command.d.ts.map +1 -0
  58. package/dist/commands/accounts.command.js +116 -0
  59. package/dist/commands/accounts.command.js.map +1 -0
  60. package/dist/commands/app.command.d.ts +3 -2
  61. package/dist/commands/app.command.d.ts.map +1 -1
  62. package/dist/commands/app.command.js +132 -233
  63. package/dist/commands/app.command.js.map +1 -1
  64. package/dist/commands/auth.command.d.ts +2 -12
  65. package/dist/commands/auth.command.d.ts.map +1 -1
  66. package/dist/commands/auth.command.js +147 -20
  67. package/dist/commands/auth.command.js.map +1 -1
  68. package/dist/commands/billing.command.d.ts +4 -0
  69. package/dist/commands/billing.command.d.ts.map +1 -0
  70. package/dist/commands/billing.command.js +185 -0
  71. package/dist/commands/billing.command.js.map +1 -0
  72. package/dist/commands/check.command.d.ts +2 -1
  73. package/dist/commands/check.command.d.ts.map +1 -1
  74. package/dist/commands/check.command.js +11 -14
  75. package/dist/commands/check.command.js.map +1 -1
  76. package/dist/commands/context.d.ts +58 -0
  77. package/dist/commands/context.d.ts.map +1 -0
  78. package/dist/commands/context.js +78 -0
  79. package/dist/commands/context.js.map +1 -0
  80. package/dist/commands/flag.command.d.ts +3 -62
  81. package/dist/commands/flag.command.d.ts.map +1 -1
  82. package/dist/commands/flag.command.js +181 -864
  83. package/dist/commands/flag.command.js.map +1 -1
  84. package/dist/commands/key.command.d.ts +4 -0
  85. package/dist/commands/key.command.d.ts.map +1 -0
  86. package/dist/commands/key.command.js +44 -0
  87. package/dist/commands/key.command.js.map +1 -0
  88. package/dist/commands/plan.command.d.ts +3 -203
  89. package/dist/commands/plan.command.d.ts.map +1 -1
  90. package/dist/commands/plan.command.js +200 -759
  91. package/dist/commands/plan.command.js.map +1 -1
  92. package/dist/config.d.ts +19 -101
  93. package/dist/config.d.ts.map +1 -1
  94. package/dist/config.js +51 -364
  95. package/dist/config.js.map +1 -1
  96. package/dist/core/discovery.d.ts +27 -0
  97. package/dist/core/discovery.d.ts.map +1 -0
  98. package/dist/core/discovery.js +97 -0
  99. package/dist/core/discovery.js.map +1 -0
  100. package/dist/core/errors.d.ts +26 -0
  101. package/dist/core/errors.d.ts.map +1 -0
  102. package/dist/core/errors.js +33 -0
  103. package/dist/core/errors.js.map +1 -0
  104. package/dist/core/http.d.ts +23 -0
  105. package/dist/core/http.d.ts.map +1 -0
  106. package/dist/core/http.js +153 -0
  107. package/dist/core/http.js.map +1 -0
  108. package/dist/core/private-files.d.ts +6 -0
  109. package/dist/core/private-files.d.ts.map +1 -0
  110. package/dist/core/private-files.js +96 -0
  111. package/dist/core/private-files.js.map +1 -0
  112. package/dist/core/routes.d.ts +94 -0
  113. package/dist/core/routes.d.ts.map +1 -0
  114. package/dist/core/routes.js +91 -0
  115. package/dist/core/routes.js.map +1 -0
  116. package/dist/core/urls.d.ts +15 -0
  117. package/dist/core/urls.d.ts.map +1 -0
  118. package/dist/core/urls.js +55 -0
  119. package/dist/core/urls.js.map +1 -0
  120. package/dist/gate-rules.d.ts +1 -2
  121. package/dist/gate-rules.d.ts.map +1 -1
  122. package/dist/gate-rules.js +4 -5
  123. package/dist/gate-rules.js.map +1 -1
  124. package/dist/index.d.ts +23 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +18 -3
  127. package/dist/index.js.map +1 -1
  128. package/dist/io.d.ts +21 -0
  129. package/dist/io.d.ts.map +1 -0
  130. package/dist/io.js +59 -0
  131. package/dist/io.js.map +1 -0
  132. package/dist/money.d.ts +42 -0
  133. package/dist/money.d.ts.map +1 -0
  134. package/dist/money.js +112 -0
  135. package/dist/money.js.map +1 -0
  136. package/dist/program.d.ts +5 -24
  137. package/dist/program.d.ts.map +1 -1
  138. package/dist/program.js +25 -113
  139. package/dist/program.js.map +1 -1
  140. package/package.json +7 -8
  141. package/dist/auth/api-client.d.ts +0 -50
  142. package/dist/auth/api-client.d.ts.map +0 -1
  143. package/dist/auth/api-client.js +0 -109
  144. package/dist/auth/api-client.js.map +0 -1
  145. package/dist/auth/relative-time.d.ts +0 -2
  146. package/dist/auth/relative-time.d.ts.map +0 -1
  147. package/dist/auth/relative-time.js +0 -27
  148. package/dist/auth/relative-time.js.map +0 -1
  149. package/dist/auth/scopes.d.ts +0 -30
  150. package/dist/auth/scopes.d.ts.map +0 -1
  151. package/dist/auth/scopes.js +0 -39
  152. package/dist/auth/scopes.js.map +0 -1
  153. package/dist/auth/workspace-client.d.ts +0 -70
  154. package/dist/auth/workspace-client.d.ts.map +0 -1
  155. package/dist/auth/workspace-client.js +0 -105
  156. package/dist/auth/workspace-client.js.map +0 -1
  157. package/dist/commands/auth/config.command.d.ts +0 -8
  158. package/dist/commands/auth/config.command.d.ts.map +0 -1
  159. package/dist/commands/auth/config.command.js +0 -60
  160. package/dist/commands/auth/config.command.js.map +0 -1
  161. package/dist/commands/auth/login.command.d.ts +0 -46
  162. package/dist/commands/auth/login.command.d.ts.map +0 -1
  163. package/dist/commands/auth/login.command.js +0 -166
  164. package/dist/commands/auth/login.command.js.map +0 -1
  165. package/dist/commands/auth/logout.command.d.ts +0 -21
  166. package/dist/commands/auth/logout.command.d.ts.map +0 -1
  167. package/dist/commands/auth/logout.command.js +0 -97
  168. package/dist/commands/auth/logout.command.js.map +0 -1
  169. package/dist/commands/auth/methods.command.d.ts +0 -68
  170. package/dist/commands/auth/methods.command.d.ts.map +0 -1
  171. package/dist/commands/auth/methods.command.js +0 -91
  172. package/dist/commands/auth/methods.command.js.map +0 -1
  173. package/dist/commands/auth/status.command.d.ts +0 -18
  174. package/dist/commands/auth/status.command.d.ts.map +0 -1
  175. package/dist/commands/auth/status.command.js +0 -104
  176. package/dist/commands/auth/status.command.js.map +0 -1
  177. package/dist/commands/auth/use.command.d.ts +0 -17
  178. package/dist/commands/auth/use.command.d.ts.map +0 -1
  179. package/dist/commands/auth/use.command.js +0 -47
  180. package/dist/commands/auth/use.command.js.map +0 -1
  181. package/dist/commands/branding.command.d.ts +0 -3
  182. package/dist/commands/branding.command.d.ts.map +0 -1
  183. package/dist/commands/branding.command.js +0 -42
  184. package/dist/commands/branding.command.js.map +0 -1
  185. package/dist/commands/event.command.d.ts +0 -3
  186. package/dist/commands/event.command.d.ts.map +0 -1
  187. package/dist/commands/event.command.js +0 -27
  188. package/dist/commands/event.command.js.map +0 -1
  189. package/dist/commands/flag-init.command.d.ts +0 -13
  190. package/dist/commands/flag-init.command.d.ts.map +0 -1
  191. package/dist/commands/flag-init.command.js +0 -463
  192. package/dist/commands/flag-init.command.js.map +0 -1
  193. package/dist/commands/guide.command.d.ts +0 -61
  194. package/dist/commands/guide.command.d.ts.map +0 -1
  195. package/dist/commands/guide.command.js +0 -655
  196. package/dist/commands/guide.command.js.map +0 -1
  197. package/dist/commands/info.command.d.ts +0 -3
  198. package/dist/commands/info.command.d.ts.map +0 -1
  199. package/dist/commands/info.command.js +0 -70
  200. package/dist/commands/info.command.js.map +0 -1
  201. package/dist/commands/integration.command.d.ts +0 -35
  202. package/dist/commands/integration.command.d.ts.map +0 -1
  203. package/dist/commands/integration.command.js +0 -290
  204. package/dist/commands/integration.command.js.map +0 -1
  205. package/dist/commands/journeys.d.ts +0 -43
  206. package/dist/commands/journeys.d.ts.map +0 -1
  207. package/dist/commands/journeys.js +0 -221
  208. package/dist/commands/journeys.js.map +0 -1
  209. package/dist/commands/ops.command.d.ts +0 -17
  210. package/dist/commands/ops.command.d.ts.map +0 -1
  211. package/dist/commands/ops.command.js +0 -129
  212. package/dist/commands/ops.command.js.map +0 -1
  213. package/dist/commands/privilege.command.d.ts +0 -16
  214. package/dist/commands/privilege.command.d.ts.map +0 -1
  215. package/dist/commands/privilege.command.js +0 -86
  216. package/dist/commands/privilege.command.js.map +0 -1
  217. package/dist/commands/role.command.d.ts +0 -3
  218. package/dist/commands/role.command.d.ts.map +0 -1
  219. package/dist/commands/role.command.js +0 -101
  220. package/dist/commands/role.command.js.map +0 -1
  221. package/dist/commands/runtime-dir.d.ts +0 -16
  222. package/dist/commands/runtime-dir.d.ts.map +0 -1
  223. package/dist/commands/runtime-dir.js +0 -18
  224. package/dist/commands/runtime-dir.js.map +0 -1
  225. package/dist/commands/setup.command.d.ts +0 -63
  226. package/dist/commands/setup.command.d.ts.map +0 -1
  227. package/dist/commands/setup.command.js +0 -148
  228. package/dist/commands/setup.command.js.map +0 -1
  229. package/dist/commands/stripe.command.d.ts +0 -29
  230. package/dist/commands/stripe.command.d.ts.map +0 -1
  231. package/dist/commands/stripe.command.js +0 -99
  232. package/dist/commands/stripe.command.js.map +0 -1
  233. package/dist/commands/tenant.command.d.ts +0 -3
  234. package/dist/commands/tenant.command.d.ts.map +0 -1
  235. package/dist/commands/tenant.command.js +0 -135
  236. package/dist/commands/tenant.command.js.map +0 -1
  237. package/dist/commands/token.command.d.ts +0 -3
  238. package/dist/commands/token.command.d.ts.map +0 -1
  239. package/dist/commands/token.command.js +0 -54
  240. package/dist/commands/token.command.js.map +0 -1
  241. package/dist/commands/user.command.d.ts +0 -3
  242. package/dist/commands/user.command.d.ts.map +0 -1
  243. package/dist/commands/user.command.js +0 -117
  244. package/dist/commands/user.command.js.map +0 -1
  245. package/dist/credentials.d.ts +0 -141
  246. package/dist/credentials.d.ts.map +0 -1
  247. package/dist/credentials.js +0 -348
  248. package/dist/credentials.js.map +0 -1
  249. package/dist/flag-coherence.d.ts +0 -42
  250. package/dist/flag-coherence.d.ts.map +0 -1
  251. package/dist/flag-coherence.js +0 -103
  252. package/dist/flag-coherence.js.map +0 -1
  253. package/dist/output.d.ts +0 -17
  254. package/dist/output.d.ts.map +0 -1
  255. package/dist/output.js +0 -107
  256. package/dist/output.js.map +0 -1
  257. package/dist/preview.d.ts +0 -9
  258. package/dist/preview.d.ts.map +0 -1
  259. package/dist/preview.js +0 -63
  260. package/dist/preview.js.map +0 -1
  261. package/dist/product-decisions.d.ts +0 -40
  262. package/dist/product-decisions.d.ts.map +0 -1
  263. package/dist/product-decisions.js +0 -52
  264. package/dist/product-decisions.js.map +0 -1
  265. package/dist/prompt-urls.d.ts +0 -31
  266. package/dist/prompt-urls.d.ts.map +0 -1
  267. package/dist/prompt-urls.js +0 -35
  268. package/dist/prompt-urls.js.map +0 -1
  269. package/dist/prompts/auth-master-integration-prompt.md +0 -255
  270. package/dist/prompts/billing/master.md +0 -332
  271. package/dist/prompts/decisions/feature-control.md +0 -131
  272. package/dist/prompts/decisions/going-live.md +0 -60
  273. package/dist/prompts/decisions/login.md +0 -71
  274. package/dist/prompts/decisions/look-and-feel.md +0 -68
  275. package/dist/prompts/decisions/payments.md +0 -97
  276. package/dist/prompts/decisions/roles.md +0 -78
  277. package/dist/prompts/decisions/teams.md +0 -61
  278. package/dist/prompts/fit-together.md +0 -97
  279. package/dist/prompts/flags/master.md +0 -220
  280. package/dist/prompts/guide-coverage.json +0 -17
  281. package/dist/prompts/integration-success.md +0 -106
  282. package/dist/prompts/mcp-tools.json +0 -90
  283. package/dist/prompts/mechanisms.md +0 -304
  284. package/dist/prompts/orientation.md +0 -42
  285. package/dist/prompts/product-decisions.json +0 -338
  286. package/dist/resolve.d.ts +0 -110
  287. package/dist/resolve.d.ts.map +0 -1
  288. package/dist/resolve.js +0 -260
  289. package/dist/resolve.js.map +0 -1
  290. package/dist/shape.d.ts +0 -25
  291. package/dist/shape.d.ts.map +0 -1
  292. package/dist/shape.js +0 -74
  293. package/dist/shape.js.map +0 -1
@@ -6,24 +6,23 @@
6
6
  * - Use an OS-assigned ephemeral port (`port: 0`).
7
7
  * - Handle exactly one request, then close the listener.
8
8
  *
9
- * The server understands two response shapes from the IdP:
10
- * `?code=<code>&state=<state>` → success
11
- * `?error=access_denied&state=<state>` → user cancelled (or other error)
9
+ * The server understands two response shapes from the sign-in page:
10
+ * `?code=<code>&state=<state>&iss=<issuer>` → success
11
+ * `?error=access_denied&state=<state>` → the person cancelled (or another error)
12
12
  *
13
- * It also CSRF-checks the returned `state` against the value the caller
14
- * generated.
13
+ * It CSRF-checks the returned `state` against the value the caller
14
+ * generated, and requires the issuer the page names (`iss`, RFC 9207) to be
15
+ * the one the CLI signs in to, so a code from another issuer is never traded.
15
16
  */
16
17
  import * as http from 'node:http';
17
18
  /**
18
19
  * Bridge wordmark — inlined SVG so the page renders without any network fetch.
19
20
  * `fill="currentColor"` makes it themable via CSS `color`.
20
- * Source: bridge-cloud-views/static/assets/logos/bridge_logo.svg (color stripped).
21
21
  */
22
22
  const BRIDGE_LOGO = `<svg viewBox="0 0 818 148" xmlns="http://www.w3.org/2000/svg" aria-label="The Bridge" role="img"><path d="M668.241 0.0799866C684.368 0.0801786 696.657 3.98507 705.105 11.7929C713.026 19.1143 717.515 29.8687 718.572 44.0547C718.691 45.659 717.471 47.0329 715.868 47.1651L686.915 49.5511C685.213 49.6914 683.752 48.3801 683.562 46.6823C682.997 41.6277 681.537 37.8701 679.183 35.4091C676.367 32.3372 672.272 30.8008 666.896 30.8007C661.392 30.8007 656.783 32.5927 653.071 36.1767C649.487 39.6326 646.8 44.5604 645.008 50.9599C643.216 57.2319 642.319 64.5929 642.319 73.0409C642.319 81.4885 643.279 88.657 645.199 94.5448C647.119 100.433 649.936 104.977 653.648 108.177C657.36 111.248 661.776 112.785 666.896 112.785C672.656 112.785 677.135 110.734 680.334 106.639C683.534 102.543 685.136 96.5923 685.136 88.7851C685.136 87.8304 684.362 87.0565 683.408 87.0565H661.447C659.79 87.0565 658.447 85.7134 658.447 84.0565V62.9833C658.447 61.3265 659.79 59.9833 661.447 59.9833H716.695C718.352 59.9833 719.695 61.3265 719.695 62.9833V143.96C719.695 145.617 718.352 146.96 716.695 146.96H692.4C690.803 146.96 689.487 145.709 689.404 144.115L687.838 113.854C687.83 113.685 687.69 113.553 687.521 113.553C687.364 113.553 687.231 113.668 687.207 113.823C685.525 124.573 681.955 132.931 676.497 138.897C671.121 144.913 663.566 147.921 653.838 147.921C644.111 147.921 635.536 144.782 628.113 138.511C620.817 132.239 615.12 123.537 611.024 112.401C607.056 101.137 605.071 88.2723 605.071 73.8085C605.071 59.0885 607.439 46.2237 612.175 35.2157C617.039 24.0798 624.145 15.4408 633.489 9.29678C642.961 3.153 654.545 0.0799866 668.241 0.0799866Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M245.083 1.04092C254.682 1.041 262.683 2.63982 269.083 5.83975C275.482 9.03973 280.154 13.3281 283.098 18.704C286.17 24.0797 287.706 29.9675 287.707 36.3671C287.707 44.5587 285.21 51.855 280.218 58.2548C275.401 64.5932 268.511 68.7991 259.55 70.8689C259.398 70.9041 259.289 71.0397 259.289 71.1961C259.289 71.3696 259.423 71.5143 259.596 71.5302C269.811 72.4674 277.582 75.9152 282.905 81.871C288.409 87.759 291.161 95.9522 291.161 106.448C291.161 119.12 286.489 129.04 277.145 136.208C267.929 143.376 255.257 146.96 239.129 146.96H187.986C186.329 146.96 184.986 145.617 184.986 143.96V4.04093C184.986 2.38407 186.329 1.04092 187.986 1.04092H245.083ZM221.657 112.472C221.657 114.128 223 115.472 224.657 115.472H235.866C241.754 115.472 246.104 114.191 248.92 111.631C251.864 108.943 253.338 105.231 253.338 100.495C253.338 95.7595 251.864 92.1125 248.92 89.5526C246.104 86.8649 241.753 85.5214 235.866 85.5214H224.657C223 85.5214 221.657 86.8645 221.657 88.5214V112.472ZM221.657 60.4403C221.657 62.0972 223 63.4403 224.657 63.4403H235.098C240.729 63.4403 244.953 62.0964 247.769 59.4091C250.585 56.5931 251.994 52.7506 251.994 47.8866C251.993 42.8953 250.585 39.0549 247.769 36.3671C244.953 33.6797 240.729 32.3359 235.098 32.3358H224.657C223 32.3358 221.657 33.679 221.657 35.3358V60.4403Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M365.258 1.04092C379.849 1.04104 390.793 4.23949 398.088 10.6386C405.384 17.0386 409.034 25.5527 409.034 36.1767C409.034 45.0084 406.537 52.7533 401.545 59.4091C396.593 65.8845 388.87 70.5342 378.376 73.357C378.227 73.397 378.123 73.5319 378.123 73.6857C378.123 73.8626 378.259 74.0098 378.436 74.0249C384.683 74.5595 389.507 76.0248 392.906 78.4169C396.489 80.8489 399.176 84.4331 400.968 89.1689C402.76 93.7768 404.362 100.562 405.77 109.521C407.434 119.12 408.905 126.863 410.185 132.751C411.588 138.62 413.246 143.283 415.158 146.735C415.171 146.758 415.177 146.783 415.177 146.809C415.177 146.892 415.11 146.96 415.026 146.96H377.595C376.304 146.96 375.152 146.135 374.778 144.899C373.912 142.04 373.171 139.015 372.553 135.824C371.913 131.728 371.019 126.031 369.867 118.735C368.715 110.288 367.689 104.335 366.793 100.879C365.897 97.4234 364.361 94.8639 362.185 93.2001C360.009 91.5363 356.746 90.7041 352.394 90.704H345.026C343.369 90.704 342.026 92.0472 342.026 93.704V143.96C342.026 145.617 340.683 146.96 339.026 146.96H308.355C306.698 146.96 305.355 145.617 305.355 143.96V4.04093C305.355 2.38407 306.698 1.04092 308.355 1.04092H365.258ZM342.026 62.1689C342.026 63.8257 343.369 65.1689 345.026 65.1689H354.313C360.073 65.1689 364.554 63.7602 367.754 60.9442C370.954 58.0003 372.553 53.9674 372.553 48.8476C372.553 43.7278 370.954 39.6948 367.754 36.7509C364.554 33.8069 360.073 32.3358 354.313 32.3358H345.026C343.369 32.3358 342.026 33.679 342.026 35.3358V62.1689Z" fill="currentColor"/><path d="M463.892 143.96C463.892 145.617 462.549 146.96 460.892 146.96H430.221C428.564 146.96 427.221 145.617 427.221 143.96V4.04093C427.221 2.38407 428.564 1.04092 430.221 1.04092H460.892C462.549 1.04092 463.892 2.38407 463.892 4.04092V143.96Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M526.892 1.04092C540.716 1.04106 552.556 4.11101 562.412 10.2548C572.395 16.3987 579.947 24.9756 585.067 35.9833C590.315 46.9913 592.939 59.6005 592.939 73.8085C592.939 88.6562 590.315 101.584 585.067 112.592C579.947 123.599 572.395 132.111 562.412 138.127C552.556 144.015 540.716 146.96 526.892 146.96H484.962C483.306 146.96 481.962 145.617 481.962 143.96V4.04093C481.962 2.38407 483.306 1.04092 484.962 1.04092H526.892ZM518.443 111.127C518.443 112.784 519.786 114.127 521.443 114.127H526.315C535.531 114.127 542.699 110.929 547.819 104.529C553.067 98.1293 555.691 87.8882 555.691 73.8085C555.691 60.1128 553.067 50.065 547.819 43.6649C542.571 37.1369 535.403 33.871 526.315 33.871H521.443C519.786 33.871 518.443 35.2141 518.443 36.871V111.127Z" fill="currentColor"/><path d="M817.409 29.3358C817.409 30.9927 816.066 32.3358 814.409 32.3358H775.866C774.209 32.3358 772.866 33.679 772.866 35.3358V55.6386C772.866 57.2954 774.209 58.6386 775.866 58.6386H811.722C813.379 58.6386 814.722 59.9817 814.722 61.6386V84.247C814.722 85.9038 813.379 87.247 811.722 87.247H775.866C774.209 87.247 772.866 88.5901 772.866 90.247V112.472C772.866 114.128 774.209 115.472 775.866 115.472H814.602C816.259 115.472 817.602 116.815 817.602 118.472V143.96C817.602 145.617 816.259 146.96 814.602 146.96H739.385C737.728 146.96 736.385 145.617 736.385 143.96V4.04093C736.385 2.38407 737.728 1.04092 739.385 1.04092H814.409C816.066 1.04092 817.409 2.38407 817.409 4.04092V29.3358Z" fill="currentColor"/><path d="M24.978 46.28C6.738 46.28 0.21 38.024 0.402 30.536C0.402 22.856 7.122 14.792 17.682 14.792C23.826 14.792 28.434 17.48 29.586 21.512C29.586 21.896 29.778 22.28 29.778 22.472C29.778 23.432 29.01 23.816 27.282 23.432C25.938 23.048 25.362 22.664 23.634 22.664C18.834 22.664 16.338 25.736 16.338 28.616C16.146 33.416 22.098 34.568 27.858 34.568C30.162 34.568 32.466 34.376 34.194 34.184C51.858 32.264 106.194 19.4 135.378 19.4C142.482 19.4 147.666 20.168 150.546 21.704C151.314 22.088 151.698 22.472 151.698 22.856C151.698 23.24 150.93 23.624 149.394 23.816C96.786 29.96 58.194 43.016 33.042 45.704C30.354 46.088 27.666 46.28 24.978 46.28ZM134.226 141.896C128.274 141.896 123.666 139.208 122.514 135.176C122.514 134.792 122.322 134.408 122.322 134.024C122.322 133.064 123.09 132.872 124.818 133.256C126.162 133.448 126.738 134.024 128.466 134.024C133.266 134.024 135.762 130.952 135.762 127.88C135.762 123.272 130.002 122.12 124.242 122.12C121.938 122.12 119.634 122.312 117.906 122.504C100.242 124.424 45.906 137.096 16.722 137.096C9.618 137.096 4.434 136.52 1.362 134.792C0.786 134.408 0.402 134.024 0.402 133.832C0.402 133.448 1.17 133.064 2.706 132.872C55.314 126.728 93.906 113.672 119.058 110.792C121.746 110.6 124.434 110.408 127.122 110.408C145.362 110.408 151.89 118.472 151.698 126.152C151.698 133.64 144.978 141.896 134.226 141.896Z" fill="currentColor"/></svg>`;
23
23
  /**
24
- * Shared <style> block — matches bridge-cloud-views / bridge-admin-ui brand tokens
25
- * (see bridge-cloud-views/src/global.css). Light mode, Inter, Bridge purple accent,
26
- * slate text scale, white card with subtle border.
24
+ * Shared <style> block: Bridge's brand tokens. Light mode, the system font,
25
+ * Bridge purple accent, slate text scale, white card with subtle border.
27
26
  */
28
27
  const SHARED_STYLES = `
29
28
  :root {
@@ -40,7 +39,7 @@ const SHARED_STYLES = `
40
39
  }
41
40
  * { box-sizing: border-box; }
42
41
  body {
43
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
42
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
44
43
  background: var(--color-background-muted);
45
44
  color: var(--color-foreground);
46
45
  display: flex;
@@ -87,7 +86,6 @@ const SHARED_STYLES = `
87
86
  line-height: 1.55;
88
87
  }
89
88
  `;
90
- const FONT_LINK = `<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">`;
91
89
  const SUCCESS_ICON = `<svg class="icon" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="24" cy="24" r="22" fill="#c4ffb8" stroke="#1fb800" stroke-width="2"/><path d="M14 24l7 7 13-14" fill="none" stroke="#1fb800" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
92
90
  const FAILURE_ICON = `<svg class="icon" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="24" cy="24" r="22" fill="#fee2e2" stroke="#d6210b" stroke-width="2"/><path d="M16 16l16 16M32 16L16 32" fill="none" stroke="#d6210b" stroke-width="3" stroke-linecap="round"/></svg>`;
93
91
  const DEFAULT_SUCCESS_HTML = `<!doctype html>
@@ -95,15 +93,14 @@ const DEFAULT_SUCCESS_HTML = `<!doctype html>
95
93
  <head>
96
94
  <meta charset="utf-8">
97
95
  <meta name="viewport" content="width=device-width, initial-scale=1">
98
- <title>Bridge CLI — Login successful</title>
99
- ${FONT_LINK}
96
+ <title>Bridge CLI — Signed in</title>
100
97
  <style>${SHARED_STYLES}</style>
101
98
  </head>
102
99
  <body>
103
100
  <main class="card">
104
101
  <span class="logo">${BRIDGE_LOGO}</span>
105
102
  ${SUCCESS_ICON}
106
- <h1>Login successful</h1>
103
+ <h1>Signed in</h1>
107
104
  <p>You can close this tab and return to your terminal.</p>
108
105
  </main>
109
106
  </body>
@@ -113,15 +110,14 @@ const FAILURE_HTML = (msg) => `<!doctype html>
113
110
  <head>
114
111
  <meta charset="utf-8">
115
112
  <meta name="viewport" content="width=device-width, initial-scale=1">
116
- <title>Bridge CLI — Login failed</title>
117
- ${FONT_LINK}
113
+ <title>Bridge CLI — Sign-in failed</title>
118
114
  <style>${SHARED_STYLES} h1 { color: var(--color-bridge-red); }</style>
119
115
  </head>
120
116
  <body>
121
117
  <main class="card">
122
118
  <span class="logo">${BRIDGE_LOGO}</span>
123
119
  ${FAILURE_ICON}
124
- <h1>Login failed</h1>
120
+ <h1>Sign-in failed</h1>
125
121
  <p>${escapeHtml(msg)}</p>
126
122
  </main>
127
123
  </body>
@@ -140,6 +136,7 @@ export function parseCallbackUrl(rawUrl) {
140
136
  state: url.searchParams.get('state') ?? undefined,
141
137
  error: url.searchParams.get('error') ?? undefined,
142
138
  errorDescription: url.searchParams.get('error_description') ?? undefined,
139
+ iss: url.searchParams.get('iss') ?? undefined,
143
140
  pathname: url.pathname,
144
141
  };
145
142
  }
@@ -147,10 +144,14 @@ export function parseCallbackUrl(rawUrl) {
147
144
  * Validates a parsed callback against the expected CSRF state. Returns the
148
145
  * verified `{code, state}` or throws a friendly error.
149
146
  */
150
- export function verifyCallback(parsed, expectedState) {
147
+ export function verifyCallback(parsed, expectedState, expectedIssuer) {
148
+ // The state first: an error from a request this process never made is not reported as its own.
149
+ if (parsed.state && !constantTimeEqual(parsed.state, expectedState)) {
150
+ throw new LoopbackError('State mismatch on the sign-in callback (possible CSRF).', 'state_mismatch');
151
+ }
151
152
  if (parsed.error) {
152
153
  if (parsed.error === 'access_denied') {
153
- throw new LoopbackError('Login cancelled.', 'access_denied');
154
+ throw new LoopbackError('Sign-in cancelled.', 'access_denied');
154
155
  }
155
156
  const desc = parsed.errorDescription ? `: ${parsed.errorDescription}` : '';
156
157
  throw new LoopbackError(`Authorization failed (${parsed.error})${desc}`, parsed.error);
@@ -162,7 +163,14 @@ export function verifyCallback(parsed, expectedState) {
162
163
  throw new LoopbackError('Authorization callback was missing the `state` parameter.', 'invalid_callback');
163
164
  }
164
165
  if (!constantTimeEqual(parsed.state, expectedState)) {
165
- throw new LoopbackError('State mismatch on authorization callback (possible CSRF).', 'state_mismatch');
166
+ throw new LoopbackError('State mismatch on the sign-in callback (possible CSRF).', 'state_mismatch');
167
+ }
168
+ // RFC 9207: the callback must name its issuer, and it must be the one this sign-in is for.
169
+ if (expectedIssuer !== undefined) {
170
+ if (parsed.iss === undefined)
171
+ throw new LoopbackError('The sign-in callback does not name its issuer (iss).', 'issuer_missing');
172
+ if (parsed.iss !== expectedIssuer)
173
+ throw new LoopbackError('The sign-in callback names another issuer.', 'issuer_mismatch');
166
174
  }
167
175
  return { code: parsed.code, state: parsed.state };
168
176
  }
@@ -170,7 +178,7 @@ export function verifyCallback(parsed, expectedState) {
170
178
  * Boots the single-shot loopback listener on a random ephemeral port.
171
179
  *
172
180
  * Returns immediately with `{ redirectUri, result }`. The caller passes
173
- * `redirectUri` to the IdP, then awaits `result` for the verified `{code, state}`.
181
+ * `redirectUri` to the sign-in page, then awaits `result` for the verified `{code, state}`.
174
182
  *
175
183
  * The server:
176
184
  * - Closes itself after the first GET to `/callback` completes its response.
@@ -179,7 +187,7 @@ export function verifyCallback(parsed, expectedState) {
179
187
  * - Times out after `timeoutMs` (default 5 minutes).
180
188
  */
181
189
  export async function startLoopback(opts) {
182
- const { expectedState, successRedirectUrl, successHtml = DEFAULT_SUCCESS_HTML, timeoutMs = 5 * 60_000, } = opts;
190
+ const { expectedState, expectedIssuer, successHtml = DEFAULT_SUCCESS_HTML, timeoutMs = 5 * 60_000, } = opts;
183
191
  let resolveResult;
184
192
  let rejectResult;
185
193
  const result = new Promise((res, rej) => {
@@ -196,29 +204,16 @@ export async function startLoopback(opts) {
196
204
  return;
197
205
  }
198
206
  try {
199
- const verified = verifyCallback(parsed, expectedState);
200
- if (successRedirectUrl) {
201
- // Bounce the browser to the hosted success page on cloud-views.
202
- // The user lands on auth.thebridge.dev/cli/login-complete instead of
203
- // staying on 127.0.0.1 — branded UX.
204
- res.statusCode = 302;
205
- res.setHeader('Location', successRedirectUrl);
206
- res.setHeader('Connection', 'close');
207
- res.end(() => {
208
- server.close();
209
- server.closeAllConnections();
210
- });
211
- }
212
- else {
213
- res.statusCode = 200;
214
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
215
- res.setHeader('Connection', 'close');
216
- res.end(successHtml, () => {
217
- // Close listener AFTER the response flushes so the user actually sees the page.
218
- server.close();
219
- server.closeAllConnections();
220
- });
221
- }
207
+ const verified = verifyCallback(parsed, expectedState, expectedIssuer);
208
+ res.statusCode = 200;
209
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
210
+ res.setHeader('Cache-Control', 'no-store');
211
+ res.setHeader('Connection', 'close');
212
+ res.end(successHtml, () => {
213
+ // Close listener AFTER the response flushes so the person actually sees the page.
214
+ server.close();
215
+ server.closeAllConnections();
216
+ });
222
217
  resolveResult(verified);
223
218
  }
224
219
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../src/auth/loopback.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../src/auth/loopback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { Discovery } from '../core/discovery.js';
2
+ import { type Fetch } from '../core/http.js';
3
+ import type { SessionStore, StoredSession } from './store.js';
4
+ export interface TokenAnswer {
5
+ readonly access_token: string;
6
+ readonly token_type?: string;
7
+ readonly expires_in: number;
8
+ readonly refresh_token?: string;
9
+ readonly workspace?: string;
10
+ }
11
+ export interface SessionDeps {
12
+ readonly fetch: Fetch;
13
+ readonly store: SessionStore;
14
+ /** Where the renewal lock lives. */
15
+ readonly lockDir: string;
16
+ readonly now?: () => number;
17
+ }
18
+ /**
19
+ * The developer's session with the Bridge App: kept in the store, renewed
20
+ * with rotation (each renewal gives a new refresh token, saved before it is
21
+ * used), ended by revoking the refresh token.
22
+ */
23
+ export declare class Sessions {
24
+ private readonly deps;
25
+ private readonly now;
26
+ constructor(deps: SessionDeps);
27
+ describeStore(): string;
28
+ /** Trades a sign-in code for tokens (PKCE: only the holder of the verifier can) and keeps them. */
29
+ exchangeCode(d: Discovery, code: string, verifier: string): Promise<StoredSession>;
30
+ read(issuer: string): Promise<StoredSession | null>;
31
+ /** Removes the session from this machine only. */
32
+ forget(issuer: string): Promise<void>;
33
+ /** The signed-in session for the issuer, or `not-signed-in`. */
34
+ current(d: Discovery): Promise<StoredSession>;
35
+ /** An access token that is good for at least a minute, renewed first if needed. */
36
+ accessToken(d: Discovery, options?: {
37
+ force?: boolean;
38
+ }): Promise<string>;
39
+ /**
40
+ * Renews the tokens, optionally for another workspace. Under a lock, and
41
+ * with the store read again inside it: a command that ran at the same
42
+ * time may have renewed already, and its refresh token is the only one
43
+ * still good.
44
+ */
45
+ renew(d: Discovery, options?: {
46
+ workspace?: string;
47
+ force?: boolean;
48
+ stale?: string;
49
+ }): Promise<StoredSession>;
50
+ /**
51
+ * Signs out: revokes the refresh token, which ends the session at Bridge,
52
+ * then forgets it here. Forgotten here even when Bridge cannot be
53
+ * reached, and then says so.
54
+ */
55
+ signOut(d: Discovery): Promise<{
56
+ revoked: boolean;
57
+ reason?: string;
58
+ }>;
59
+ private fromAnswer;
60
+ }
61
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAQ,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK9D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAUD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,QAAQ;IAGP,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEN,IAAI,EAAE,WAAW;IAI9C,aAAa,IAAI,MAAM;IAIvB,mGAAmG;IAC7F,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAWlF,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAIzD,kDAAkD;IAC5C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,gEAAgE;IAC1D,OAAO,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;IAMnD,mFAAmF;IAC7E,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAMnF;;;;;OAKG;IACG,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAmCxH;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAoB3E,OAAO,CAAC,UAAU;CAYnB"}
@@ -0,0 +1,142 @@
1
+ import { CliError, SIGN_IN_AGAIN } from '../core/errors.js';
2
+ import { call } from '../core/http.js';
3
+ import { withLock } from './lock.js';
4
+ /** Renew this long before the access token expires, so a call never carries one that runs out on the way. */
5
+ const RENEW_BEFORE_MS = 60_000;
6
+ function tokenAnswer(body) {
7
+ const answer = (body ?? {});
8
+ if (typeof answer['access_token'] !== 'string' || typeof answer['expires_in'] !== 'number') {
9
+ throw new CliError('misconfigured', 'The token endpoint answered without an access token.');
10
+ }
11
+ return answer;
12
+ }
13
+ /**
14
+ * The developer's session with the Bridge App: kept in the store, renewed
15
+ * with rotation (each renewal gives a new refresh token, saved before it is
16
+ * used), ended by revoking the refresh token.
17
+ */
18
+ export class Sessions {
19
+ deps;
20
+ now;
21
+ constructor(deps) {
22
+ this.deps = deps;
23
+ this.now = deps.now ?? Date.now;
24
+ }
25
+ describeStore() {
26
+ return this.deps.store.describe();
27
+ }
28
+ /** Trades a sign-in code for tokens (PKCE: only the holder of the verifier can) and keeps them. */
29
+ async exchangeCode(d, code, verifier) {
30
+ const { body } = await call(this.deps.fetch, {
31
+ method: 'POST',
32
+ url: d.token,
33
+ body: { grant_type: 'authorization_code', code, code_verifier: verifier, client_id: d.appId },
34
+ });
35
+ const session = this.fromAnswer(d, tokenAnswer(body), null, this.now());
36
+ await this.deps.store.write(session);
37
+ return session;
38
+ }
39
+ async read(issuer) {
40
+ return this.deps.store.read(issuer);
41
+ }
42
+ /** Removes the session from this machine only. */
43
+ async forget(issuer) {
44
+ await this.deps.store.remove(issuer);
45
+ }
46
+ /** The signed-in session for the issuer, or `not-signed-in`. */
47
+ async current(d) {
48
+ const session = await this.deps.store.read(d.issuer);
49
+ if (session === null)
50
+ throw new CliError('not-signed-in', `Not signed in to ${d.issuer}.`, { hint: SIGN_IN_AGAIN });
51
+ return session;
52
+ }
53
+ /** An access token that is good for at least a minute, renewed first if needed. */
54
+ async accessToken(d, options = {}) {
55
+ const session = await this.current(d);
56
+ if (!options.force && session.accessTokenExpiresAt - this.now() > RENEW_BEFORE_MS)
57
+ return session.accessToken;
58
+ return (await this.renew(d, { force: options.force, stale: session.accessToken })).accessToken;
59
+ }
60
+ /**
61
+ * Renews the tokens, optionally for another workspace. Under a lock, and
62
+ * with the store read again inside it: a command that ran at the same
63
+ * time may have renewed already, and its refresh token is the only one
64
+ * still good.
65
+ */
66
+ async renew(d, options = {}) {
67
+ return withLock(this.deps.lockDir, 'renew', async () => {
68
+ const session = await this.current(d);
69
+ const renewedMeanwhile = options.stale !== undefined && session.accessToken !== options.stale;
70
+ if (options.workspace === undefined && renewedMeanwhile && session.accessTokenExpiresAt - this.now() > RENEW_BEFORE_MS)
71
+ return session;
72
+ if (session.refreshToken === null) {
73
+ await this.deps.store.remove(d.issuer);
74
+ throw new CliError('session-ended', 'The session cannot be renewed.', { hint: SIGN_IN_AGAIN });
75
+ }
76
+ let body;
77
+ try {
78
+ ({ body } = await call(this.deps.fetch, {
79
+ method: 'POST',
80
+ url: d.token,
81
+ body: {
82
+ grant_type: 'refresh_token',
83
+ refresh_token: session.refreshToken,
84
+ client_id: d.appId,
85
+ ...(options.workspace === undefined ? {} : { workspace: options.workspace }),
86
+ },
87
+ }));
88
+ }
89
+ catch (error) {
90
+ // Refused for good: the session ended (signed out elsewhere, removed from the workspace, the token reused).
91
+ if (error instanceof CliError && error.kind === 'session-ended') {
92
+ await this.deps.store.remove(d.issuer);
93
+ throw new CliError('session-ended', `The session ended: ${error.message}`, { status: error.status, hint: SIGN_IN_AGAIN });
94
+ }
95
+ throw error;
96
+ }
97
+ const renewed = this.fromAnswer(d, tokenAnswer(body), session, session.signedInAt);
98
+ await this.deps.store.write(renewed);
99
+ return renewed;
100
+ });
101
+ }
102
+ /**
103
+ * Signs out: revokes the refresh token, which ends the session at Bridge,
104
+ * then forgets it here. Forgotten here even when Bridge cannot be
105
+ * reached, and then says so.
106
+ */
107
+ async signOut(d) {
108
+ const session = await this.deps.store.read(d.issuer);
109
+ if (session === null)
110
+ return { revoked: false, reason: 'not signed in' };
111
+ let revoked = false;
112
+ let reason;
113
+ try {
114
+ if (session.refreshToken !== null) {
115
+ await call(this.deps.fetch, { method: 'POST', url: d.revoke, body: { token: session.refreshToken, token_type_hint: 'refresh_token', client_id: d.appId } });
116
+ revoked = true;
117
+ }
118
+ else if (d.signOut !== null) {
119
+ await call(this.deps.fetch, { method: 'POST', url: d.signOut, headers: { authorization: `Bearer ${session.accessToken}` } });
120
+ revoked = true;
121
+ }
122
+ }
123
+ catch (error) {
124
+ reason = error instanceof Error ? error.message : String(error);
125
+ }
126
+ await this.deps.store.remove(d.issuer);
127
+ return reason === undefined ? { revoked } : { revoked, reason };
128
+ }
129
+ fromAnswer(d, answer, previous, signedInAt) {
130
+ return {
131
+ issuer: d.issuer,
132
+ appId: d.appId,
133
+ accessToken: answer.access_token,
134
+ accessTokenExpiresAt: this.now() + answer.expires_in * 1000,
135
+ // Rotation: the new refresh token replaces the old one, which is now spent.
136
+ refreshToken: answer.refresh_token ?? previous?.refreshToken ?? null,
137
+ workspace: answer.workspace ?? null,
138
+ signedInAt,
139
+ };
140
+ }
141
+ }
142
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import type { Discovery } from '../core/discovery.js';
2
+ import { type Fetch } from '../core/http.js';
3
+ import { type StartedLoopback } from './loopback.js';
4
+ import type { Sessions } from './session.js';
5
+ import type { StoredSession } from './store.js';
6
+ /** A workspace offered at sign-in, and what entering it would still ask for. */
7
+ export interface WorkspaceChoice {
8
+ readonly id: string;
9
+ readonly name: string;
10
+ readonly needs?: unknown;
11
+ }
12
+ export type ChooseWorkspace = (choices: readonly WorkspaceChoice[]) => Promise<string>;
13
+ export interface PasswordSignIn {
14
+ readonly email: string;
15
+ readonly password: string;
16
+ /** The workspace to enter; asked for with `chooseWorkspace` when the person has several and none is named. */
17
+ readonly workspace?: string;
18
+ readonly chooseWorkspace: ChooseWorkspace;
19
+ }
20
+ /**
21
+ * Email and password in the terminal, against the Bridge App's challenge
22
+ * endpoint. The Bridge App is a first-party client, so no page is needed.
23
+ * The code is bound with PKCE to this process and traded at once.
24
+ *
25
+ * 1. `{ email, password, code_challenge }` gives a code, or the workspace step.
26
+ * 2. The workspace step: `{ flow, workspace }` gives the code.
27
+ * 3. The token endpoint trades the code and the verifier for tokens.
28
+ */
29
+ export declare function signInWithPassword(fetchFn: Fetch, sessions: Sessions, d: Discovery, input: PasswordSignIn): Promise<StoredSession>;
30
+ export interface BrowserSignIn {
31
+ /** Bridge's sign-in page in hand-off mode. */
32
+ readonly signInPage: string;
33
+ readonly openBrowser: (url: string) => Promise<{
34
+ ok: boolean;
35
+ reason?: string;
36
+ }>;
37
+ /** Told the address when the browser cannot be opened, or always, so it can be opened by hand. */
38
+ readonly show: (message: string) => void;
39
+ readonly timeoutMs?: number;
40
+ /** For tests: called once the loopback listens. */
41
+ readonly onListening?: (loopback: StartedLoopback, url: string) => void;
42
+ }
43
+ /**
44
+ * The address of the sign-in page for this sign-in: the page signs in with
45
+ * the PKCE challenge and sends the browser back to the loopback address
46
+ * with the code and the state (hand-off mode). Only this process holds the
47
+ * verifier, so only it can trade the code.
48
+ */
49
+ export declare function signInPageUrl(page: string, d: Discovery, request: {
50
+ codeChallenge: string;
51
+ redirectUri: string;
52
+ state: string;
53
+ }): string;
54
+ /**
55
+ * The sign-in page to open. The page the issuer publishes always wins. An
56
+ * override (`--sign-in-page`, `BRIDGE_SIGN_IN_PAGE`) is used only when the
57
+ * issuer publishes none, and only on the issuer's own origin, or on this
58
+ * machine for development: a page anywhere else could take the password
59
+ * and relay it. An override is never saved.
60
+ */
61
+ export declare function chooseSignInPage(d: Discovery, override: string | undefined): {
62
+ page: string;
63
+ ignored?: string;
64
+ };
65
+ /** Browser sign-in through Bridge's own sign-in page, with a loopback on 127.0.0.1 and PKCE (RFC 8252, RFC 7636). */
66
+ export declare function signInWithBrowser(sessions: Sessions, d: Discovery, input: BrowserSignIn): Promise<StoredSession>;
67
+ //# sourceMappingURL=sign-in.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../src/auth/sign-in.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAQ,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAgC,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAEnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AASvF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8GAA8G;IAC9G,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA2BxI;AAED,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjF,kGAAkG;IAClG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CASxI;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAiB/G;AAED,qHAAqH;AACrH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CA0BtH"}
@@ -0,0 +1,113 @@
1
+ import { CliError } from '../core/errors.js';
2
+ import { call } from '../core/http.js';
3
+ import { isLocalHost, secureUrl } from '../core/urls.js';
4
+ import { LoopbackError, startLoopback } from './loopback.js';
5
+ import { codeChallengeFor, generateCodeVerifier, generateState } from './pkce.js';
6
+ /**
7
+ * Email and password in the terminal, against the Bridge App's challenge
8
+ * endpoint. The Bridge App is a first-party client, so no page is needed.
9
+ * The code is bound with PKCE to this process and traded at once.
10
+ *
11
+ * 1. `{ email, password, code_challenge }` gives a code, or the workspace step.
12
+ * 2. The workspace step: `{ flow, workspace }` gives the code.
13
+ * 3. The token endpoint trades the code and the verifier for tokens.
14
+ */
15
+ export async function signInWithPassword(fetchFn, sessions, d, input) {
16
+ const verifier = generateCodeVerifier();
17
+ const challenge = codeChallengeFor(verifier);
18
+ let { body } = await call(fetchFn, {
19
+ method: 'POST',
20
+ url: d.signIn,
21
+ body: {
22
+ email: input.email,
23
+ password: input.password,
24
+ code_challenge: challenge,
25
+ code_challenge_method: 'S256',
26
+ client_id: d.appId,
27
+ ...(input.workspace === undefined ? {} : { workspace: input.workspace }),
28
+ },
29
+ });
30
+ if (typeof body?.code !== 'string' && body?.next === 'workspace' && typeof body.flow === 'string') {
31
+ const choices = Array.isArray(body.workspaces) ? body.workspaces : [];
32
+ const workspace = await input.chooseWorkspace(choices);
33
+ ({ body } = await call(fetchFn, { method: 'POST', url: d.signIn, body: { flow: body.flow, workspace, client_id: d.appId } }));
34
+ }
35
+ const code = body?.code;
36
+ if (typeof code !== 'string') {
37
+ throw new CliError('unsupported-step', `Sign-in asks for a step the CLI cannot do in the terminal (${String(body?.next ?? 'none')}).`, {
38
+ hint: 'Sign in with the browser instead: `bridge auth login`.',
39
+ });
40
+ }
41
+ return sessions.exchangeCode(d, code, verifier);
42
+ }
43
+ /**
44
+ * The address of the sign-in page for this sign-in: the page signs in with
45
+ * the PKCE challenge and sends the browser back to the loopback address
46
+ * with the code and the state (hand-off mode). Only this process holds the
47
+ * verifier, so only it can trade the code.
48
+ */
49
+ export function signInPageUrl(page, d, request) {
50
+ const url = new URL(page);
51
+ url.searchParams.set('response_type', 'code');
52
+ url.searchParams.set('client_id', d.appId);
53
+ url.searchParams.set('code_challenge', request.codeChallenge);
54
+ url.searchParams.set('code_challenge_method', 'S256');
55
+ url.searchParams.set('redirect_uri', request.redirectUri);
56
+ url.searchParams.set('state', request.state);
57
+ return url.toString();
58
+ }
59
+ /**
60
+ * The sign-in page to open. The page the issuer publishes always wins. An
61
+ * override (`--sign-in-page`, `BRIDGE_SIGN_IN_PAGE`) is used only when the
62
+ * issuer publishes none, and only on the issuer's own origin, or on this
63
+ * machine for development: a page anywhere else could take the password
64
+ * and relay it. An override is never saved.
65
+ */
66
+ export function chooseSignInPage(d, override) {
67
+ if (d.signInPage !== null) {
68
+ return override === undefined || override === d.signInPage ? { page: d.signInPage } : { page: d.signInPage, ignored: override };
69
+ }
70
+ if (override === undefined || override === '') {
71
+ throw new CliError('not-offered', 'This Bridge does not offer a sign-in page for the CLI yet.', {
72
+ hint: 'Sign in in the terminal instead: `bridge auth login --password`.',
73
+ });
74
+ }
75
+ const url = secureUrl(override);
76
+ if (url === null)
77
+ throw new CliError('misconfigured', `The sign-in page must be an https address (http only on localhost): ${override}`);
78
+ if (url.origin !== new URL(d.issuer).origin && !isLocalHost(url.hostname)) {
79
+ throw new CliError('misconfigured', `A sign-in page is taken only on the issuer's own address (${new URL(d.issuer).origin}) or on localhost, not ${url.origin}.`, {
80
+ hint: 'Sign in in the terminal instead: `bridge auth login --password`.',
81
+ });
82
+ }
83
+ return { page: url.toString() };
84
+ }
85
+ /** Browser sign-in through Bridge's own sign-in page, with a loopback on 127.0.0.1 and PKCE (RFC 8252, RFC 7636). */
86
+ export async function signInWithBrowser(sessions, d, input) {
87
+ if (secureUrl(input.signInPage) === null) {
88
+ throw new CliError('misconfigured', `The sign-in page must be an https address (http only on localhost): ${input.signInPage}`);
89
+ }
90
+ const verifier = generateCodeVerifier();
91
+ const state = generateState();
92
+ const loopback = await startLoopback({ expectedState: state, expectedIssuer: d.issuer, timeoutMs: input.timeoutMs });
93
+ const url = signInPageUrl(input.signInPage, d, { codeChallenge: codeChallengeFor(verifier), redirectUri: loopback.redirectUri, state });
94
+ input.onListening?.(loopback, url);
95
+ try {
96
+ const opened = await input.openBrowser(url);
97
+ input.show(opened.ok
98
+ ? `Opened your browser to sign in. If it did not open, go to:\n ${url}`
99
+ : `Open this address in your browser to sign in:\n ${url}`);
100
+ const { code } = await loopback.result;
101
+ return await sessions.exchangeCode(d, code, verifier);
102
+ }
103
+ catch (error) {
104
+ if (error instanceof LoopbackError) {
105
+ throw new CliError(error.kind === 'access_denied' ? 'cancelled' : 'invalid', error.message);
106
+ }
107
+ throw error;
108
+ }
109
+ finally {
110
+ loopback.close();
111
+ }
112
+ }
113
+ //# sourceMappingURL=sign-in.js.map