@toa.io/extensions.exposition 1.0.0-alpha.282 → 1.0.0-alpha.283

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 (312) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/components/exposition.octets/manifest.toa.yaml +11 -0
  3. package/components/exposition.octets/types/index.d.ts +3 -0
  4. package/components/exposition.octets/types/toa.d.ts +41 -0
  5. package/components/identity.bans/types/index.d.ts +3 -0
  6. package/components/identity.bans/types/toa.d.ts +34 -0
  7. package/components/identity.basic/manifest.toa.yaml +13 -2
  8. package/components/identity.basic/operations/add.d.ts +3 -3
  9. package/components/identity.basic/operations/authenticate.d.ts +2 -2
  10. package/components/identity.basic/operations/check.d.ts +1 -1
  11. package/components/identity.basic/operations/delete.d.ts +2 -2
  12. package/components/identity.basic/operations/incept.d.ts +2 -2
  13. package/components/identity.basic/operations/info.d.ts +1 -1
  14. package/components/identity.basic/operations/transit.d.ts +4 -3
  15. package/components/identity.basic/operations/transit.js +2 -1
  16. package/components/identity.basic/operations/transit.js.map +1 -1
  17. package/components/identity.basic/source/add.ts +3 -3
  18. package/components/identity.basic/source/authenticate.ts +2 -2
  19. package/components/identity.basic/source/check.ts +1 -1
  20. package/components/identity.basic/source/delete.ts +2 -2
  21. package/components/identity.basic/source/incept.ts +2 -2
  22. package/components/identity.basic/source/info.ts +1 -1
  23. package/components/identity.basic/source/transit.ts +6 -4
  24. package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
  25. package/components/identity.basic/types/index.d.ts +21 -0
  26. package/components/identity.basic/types/toa.d.ts +121 -0
  27. package/components/identity.clients/manifest.toa.yaml +243 -0
  28. package/components/identity.clients/operations/describe.d.ts +27 -0
  29. package/components/identity.clients/operations/describe.js +44 -0
  30. package/components/identity.clients/operations/describe.js.map +1 -0
  31. package/components/identity.clients/operations/lib/Context.d.ts +29 -0
  32. package/components/identity.clients/operations/lib/Context.js +2 -0
  33. package/components/identity.clients/operations/lib/Context.js.map +1 -0
  34. package/components/identity.clients/operations/lib/Entity.d.ts +29 -0
  35. package/components/identity.clients/operations/lib/Entity.js +2 -0
  36. package/components/identity.clients/operations/lib/Entity.js.map +1 -0
  37. package/components/identity.clients/operations/lib/canonical.d.ts +11 -0
  38. package/components/identity.clients/operations/lib/canonical.js +24 -0
  39. package/components/identity.clients/operations/lib/canonical.js.map +1 -0
  40. package/components/identity.clients/operations/lib/cimd.d.ts +8 -0
  41. package/components/identity.clients/operations/lib/cimd.js +86 -0
  42. package/components/identity.clients/operations/lib/cimd.js.map +1 -0
  43. package/components/identity.clients/operations/lib/errors.d.ts +7 -0
  44. package/components/identity.clients/operations/lib/errors.js +5 -0
  45. package/components/identity.clients/operations/lib/errors.js.map +1 -0
  46. package/components/identity.clients/operations/lib/index.d.ts +6 -0
  47. package/components/identity.clients/operations/lib/index.js +7 -0
  48. package/components/identity.clients/operations/lib/index.js.map +1 -0
  49. package/components/identity.clients/operations/lib/redirect.d.ts +10 -0
  50. package/components/identity.clients/operations/lib/redirect.js +32 -0
  51. package/components/identity.clients/operations/lib/redirect.js.map +1 -0
  52. package/components/identity.clients/operations/register.d.ts +37 -0
  53. package/components/identity.clients/operations/register.js +84 -0
  54. package/components/identity.clients/operations/register.js.map +1 -0
  55. package/components/identity.clients/source/describe.ts +65 -0
  56. package/components/identity.clients/source/lib/Context.ts +38 -0
  57. package/components/identity.clients/source/lib/Entity.ts +31 -0
  58. package/components/identity.clients/source/lib/canonical.test.ts +43 -0
  59. package/components/identity.clients/source/lib/canonical.ts +27 -0
  60. package/components/identity.clients/source/lib/cimd.test.ts +120 -0
  61. package/components/identity.clients/source/lib/cimd.ts +107 -0
  62. package/components/identity.clients/source/lib/errors.ts +4 -0
  63. package/components/identity.clients/source/lib/index.ts +6 -0
  64. package/components/identity.clients/source/lib/redirect.test.ts +41 -0
  65. package/components/identity.clients/source/lib/redirect.ts +35 -0
  66. package/components/identity.clients/source/register.ts +125 -0
  67. package/components/identity.clients/tsconfig.json +10 -0
  68. package/components/identity.clients/tsconfig.tsbuildinfo +1 -0
  69. package/components/identity.clients/types/index.d.ts +3 -0
  70. package/components/identity.clients/types/toa.d.ts +109 -0
  71. package/components/identity.credentials/operations/list.d.ts +2 -1
  72. package/components/identity.credentials/operations/list.js.map +1 -1
  73. package/components/identity.credentials/source/list.ts +2 -1
  74. package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
  75. package/components/identity.credentials/types/index.d.ts +3 -0
  76. package/components/identity.credentials/types/toa.d.ts +30 -0
  77. package/components/identity.federation/manifest.toa.yaml +37 -1
  78. package/components/identity.federation/operations/authenticate.d.ts +1 -1
  79. package/components/identity.federation/operations/lib/Ctx.d.ts +2 -2
  80. package/components/identity.federation/operations/lib/decode.js +22 -8
  81. package/components/identity.federation/operations/lib/decode.js.map +1 -1
  82. package/components/identity.federation/operations/lib/errors.d.ts +7 -0
  83. package/components/identity.federation/operations/lib/errors.js +4 -0
  84. package/components/identity.federation/operations/lib/errors.js.map +1 -1
  85. package/components/identity.federation/operations/lib/exchange.js +11 -6
  86. package/components/identity.federation/operations/lib/exchange.js.map +1 -1
  87. package/components/identity.federation/operations/types/configuration.d.ts +1 -1
  88. package/components/identity.federation/operations/types/context.d.ts +3 -2
  89. package/components/identity.federation/source/authenticate.ts +1 -1
  90. package/components/identity.federation/source/lib/Ctx.ts +2 -2
  91. package/components/identity.federation/source/lib/decode.ts +25 -9
  92. package/components/identity.federation/source/lib/errors.ts +5 -0
  93. package/components/identity.federation/source/lib/exchange.ts +11 -7
  94. package/components/identity.federation/source/types/configuration.ts +1 -1
  95. package/components/identity.federation/source/types/context.ts +3 -2
  96. package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
  97. package/components/identity.federation/types/index.d.ts +3 -0
  98. package/components/identity.federation/types/toa.d.ts +127 -0
  99. package/components/identity.grants/manifest.toa.yaml +245 -0
  100. package/components/identity.grants/operations/authorize.d.ts +35 -0
  101. package/components/identity.grants/operations/authorize.js +54 -0
  102. package/components/identity.grants/operations/authorize.js.map +1 -0
  103. package/components/identity.grants/operations/exchange.d.ts +37 -0
  104. package/components/identity.grants/operations/exchange.js +89 -0
  105. package/components/identity.grants/operations/exchange.js.map +1 -0
  106. package/components/identity.grants/operations/lib/code.d.ts +15 -0
  107. package/components/identity.grants/operations/lib/code.js +30 -0
  108. package/components/identity.grants/operations/lib/code.js.map +1 -0
  109. package/components/identity.grants/operations/lib/identify.d.ts +6 -0
  110. package/components/identity.grants/operations/lib/identify.js +15 -0
  111. package/components/identity.grants/operations/lib/identify.js.map +1 -0
  112. package/components/identity.grants/operations/lib/index.d.ts +3 -0
  113. package/components/identity.grants/operations/lib/index.js +3 -0
  114. package/components/identity.grants/operations/lib/index.js.map +1 -0
  115. package/components/identity.grants/operations/list.d.ts +13 -0
  116. package/components/identity.grants/operations/list.js +18 -0
  117. package/components/identity.grants/operations/list.js.map +1 -0
  118. package/components/identity.grants/operations/revoke.d.ts +18 -0
  119. package/components/identity.grants/operations/revoke.js +25 -0
  120. package/components/identity.grants/operations/revoke.js.map +1 -0
  121. package/components/identity.grants/source/authorize.ts +90 -0
  122. package/components/identity.grants/source/exchange.ts +130 -0
  123. package/components/identity.grants/source/lib/code.ts +39 -0
  124. package/components/identity.grants/source/lib/identify.ts +16 -0
  125. package/components/identity.grants/source/lib/index.ts +3 -0
  126. package/components/identity.grants/source/list.ts +28 -0
  127. package/components/identity.grants/source/revoke.ts +38 -0
  128. package/components/identity.grants/tsconfig.json +10 -0
  129. package/components/identity.grants/tsconfig.tsbuildinfo +1 -0
  130. package/components/identity.grants/types/index.d.ts +60 -0
  131. package/components/identity.grants/types/toa.d.ts +108 -0
  132. package/components/identity.keys/manifest.toa.yaml +1 -0
  133. package/components/identity.keys/operations/create.d.ts +1 -1
  134. package/components/identity.keys/operations/revoke.d.ts +1 -1
  135. package/components/identity.keys/source/create.ts +1 -1
  136. package/components/identity.keys/source/revoke.ts +1 -1
  137. package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
  138. package/components/identity.keys/types/index.d.ts +3 -0
  139. package/components/identity.keys/types/toa.d.ts +47 -0
  140. package/components/identity.otp/operations/lib/Context.d.ts +4 -2
  141. package/components/identity.otp/source/lib/Context.ts +4 -2
  142. package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
  143. package/components/identity.otp/types/index.d.ts +3 -0
  144. package/components/identity.otp/types/toa.d.ts +56 -0
  145. package/components/identity.passkeys/manifest.toa.yaml +3 -0
  146. package/components/identity.passkeys/operations/authenticate.d.ts +1 -1
  147. package/components/identity.passkeys/operations/challenge.d.ts +1 -1
  148. package/components/identity.passkeys/operations/create.d.ts +1 -1
  149. package/components/identity.passkeys/operations/types/Context.d.ts +2 -1
  150. package/components/identity.passkeys/operations/use.d.ts +1 -1
  151. package/components/identity.passkeys/source/authenticate.ts +1 -1
  152. package/components/identity.passkeys/source/challenge.ts +1 -1
  153. package/components/identity.passkeys/source/create.ts +1 -1
  154. package/components/identity.passkeys/source/types/Context.ts +2 -1
  155. package/components/identity.passkeys/source/use.ts +1 -1
  156. package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
  157. package/components/identity.passkeys/types/index.d.ts +3 -0
  158. package/components/identity.passkeys/types/toa.d.ts +120 -0
  159. package/components/identity.roles/operations/principal.d.ts +1 -1
  160. package/components/identity.roles/source/principal.ts +1 -1
  161. package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
  162. package/components/identity.roles/types/index.d.ts +3 -0
  163. package/components/identity.roles/types/toa.d.ts +43 -0
  164. package/components/identity.tokens/manifest.toa.yaml +34 -3
  165. package/components/identity.tokens/operations/authenticate.d.ts +2 -1
  166. package/components/identity.tokens/operations/authenticate.js +8 -0
  167. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  168. package/components/identity.tokens/operations/decrypt.d.ts +2 -1
  169. package/components/identity.tokens/operations/decrypt.js +2 -2
  170. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  171. package/components/identity.tokens/operations/encrypt.d.ts +2 -1
  172. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  173. package/components/identity.tokens/operations/issue.d.ts +2 -1
  174. package/components/identity.tokens/operations/issue.js +2 -1
  175. package/components/identity.tokens/operations/issue.js.map +1 -1
  176. package/components/identity.tokens/operations/lib/form.d.ts +8 -0
  177. package/components/identity.tokens/operations/lib/form.js +24 -0
  178. package/components/identity.tokens/operations/lib/form.js.map +1 -0
  179. package/components/identity.tokens/operations/lib/index.d.ts +2 -1
  180. package/components/identity.tokens/operations/lib/index.js +1 -1
  181. package/components/identity.tokens/operations/lib/index.js.map +1 -1
  182. package/components/identity.tokens/source/authenticate.test.ts +8 -5
  183. package/components/identity.tokens/source/authenticate.ts +12 -1
  184. package/components/identity.tokens/source/decrypt.test.ts +1 -1
  185. package/components/identity.tokens/source/decrypt.ts +4 -3
  186. package/components/identity.tokens/source/encrypt.test.ts +1 -1
  187. package/components/identity.tokens/source/encrypt.ts +2 -1
  188. package/components/identity.tokens/source/issue.test.ts +71 -0
  189. package/components/identity.tokens/source/issue.ts +4 -2
  190. package/components/identity.tokens/source/lib/form.test.ts +25 -0
  191. package/components/identity.tokens/source/lib/form.ts +31 -0
  192. package/components/identity.tokens/source/lib/index.ts +2 -1
  193. package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
  194. package/components/identity.tokens/types/index.d.ts +61 -0
  195. package/components/identity.tokens/types/toa.d.ts +113 -0
  196. package/documentation/cache.md +10 -1
  197. package/documentation/consent.md +101 -0
  198. package/documentation/identity.md +3 -1
  199. package/documentation/io.md +36 -2
  200. package/documentation/oauth.md +179 -0
  201. package/documentation/protocol.md +4 -0
  202. package/features/auth.bearer.feature +120 -0
  203. package/features/cache.feature +46 -0
  204. package/features/oauth.clients.feature +195 -0
  205. package/features/oauth.discovery.feature +165 -0
  206. package/features/oauth.grants.feature +286 -0
  207. package/features/steps/Gateway.ts +6 -1
  208. package/features/steps/Parameters.ts +4 -0
  209. package/features/steps/components/echo/manifest.toa.yaml +2 -0
  210. package/features/steps/components/octets.tester/manifest.toa.yaml +2 -0
  211. package/features/steps/components/octets.tester/operations/redirect.js +6 -1
  212. package/features/steps/components/users/manifest.toa.yaml +1 -0
  213. package/package.json +8 -6
  214. package/readme.md +3 -0
  215. package/schemas/annotation.cos.yaml +30 -0
  216. package/source/Annotation.ts +23 -0
  217. package/source/Factory.ts +1 -1
  218. package/source/HTTP/Server.ts +9 -5
  219. package/source/HTTP/formats/form.ts +21 -0
  220. package/source/HTTP/formats/index.ts +17 -2
  221. package/source/HTTP/messages.test.ts +25 -1
  222. package/source/HTTP/messages.ts +10 -3
  223. package/source/Interception.ts +8 -2
  224. package/source/deployment.ts +4 -1
  225. package/source/directives/auth/Authorization.ts +46 -19
  226. package/source/directives/auth/Incept.ts +14 -9
  227. package/source/directives/auth/schemes.test.ts +8 -4
  228. package/source/directives/auth/schemes.ts +22 -9
  229. package/source/directives/auth/types.ts +5 -2
  230. package/source/directives/cache/Cache.ts +13 -4
  231. package/source/directives/cache/Control.ts +0 -3
  232. package/source/directives/flow/Fetch.ts +1 -1
  233. package/source/directives/index.ts +2 -1
  234. package/source/directives/io/IO.ts +2 -0
  235. package/source/directives/io/Output.ts +4 -1
  236. package/source/directives/io/Status.test.ts +84 -0
  237. package/source/directives/io/Status.ts +46 -0
  238. package/source/directives/oauth/Discovery.ts +76 -0
  239. package/source/directives/oauth/documents.ts +113 -0
  240. package/source/directives/oauth/index.ts +3 -0
  241. package/source/directives/octets/Delete.ts +1 -1
  242. package/source/directives/octets/Get.ts +1 -1
  243. package/source/directives/octets/Workflow.ts +1 -1
  244. package/source/directives/octets/workflows/Execution.ts +1 -1
  245. package/source/index.ts +6 -0
  246. package/source/octets.d.ts +17 -0
  247. package/source/userland.ts +47 -0
  248. package/transpiled/Annotation.d.ts +17 -0
  249. package/transpiled/Factory.js +1 -1
  250. package/transpiled/Factory.js.map +1 -1
  251. package/transpiled/HTTP/Server.d.ts +4 -1
  252. package/transpiled/HTTP/Server.js +4 -3
  253. package/transpiled/HTTP/Server.js.map +1 -1
  254. package/transpiled/HTTP/formats/form.d.ts +9 -0
  255. package/transpiled/HTTP/formats/form.js +18 -0
  256. package/transpiled/HTTP/formats/form.js.map +1 -0
  257. package/transpiled/HTTP/formats/index.d.ts +10 -2
  258. package/transpiled/HTTP/formats/index.js +10 -0
  259. package/transpiled/HTTP/formats/index.js.map +1 -1
  260. package/transpiled/HTTP/messages.d.ts +6 -0
  261. package/transpiled/HTTP/messages.js +3 -3
  262. package/transpiled/HTTP/messages.js.map +1 -1
  263. package/transpiled/Interception.d.ts +4 -1
  264. package/transpiled/Interception.js +4 -2
  265. package/transpiled/Interception.js.map +1 -1
  266. package/transpiled/deployment.js +2 -0
  267. package/transpiled/deployment.js.map +1 -1
  268. package/transpiled/directives/auth/Authorization.d.ts +1 -1
  269. package/transpiled/directives/auth/Authorization.js +38 -18
  270. package/transpiled/directives/auth/Authorization.js.map +1 -1
  271. package/transpiled/directives/auth/Incept.d.ts +1 -1
  272. package/transpiled/directives/auth/Incept.js +11 -7
  273. package/transpiled/directives/auth/Incept.js.map +1 -1
  274. package/transpiled/directives/auth/schemes.d.ts +16 -4
  275. package/transpiled/directives/auth/schemes.js +20 -8
  276. package/transpiled/directives/auth/schemes.js.map +1 -1
  277. package/transpiled/directives/auth/types.d.ts +4 -2
  278. package/transpiled/directives/cache/Cache.js +9 -2
  279. package/transpiled/directives/cache/Cache.js.map +1 -1
  280. package/transpiled/directives/cache/Control.js +0 -2
  281. package/transpiled/directives/cache/Control.js.map +1 -1
  282. package/transpiled/directives/index.js +2 -1
  283. package/transpiled/directives/index.js.map +1 -1
  284. package/transpiled/directives/io/IO.js +2 -0
  285. package/transpiled/directives/io/IO.js.map +1 -1
  286. package/transpiled/directives/io/Output.js +4 -1
  287. package/transpiled/directives/io/Output.js.map +1 -1
  288. package/transpiled/directives/io/Status.d.ts +19 -0
  289. package/transpiled/directives/io/Status.js +34 -0
  290. package/transpiled/directives/io/Status.js.map +1 -0
  291. package/transpiled/directives/oauth/Discovery.d.ts +23 -0
  292. package/transpiled/directives/oauth/Discovery.js +58 -0
  293. package/transpiled/directives/oauth/Discovery.js.map +1 -0
  294. package/transpiled/directives/oauth/documents.d.ts +9 -0
  295. package/transpiled/directives/oauth/documents.js +82 -0
  296. package/transpiled/directives/oauth/documents.js.map +1 -0
  297. package/transpiled/directives/oauth/index.d.ts +2 -0
  298. package/transpiled/directives/oauth/index.js +3 -0
  299. package/transpiled/directives/oauth/index.js.map +1 -0
  300. package/transpiled/index.d.ts +2 -0
  301. package/transpiled/userland.d.ts +39 -0
  302. package/transpiled/userland.js +7 -0
  303. package/transpiled/userland.js.map +1 -0
  304. package/tsconfig.tsbuildinfo +1 -1
  305. package/components/identity.basic/operations/types.d.ts +0 -61
  306. package/components/identity.basic/operations/types.js +0 -2
  307. package/components/identity.basic/operations/types.js.map +0 -1
  308. package/components/identity.basic/source/types.ts +0 -69
  309. package/components/identity.tokens/operations/lib/types.d.ts +0 -88
  310. package/components/identity.tokens/operations/lib/types.js +0 -2
  311. package/components/identity.tokens/operations/lib/types.js.map +0 -1
  312. package/components/identity.tokens/source/lib/types.ts +0 -102
@@ -0,0 +1,101 @@
1
+ # Consent
2
+
3
+ The one endpoint of the [authorization server](oauth.md) that needs a person, and so the one this
4
+ extension does not serve. An application serves it at the URL its `exposition.oauth.authorize`
5
+ names.
6
+
7
+ The page is reached by a client opening it in the user's browser. It authenticates the user, shows
8
+ them who is asking, and on their assent redirects back to the client with a code.
9
+
10
+ ## What the page receives
11
+
12
+ ```
13
+ GET https://app.example.com/oauth/authorize
14
+ ?response_type=code
15
+ &client_id=https%3A%2F%2Fclaude.ai%2Foauth%2Fclaude-code-client-metadata
16
+ &redirect_uri=http%3A%2F%2Flocalhost%3A3118%2Fcallback
17
+ &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
18
+ &code_challenge_method=S256
19
+ &state=af0ifjsldkj
20
+ &resource=https%3A%2F%2Fapi.example.com%2Fmcp
21
+ &scope=app:notes
22
+ ```
23
+
24
+ `state` is the client's and is returned untouched. `code_challenge`, `code_challenge_method`,
25
+ `client_id`, `redirect_uri` and `resource` are passed on as they arrived.
26
+
27
+ ## What the page does
28
+
29
+ The user is authenticated by whatever means the application already offers. The page then holds
30
+ that identity's own `Token`, and makes two calls with it.
31
+
32
+ Who is asking:
33
+
34
+ ```http
35
+ GET /identity/clients/https%3A%2F%2Fclaude.ai%2Foauth%2Fclaude-code-client-metadata/ HTTP/1.1
36
+ authorization: Token …
37
+ ```
38
+
39
+ ```yaml
40
+ client_id: https://claude.ai/oauth/claude-code-client-metadata
41
+ client_name: Claude Code
42
+ client_uri: https://claude.ai
43
+ logo_uri: https://claude.ai/logo.png
44
+ redirect_uris: [http://localhost/callback, http://127.0.0.1/callback]
45
+ ```
46
+
47
+ The page displays `client_name`, and **the host of `redirect_uri`**. A loopback redirect is
48
+ indistinguishable from any other local process, so the user is told where the code will be sent.
49
+
50
+ On assent:
51
+
52
+ ```http
53
+ POST /identity/grants/:identity/ HTTP/1.1
54
+ authorization: Token …
55
+ content-type: application/json
56
+
57
+ {"client": "…", "redirect": "…", "challenge": "…", "method": "S256",
58
+ "scope": ["app:notes"], "resource": ["https://api.example.com/mcp"]}
59
+ ```
60
+
61
+ ```http
62
+ 201 Created
63
+ cache-control: no-store
64
+
65
+ {"code": "SplxlOBeZQQYbYS6WxSbIA", "expires_in": 60}
66
+ ```
67
+
68
+ `:identity` is the id of the identity that consents, which is the one the page authenticated.
69
+
70
+ ## What the page answers
71
+
72
+ On assent, a redirect carrying the code, the client's `state`, and the issuer:
73
+
74
+ ```
75
+ 302 Found
76
+ location: http://localhost:3118/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.example.com
77
+ ```
78
+
79
+ A single-page application does this with `location.assign`; nothing requires a server redirect.
80
+
81
+ `iss` is [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207), and the metadata declares that it is
82
+ sent, so a client rejects a response without it. Its value is the `issuer` of
83
+ `/.well-known/oauth-authorization-server`, which the page may read.
84
+
85
+ On refusal, a redirect saying so, not an error page:
86
+
87
+ ```
88
+ 302 Found
89
+ location: http://localhost:3118/callback?error=access_denied&state=af0ifjsldkj&iss=https%3A%2F%2Fapi.example.com
90
+ ```
91
+
92
+ ## Errors
93
+
94
+ `400` with `error` and `error_description` is answered when the client is unknown, when the
95
+ redirect is not one that client may receive a code at, or when the challenge method is not `S256`.
96
+ A redirect that was not registered must not be redirected to; the page states the refusal itself.
97
+
98
+ ## Consent is asked every time
99
+
100
+ A grant is recorded, and `GET /identity/grants/:identity/` lists what a user allowed, but it is
101
+ not consulted to skip this page.
@@ -83,7 +83,9 @@ the request path.
83
83
 
84
84
  ### Bearer scheme
85
85
 
86
- OpenID tokens issued by trusted providers.
86
+ OpenID tokens issued by trusted providers, and access tokens issued by
87
+ [this authorization server](oauth.md) to OAuth clients. Both are presented the same way, and each
88
+ provider says which credentials are its own; a credential neither claims is refused.
87
89
  For more information, refer
88
90
  to [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html),
89
91
  [RFC6750](https://datatracker.ietf.org/doc/html/rfc6750).
@@ -1,7 +1,7 @@
1
1
  # I/O restrictions
2
2
 
3
3
  The Exposition comes with `io` directives to control access to the operation's input and output
4
- properties.
4
+ properties, and to let an operation state the status of its reply.
5
5
 
6
6
  ## Input
7
7
 
@@ -53,7 +53,41 @@ GET:
53
53
  io:output: false
54
54
  ```
55
55
 
56
- Output restrictions are not applied to stream responses and errors.
56
+ Output restrictions are not applied to stream responses, nor to a reply the gateway built out of an
57
+ exception — an operation that returns an error is answered with a code and a message of the
58
+ gateway's own, and a list of permitted properties has nothing to say about those. A reply the
59
+ operation returned is restricted whatever status it carries, see [Status](#status).
60
+
61
+ ## Status
62
+
63
+ The `io:status` optional directive names a property of the reply that carries the status of the
64
+ response. The property is removed from the body.
65
+
66
+ ```yaml
67
+ POST:
68
+ endpoint: register
69
+ io:status: status
70
+ io:output: [client_id, client_name, error, error_description]
71
+ ```
72
+
73
+ ```javascript
74
+ return { status: 400, error: 'invalid_redirect_uri' }
75
+ ```
76
+
77
+ ```http
78
+ 400 Bad Request
79
+
80
+ error: invalid_redirect_uri
81
+ ```
82
+
83
+ A reply that does not carry the property is answered with the status it would have been. A property
84
+ that is not a number is an error.
85
+
86
+ This is for an outcome the operation knows and the transport does not — a protocol that states its
87
+ error as a body rather than as a status of its own, or a request that created where it might have
88
+ updated. Such an operation returns no error: both replies are its output, and `io:output` permits
89
+ the properties of both. An operation that does return an error is unaffected — that reply is the
90
+ gateway's, and is not restricted.
57
91
 
58
92
  ## Throttling
59
93
 
@@ -0,0 +1,179 @@
1
+ # OAuth 2.1 Authorization Server
2
+
3
+ An application exposes an authorization server by annotating one, and none without it. A client
4
+ authorized this way holds a token of the identity that consented, and presents it as `Bearer`.
5
+
6
+ ## Annotation
7
+
8
+ ```yaml
9
+ exposition:
10
+ authorities:
11
+ local: api.example.com
12
+ oauth:
13
+ authorize: https://app.example.com/oauth/authorize
14
+ resources: ['/mcp/']
15
+ scopes: [app:notes, app:notes:read]
16
+ registration: closed
17
+ ```
18
+
19
+ <dl>
20
+ <dt><code>authorize</code></dt>
21
+ <dd>Required. The absolute URL of the consent page. The application serves it; see
22
+ <a href="consent.md">Consent</a>.</dd>
23
+ <dt><code>resources</code></dt>
24
+ <dd>Paths advertised as protected resources, each with a document of its own and named by the
25
+ challenge a request to it is refused with. A token is not yet bound to one, see
26
+ <a href="#audience">Audience</a>.</dd>
27
+ <dt><code>scopes</code></dt>
28
+ <dd>Advertised as what a client may ask for. Each is a <a href="access.md#roles">role</a>, or a
29
+ scope within one.</dd>
30
+ <dt><code>registration</code></dt>
31
+ <dd><code>open</code> advertises the registration endpoint, <code>closed</code> does not.
32
+ Defaults to <code>closed</code>.</dd>
33
+ </dl>
34
+
35
+ The issuer is the authority's configured host, over `https` unless the host is a loopback one.
36
+
37
+ ## Discovery
38
+
39
+ Two documents are served, at the paths their specifications fix, to anyone.
40
+
41
+ `/.well-known/oauth-authorization-server` is
42
+ [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414) metadata:
43
+
44
+ ```yaml
45
+ issuer: https://api.example.com
46
+ authorization_endpoint: https://app.example.com/oauth/authorize
47
+ token_endpoint: https://api.example.com/identity/grants/
48
+ registration_endpoint: https://api.example.com/identity/clients/
49
+ response_types_supported: [code]
50
+ grant_types_supported: [authorization_code]
51
+ code_challenge_methods_supported: [S256]
52
+ token_endpoint_auth_methods_supported: [none]
53
+ client_id_metadata_document_supported: true
54
+ authorization_response_iss_parameter_supported: true
55
+ ```
56
+
57
+ `/.well-known/oauth-protected-resource` is
58
+ [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) metadata. Each configured resource has one of
59
+ its own, at the path the well-known segment is followed by: `/mcp/` is read at
60
+ `/.well-known/oauth-protected-resource/mcp`.
61
+
62
+ ```yaml
63
+ resource: https://api.example.com/mcp
64
+ authorization_servers: [https://api.example.com]
65
+ bearer_methods_supported: [header]
66
+ ```
67
+
68
+ `/.well-known/openid-configuration` answers with the authorization server metadata, for a client
69
+ that reads only that.
70
+
71
+ A reply of `401` carries the challenge that names the document:
72
+
73
+ ```http
74
+ 401 Unauthorized
75
+ www-authenticate: Bearer resource_metadata="https://api.example.com/.well-known/oauth-protected-resource/mcp", scope="app:notes"
76
+ ```
77
+
78
+ ## Clients
79
+
80
+ A `client_id` that is an `https` URL is a
81
+ [Client ID Metadata Document](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00):
82
+ the client publishes what it is at that URL, and the document is read from there. Only origins
83
+ `identity.clients` is configured to trust are read, and an empty list reads none:
84
+
85
+ ```yaml
86
+ configuration:
87
+ identity.clients:
88
+ trust:
89
+ - https://claude.ai
90
+ ```
91
+
92
+ Anything else was registered through
93
+ [RFC 7591](https://www.rfc-editor.org/rfc/rfc7591), which `registration: open` advertises:
94
+
95
+ ```http
96
+ POST /identity/clients/ HTTP/1.1
97
+ content-type: application/json
98
+
99
+ {"client_name": "Claude", "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"]}
100
+ ```
101
+
102
+ ```http
103
+ 201 Created
104
+
105
+ {"client_id": "c8f3a1e4d8c9b2f6a0e5d7c3b1a9f8e2", "client_name": "Claude", …}
106
+ ```
107
+
108
+ A registration is addressed by a hash of what it says, so the same metadata is one client however
109
+ many times it arrives. A registration cannot be changed: a revision is a different client.
110
+
111
+ Only public clients are supported. `token_endpoint_auth_method` other than `none` is refused.
112
+
113
+ `redirect_uris` are matched exactly, except for a loopback address, where the port is ignored
114
+ ([RFC 8252 §7.3](https://datatracker.ietf.org/doc/html/rfc8252#section-7.3)).
115
+
116
+ `GET /identity/clients/:id/` returns a client to any authenticated identity, which is what the
117
+ consent page reads.
118
+
119
+ ## Token endpoint
120
+
121
+ `POST /identity/grants/`, anonymous, `application/x-www-form-urlencoded`.
122
+
123
+ ```http
124
+ POST /identity/grants/ HTTP/1.1
125
+ content-type: application/x-www-form-urlencoded
126
+
127
+ grant_type=authorization_code&code=SplxlO&code_verifier=dBjftJeZ&redirect_uri=…&client_id=…
128
+ ```
129
+
130
+ ```http
131
+ 200 OK
132
+ cache-control: no-store
133
+
134
+ {"access_token": "…", "token_type": "Bearer", "expires_in": 2592000}
135
+ ```
136
+
137
+ `code_verifier` is required: the code was issued against its `S256` hash
138
+ ([RFC 7636](https://www.rfc-editor.org/rfc/rfc7636)). A code may be redeemed once, and is spent
139
+ whether or not the verifier matches. Every refusal is `invalid_grant`.
140
+
141
+ A code is valid for `identity.grants.lifetime` seconds, 60 by default. A token is valid for
142
+ `identity.grants.token` seconds, 30 days by default; `0` is until it is revoked.
143
+
144
+ ## Grants
145
+
146
+ A grant is what a user allowed a client. One is held per identity per client.
147
+
148
+ `GET /identity/grants/:identity/` lists them. `DELETE /identity/grants/:identity/:id/` revokes one,
149
+ which disables the key its token was issued under: the token stops being one within
150
+ `identity.tokens.cache.ttl`.
151
+
152
+ ## Tokens
153
+
154
+ An access token is a token of the identity that consented, carrying the
155
+ [roles](access.md#roles) that identity holds, or the subset the client asked for as `scope`. It is
156
+ presented as `Bearer`, which `identity.federation` also answers to — see
157
+ [Bearer scheme](identity.md#bearer-scheme).
158
+
159
+ A `scope` a client asks for is not checked against `scopes_supported`: it is granted if the
160
+ consenting identity holds it, and refused as `invalid_scope` otherwise. What is advertised says
161
+ what an application means to offer, not what the server will refuse.
162
+
163
+ ## Audience
164
+
165
+ `resource` ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707)) is accepted at both endpoints and
166
+ recorded on the grant. **It does not yet restrict the token.** An access token carries the rights
167
+ of the identity that consented, over every path that identity may reach, whichever resource it was
168
+ asked for.
169
+
170
+ Restricting it is a `permissions` argument at the `identity.tokens.issue` call in
171
+ `identity.grants.exchange` — `{'/mcp/**': ['*']}` for a resource at `/mcp/` — enforced by
172
+ `permits()`, which is already what reads a token's permissions on every request. It waits on the
173
+ resource that will check the audience itself.
174
+
175
+ ## References
176
+
177
+ - [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1)
178
+ - [Consent](consent.md), the page an application serves
179
+ - [Features](../features/oauth.grants.feature)
@@ -9,6 +9,10 @@ The following media types are supported for both requests and responses:
9
9
  - `application/msgpack` using [msgpackr](https://github.com/kriszyp/msgpackr)
10
10
  - `text/plain`
11
11
 
12
+ A request may also be sent as `application/x-www-form-urlencoded`. A form is read and never
13
+ written, so it is not a format a response is negotiated to. A name repeated in a form is read as
14
+ the list it is.
15
+
12
16
  The response format is determined by content negotiation
13
17
  using [negotiator](https://github.com/jshttp/negotiator).
14
18
 
@@ -0,0 +1,120 @@
1
+ @security
2
+ Feature: More than one provider claims Bearer
3
+
4
+ An OpenID `id_token` and a token this gateway issued are both presented as `Bearer`,
5
+ and only their own provider can verify either. The gateway asks each in turn.
6
+
7
+ Background:
8
+ Given local IDP is running
9
+ And the `identity.basic` database contains:
10
+ | _id | authority | username | password |
11
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
12
+ And the annotation:
13
+ """yaml
14
+ /:
15
+ io:output: true
16
+ /hello/:id:
17
+ auth:id: id
18
+ GET:
19
+ dev:stub: Hello
20
+ """
21
+
22
+ Scenario: A token this gateway issued is presented as Bearer
23
+ When the following request is received:
24
+ """
25
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
26
+ host: nex.toa.io
27
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
28
+ accept: text/plain
29
+ """
30
+ Then the following reply is sent:
31
+ """
32
+ 200 OK
33
+ authorization: Token ${{ token }}
34
+ content-type: text/plain
35
+
36
+ Hello
37
+ """
38
+ When the following request is received:
39
+ """
40
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
41
+ host: nex.toa.io
42
+ authorization: Bearer ${{ token }}
43
+ accept: text/plain
44
+ """
45
+ Then the following reply is sent:
46
+ """
47
+ 200 OK
48
+ content-type: text/plain
49
+
50
+ Hello
51
+ """
52
+ # the token was answered by its own provider, so nothing rotates it
53
+ And the reply does not contain:
54
+ """
55
+ authorization: Token
56
+ """
57
+
58
+ Scenario: A Bearer this gateway did not issue is answered by federation
59
+ Given the IDP token for Stranger is issued
60
+ When the following request is received:
61
+ """
62
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
63
+ host: nex.toa.io
64
+ authorization: Bearer ${{ Stranger.id_token }}
65
+ accept: text/plain
66
+ """
67
+ # `identity.tokens` declines it, and federation rejects an issuer it does not trust
68
+ Then the following reply is sent:
69
+ """
70
+ 401 Unauthorized
71
+ """
72
+
73
+ Scenario: A malformed Bearer is rejected, not an error
74
+ When the following request is received:
75
+ """
76
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
77
+ host: nex.toa.io
78
+ authorization: Bearer not.a.token
79
+ accept: text/plain
80
+ """
81
+ Then the following reply is sent:
82
+ """
83
+ 401 Unauthorized
84
+ """
85
+
86
+ Scenario: Both kinds of Bearer against one authority
87
+ Given the `identity.federation` configuration:
88
+ """yaml
89
+ trust:
90
+ - iss: http://localhost:44444
91
+ aud: test
92
+ """
93
+ And the IDP token for User is issued
94
+ When the following request is received:
95
+ """
96
+ GET /identity/ HTTP/1.1
97
+ host: nex.toa.io
98
+ authorization: Bearer ${{ User.id_token }}
99
+ accept: application/yaml
100
+ """
101
+ Then the following reply is sent:
102
+ """
103
+ 200 OK
104
+ authorization: Token ${{ federated }}
105
+
106
+ id: ${{ User.id }}
107
+ """
108
+ When the following request is received:
109
+ """
110
+ GET /identity/ HTTP/1.1
111
+ host: nex.toa.io
112
+ authorization: Bearer ${{ federated }}
113
+ accept: application/yaml
114
+ """
115
+ Then the following reply is sent:
116
+ """
117
+ 200 OK
118
+
119
+ id: ${{ User.id }}
120
+ """
@@ -320,3 +320,49 @@ Feature: Caching
320
320
  200 OK
321
321
  cache-control: private, no-cache
322
322
  """
323
+
324
+ Scenario: An exact value is set whatever the method
325
+ Given the annotation:
326
+ """yaml
327
+ /:
328
+ io:output: true
329
+ /tokens/:
330
+ anonymous: true
331
+ cache:exact: no-store
332
+ POST:
333
+ dev:stub: issued
334
+ """
335
+ When the following request is received:
336
+ """
337
+ POST /tokens/ HTTP/1.1
338
+ host: nex.toa.io
339
+ accept: text/plain
340
+ """
341
+ # whether a reply may be stored is not a question about the method's cacheability
342
+ Then the following reply is sent:
343
+ """
344
+ 201 Created
345
+ cache-control: no-store
346
+ """
347
+
348
+ Scenario: Cacheability is still asked of safe methods only
349
+ Given the annotation:
350
+ """yaml
351
+ /:
352
+ io:output: true
353
+ /notes/:
354
+ anonymous: true
355
+ cache:control: max-age=60000, public
356
+ POST:
357
+ dev:stub: created
358
+ """
359
+ When the following request is received:
360
+ """
361
+ POST /notes/ HTTP/1.1
362
+ host: nex.toa.io
363
+ accept: text/plain
364
+ """
365
+ Then the reply does not contain:
366
+ """
367
+ cache-control:
368
+ """