@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
@@ -1,7 +1,12 @@
1
1
  function redirect (input) {
2
- return input.parameters.type in urls ? { url: urls[input.parameters.type] } : new Error()
2
+ return input.parameters.type in urls ? { url: urls[input.parameters.type] } : ERR_UNKNOWN
3
3
  }
4
4
 
5
+ // an error a caller is meant to see carries a code, always
6
+ const ERR_UNKNOWN = new (class UnknownError extends Error {
7
+ code = 'ERROR'
8
+ })()
9
+
5
10
  const urls = {
6
11
  'rfc': 'https://www.rfc-editor.org/rfc/rfc9564.txt',
7
12
  'img': 'https://www.w3.org/assets/logos/w3c/w3c-no-bars.svg'
@@ -21,6 +21,7 @@ operations:
21
21
  create:
22
22
  concurrency: retry
23
23
  query: false
24
+ errors: [0]
24
25
  input:
25
26
  properties:
26
27
  name:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.282",
3
+ "version": "1.0.0-alpha.283",
4
4
  "type": "module",
5
5
  "description": "Toa Exposition",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -19,9 +19,9 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@simplewebauthn/server": "14.0.0",
22
- "@toa.io/core": "1.0.0-alpha.282",
23
- "@toa.io/generic": "1.0.0-alpha.282",
24
- "@toa.io/schemas": "1.0.0-alpha.282",
22
+ "@toa.io/core": "1.0.0-alpha.283",
23
+ "@toa.io/generic": "1.0.0-alpha.283",
24
+ "@toa.io/schemas": "1.0.0-alpha.283",
25
25
  "bcryptjs": "3.0.3",
26
26
  "content-type": "3.0.0",
27
27
  "jose": "6.2.10",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "scripts": {
38
38
  "test": "echo \"Error: run tests from root\" && exit 1",
39
- "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation && npm run transpile:credentials && npm run transpile:keys && npm run transpile:passkeys && npm run transpile:otp",
39
+ "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation && npm run transpile:credentials && npm run transpile:keys && npm run transpile:passkeys && npm run transpile:otp && npm run transpile:clients && npm run transpile:grants",
40
40
  "transpile:bans": "tsc -p ./components/identity.bans",
41
41
  "transpile:basic": "tsc -p ./components/identity.basic",
42
42
  "transpile:tokens": "tsc -p ./components/identity.tokens",
@@ -46,6 +46,8 @@
46
46
  "transpile:keys": "tsc -p ./components/identity.keys",
47
47
  "transpile:passkeys": "tsc -p ./components/identity.passkeys",
48
48
  "transpile:otp": "tsc -p ./components/identity.otp",
49
+ "transpile:clients": "tsc -p ./components/identity.clients",
50
+ "transpile:grants": "tsc -p ./components/identity.grants",
49
51
  "features": "TSX_TSCONFIG_PATH=features/steps/tsconfig.json NODE_OPTIONS=--import=tsx cucumber-js",
50
52
  "features:h2c": "TOA_EXPOSITION_PROTOCOL=h2c TOA_AGENT_PROTOCOL=h2c TSX_TSCONFIG_PATH=features/steps/tsconfig.json NODE_OPTIONS=--import=tsx cucumber-js",
51
53
  "features:security": "TSX_TSCONFIG_PATH=features/steps/tsconfig.json NODE_OPTIONS=--import=tsx cucumber-js --tags @security",
@@ -56,5 +58,5 @@
56
58
  "@types/cors": "2.8.19",
57
59
  "@types/negotiator": "0.6.5"
58
60
  },
59
- "gitHead": "4007c814725dd015d3bbebb9a28e28a247645c4f"
61
+ "gitHead": "2f52b023bed0ccb2ecdb2b82fe59fbe38bf282e1"
60
62
  }
package/readme.md CHANGED
@@ -138,6 +138,7 @@ exposition:
138
138
  | `debug` | Output server errors. Default `false`. |
139
139
  | `ip` | Header the client address is read from. See [Client address](documentation/ip.md). |
140
140
  | `bouncer` | Meter of failed authentications per address. See [Identity authentication](documentation/identity.md#failed-authentications). |
141
+ | `oauth` | Authorization server exposed to OAuth clients. See [OAuth](documentation/oauth.md). |
141
142
 
142
143
  ### Observability
143
144
 
@@ -207,6 +208,8 @@ See also [telemetry ready probe](../telemetry/readme.md#ready-probe) and
207
208
  - [Resource Tree Definition](documentation/tree.md)
208
209
  - [Identity authentication](documentation/identity.md)
209
210
  - [Access authorization](documentation/access.md)
211
+ - [OAuth authorization server](documentation/oauth.md)
212
+ - [Consent](documentation/consent.md)
210
213
  - [BLOBs](documentation/octets.md)
211
214
  - [Components and resources](documentation/components.md)
212
215
  - [Caching](documentation/cache.md)
@@ -50,6 +50,36 @@ properties:
50
50
  protocol:
51
51
  type: string
52
52
  enum: [h1, h2c]
53
+ oauth:
54
+ type: object
55
+ properties:
56
+ authorize:
57
+ description: The absolute URL of the consent page the application serves
58
+ type: string
59
+ pattern: ^https?://
60
+ resources:
61
+ description: Paths a token may be restricted to
62
+ type: array
63
+ items:
64
+ type: string
65
+ pattern: ^/
66
+ scopes:
67
+ type: array
68
+ items:
69
+ type: string
70
+ trust:
71
+ description: Origins whose Client ID Metadata Documents may be read
72
+ type: array
73
+ items:
74
+ type: string
75
+ pattern: ^https://
76
+ registration:
77
+ type: string
78
+ enum: [open, closed]
79
+ default: closed
80
+ required:
81
+ - authorize
82
+ additionalProperties: false
53
83
  /: {}
54
84
  authorities:
55
85
  type: object
@@ -23,9 +23,32 @@ export interface Annotation {
23
23
 
24
24
  /** Failed authentications an address may make; none are metered unless set. See `identity.md`. */
25
25
  bouncer?: Bouncer
26
+
27
+ /** The authorization server this context exposes; none is exposed without it. */
28
+ oauth?: OAuth
26
29
  '/'?: object // parsed and validated by RTD.syntax.parse
27
30
  }
28
31
 
32
+ export interface OAuth {
33
+ /**
34
+ * Where a client sends the user to consent. The one endpoint of the flow that needs a
35
+ * person, and so the one this extension does not serve: an application builds it.
36
+ */
37
+ authorize: string
38
+
39
+ /** Paths a token may be restricted to, each advertised as a protected resource. */
40
+ resources?: string[]
41
+
42
+ /** What a client may ask for. Each is a role, or a scope within one. */
43
+ scopes?: string[]
44
+
45
+ /** Origins whose Client ID Metadata Documents may be read. None are without it. */
46
+ trust?: string[]
47
+
48
+ /** Dynamic Client Registration, off unless opened. */
49
+ registration?: 'open' | 'closed'
50
+ }
51
+
29
52
  export interface Bouncer {
30
53
  /** what an address may fail at once, 20 by default */
31
54
  attempts?: number
package/source/Factory.ts CHANGED
@@ -54,7 +54,7 @@ export class Factory implements extensions.Factory {
54
54
  const node = root.resolve()
55
55
  const methods = new EndpointsFactory(remotes)
56
56
  const directives = new DirectivesFactory(families, remotes, this.host, options)
57
- const interception = new Interception(interceptors)
57
+ const interception = new Interception(interceptors, options)
58
58
  const tree = new Tree(node, methods, directives)
59
59
 
60
60
  const composition = new Composition(this.host)
@@ -12,12 +12,13 @@ import { ClientError, Exception } from './exceptions.js'
12
12
  import { Context } from './Context.js'
13
13
  import { PROBE, Probe } from './Probe.js'
14
14
  import type { IncomingMessage, Protocol, ServerResponse } from './types.js'
15
- import type { Bouncer } from '../Annotation.js'
15
+ import type { Bouncer, OAuth } from '../Annotation.js'
16
16
 
17
17
  export class Server extends Connector {
18
18
  private readonly server: http.Server | http2.Http2Server
19
19
  private readonly properties: Properties
20
- private readonly authorities: Record<string, string>
20
+ /** Host to authority. A map: a `Host` of `constructor` names what a plain object answers. */
21
+ private readonly authorities: Map<string, string>
21
22
 
22
23
  /** Tracked for the drain: `Http2Server` has no `closeIdleConnections`. */
23
24
  private readonly sessions = new Set<http2.ServerHttp2Session>()
@@ -30,7 +31,7 @@ export class Server extends Connector {
30
31
  super()
31
32
 
32
33
  this.properties = properties
33
- this.authorities = Object.fromEntries(Object.entries(properties.authorities).map(([key, value]) => [value, key]))
34
+ this.authorities = new Map(Object.entries(properties.authorities).map(([key, value]) => [value, key]))
34
35
  this.server = instantiate(properties.protocol)
35
36
  this.probe = new Probe(properties.probe)
36
37
 
@@ -162,7 +163,7 @@ export class Server extends Connector {
162
163
 
163
164
  assert(this.process !== undefined, 'Request processor is not attached')
164
165
 
165
- const authority = this.authorities[host] ?? host
166
+ const authority = this.authorities.get(host) ?? host
166
167
 
167
168
  // if the request carries no trace context, the trace starts here
168
169
  const remote = trace(request.headers)
@@ -239,7 +240,7 @@ export class Server extends Connector {
239
240
  if (!response.writableEnded) {
240
241
  response.statusCode = status
241
242
 
242
- const message: OutgoingMessage = { status: response.statusCode }
243
+ const message: OutgoingMessage = { status: response.statusCode, authentic: true }
243
244
 
244
245
  // eslint-disable-next-line max-depth
245
246
  if (exception instanceof Exception && exception.headers !== undefined)
@@ -385,6 +386,9 @@ interface Properties {
385
386
 
386
387
  /** Failed authentications an address may make; none are metered unless set. */
387
388
  bouncer?: Bouncer
389
+
390
+ /** The authorization server this context exposes; none is exposed without it. */
391
+ oauth?: OAuth
388
392
  }
389
393
 
390
394
  export type Options = { authorities: Properties['authorities'] } & {
@@ -0,0 +1,21 @@
1
+ export const type = 'application/x-www-form-urlencoded'
2
+
3
+ /**
4
+ * A form is read and never written: it is what an HTML form and a handful of protocols send,
5
+ * and nothing asks for a reply in it.
6
+ *
7
+ * A name repeated in a form is a list, which is how a form says one — `URLSearchParams` keeps
8
+ * every value, and a name given once stays the string it was written as.
9
+ */
10
+ export function decode (buffer: Buffer, charset = 'utf-8'): any {
11
+ const params = new URLSearchParams(buffer.toString(charset as BufferEncoding))
12
+ const value: Record<string, string | string[]> = {}
13
+
14
+ for (const name of new Set(params.keys())) {
15
+ const values = params.getAll(name)
16
+
17
+ value[name] = values.length === 1 ? values[0] : values
18
+ }
19
+
20
+ return value
21
+ }
@@ -2,7 +2,9 @@ import * as json from './json.js'
2
2
  import * as yaml from './yaml.js'
3
3
  import * as msgpack from './msgpack.js'
4
4
  import * as text from './text.js'
5
+ import * as form from './form.js'
5
6
 
7
+ /** What a reply may be encoded as, and so what `accept` is negotiated against. */
6
8
  export const formats: Record<string, Format> = {
7
9
  [json.type]: json,
8
10
  [yaml.type]: yaml,
@@ -12,10 +14,23 @@ export const formats: Record<string, Format> = {
12
14
 
13
15
  export const types = Object.keys(formats)
14
16
 
15
- export interface Format {
17
+ /**
18
+ * What a request body may arrive as. Everything a reply may be, and a form besides — which
19
+ * is read and never written, so it is not something a reply can be asked for.
20
+ */
21
+ export const decoders: Record<string, Decoder> = {
22
+ ...formats,
23
+ [form.type]: form
24
+ }
25
+
26
+ export interface Decoder {
16
27
  readonly type: string
28
+
29
+ decode: (buffer: Buffer, charset?: string) => any
30
+ }
31
+
32
+ export interface Format extends Decoder {
17
33
  readonly multipart: string
18
34
 
19
35
  encode: (value: any) => Buffer
20
- decode: (buffer: Buffer, charset?: string) => any
21
36
  }
@@ -7,7 +7,7 @@ import { generate } from 'randomstring'
7
7
  import * as msgpack from 'msgpackr'
8
8
  import { multipart, read, type OutgoingMessage } from './messages.js'
9
9
  import { BadRequest, UnsupportedMediaType } from './exceptions.js'
10
- import { formats } from './formats/index.js'
10
+ import { formats, types } from './formats/index.js'
11
11
  import { Timing } from './Timing.js'
12
12
  import type * as http from 'node:http'
13
13
  import type { Context } from './Context.js'
@@ -87,6 +87,30 @@ describe('read', () => {
87
87
  await assert.rejects(read(request), BadRequest)
88
88
  })
89
89
 
90
+ it('should parse application/x-www-form-urlencoded', async () => {
91
+ const headers = { 'content-type': 'application/x-www-form-urlencoded' }
92
+ const form = 'grant_type=authorization_code&code=SplxlO&scope=a+b'
93
+ const request = createContext(generate(), headers, form)
94
+
95
+ assert.deepStrictEqual(await read(request), {
96
+ grant_type: 'authorization_code',
97
+ code: 'SplxlO',
98
+ scope: 'a b'
99
+ })
100
+ })
101
+
102
+ it('should read a repeated form name as the list it is', async () => {
103
+ const headers = { 'content-type': 'application/x-www-form-urlencoded' }
104
+ const request = createContext(generate(), headers, 'resource=one&resource=two')
105
+
106
+ assert.deepStrictEqual(await read(request), { resource: ['one', 'two'] })
107
+ })
108
+
109
+ it('should not offer a form as a reply encoding', () => {
110
+ assert.ok(!types.includes('application/x-www-form-urlencoded'),
111
+ 'a form is read and never written')
112
+ })
113
+
90
114
  it('should output correct mulitpart format', async () => {
91
115
  const response = new class extends PassThrough {
92
116
  public readonly headers = new Headers()
@@ -4,7 +4,7 @@ import { pipeline } from 'node:stream/promises'
4
4
  import { createHash } from 'node:crypto'
5
5
  import * as contentType from 'content-type'
6
6
  import { console } from 'openspan'
7
- import { type Format, formats } from './formats/index.js'
7
+ import { type Format, decoders } from './formats/index.js'
8
8
  import { BadRequest, NotAcceptable, UnsupportedMediaType } from './exceptions.js'
9
9
  import type { Context } from './Context.js'
10
10
  import type { ServerResponse } from './types.js'
@@ -49,10 +49,10 @@ export async function read (context: Context): Promise<any> {
49
49
 
50
50
  const { type, parameters } = contentType.parse(header)
51
51
 
52
- if (!(type in formats))
52
+ if (!(type in decoders))
53
53
  throw new UnsupportedMediaType()
54
54
 
55
- const format = formats[type]
55
+ const format = decoders[type]
56
56
  const buf = await context.buffer()
57
57
 
58
58
  try {
@@ -201,6 +201,13 @@ export interface OutgoingMessage {
201
201
 
202
202
  /** tag the response with a hash of the encoded body, see `conditional` */
203
203
  etag?: boolean
204
+
205
+ /**
206
+ * Built by the gateway rather than returned by an operation, as a request is `authentic`
207
+ * when it was made by one: what is in it is the gateway's own, and the checks that answer
208
+ * for what an operation returns have nothing to say about it.
209
+ */
210
+ authentic?: boolean
204
211
  }
205
212
 
206
213
  export interface Query {
@@ -1,15 +1,18 @@
1
+ import type * as http from './HTTP/index.js'
1
2
  import type { Input, Output } from './io.js'
2
3
 
3
4
  export class Interception implements Interceptor {
4
5
  private readonly interceptors: Interceptor[]
5
6
 
6
- public constructor (interceptors: Interceptor[]) {
7
+ public constructor (interceptors: Interceptor[], options: http.Options) {
7
8
  this.interceptors = interceptors
8
9
 
9
10
  // interceptors are module singletons, so a second gateway in one process —
10
11
  // which is how the features run — would otherwise inherit the first one's state
11
- for (const interceptor of interceptors)
12
+ for (const interceptor of interceptors) {
12
13
  interceptor.reset?.()
14
+ interceptor.mount?.(options)
15
+ }
13
16
  }
14
17
 
15
18
  public async intercept (input: Input): Promise<Output> {
@@ -29,4 +32,7 @@ export interface Interceptor {
29
32
 
30
33
  /** Discards whatever the interceptor accumulated while serving. */
31
34
  reset?: () => void
35
+
36
+ /** What the annotation says, for an interceptor that answers from it. */
37
+ mount?: (options: http.Options) => void
32
38
  }
@@ -67,6 +67,9 @@ export function deployment (_: unknown, annotation?: Annotation): Dependency {
67
67
  if (annotation.bouncer !== undefined)
68
68
  properties.bouncer = annotation.bouncer
69
69
 
70
+ if (annotation.oauth !== undefined)
71
+ properties.oauth = annotation.oauth
72
+
70
73
  service.variables!.push({
71
74
  name: 'TOA_EXPOSITION_PROPERTIES',
72
75
  value: JSON.stringify(properties)
@@ -83,4 +86,4 @@ export function deployment (_: unknown, annotation?: Annotation): Dependency {
83
86
  return { services: [service] }
84
87
  }
85
88
 
86
- type Properties = Pick<Annotation, 'authorities' | 'debug' | 'protocol' | 'bouncer' | 'ip'>
89
+ type Properties = Pick<Annotation, 'authorities' | 'debug' | 'protocol' | 'bouncer' | 'ip' | 'oauth'>
@@ -16,7 +16,7 @@ import { Federation } from './Federation.js'
16
16
  import { Anyone } from './Anyone.js'
17
17
  import { Input, type Declaration } from './Input.js'
18
18
  import { split } from './split.js'
19
- import { PRIMARY, provider as providerOf } from './schemes.js'
19
+ import { PRIMARY, UNRECOGNIZED, providers as providersOf } from './schemes.js'
20
20
  import { ATOM_GROUP } from '../../const.js'
21
21
  import { Quotas, Sync } from '../io/lib/throttle/index.js'
22
22
  import { Keys } from '../io/lib/throttle/Keys.js'
@@ -34,7 +34,7 @@ import type {
34
34
  Identity,
35
35
  Context,
36
36
  Remote,
37
- Schemes
37
+ Components
38
38
  } from './types.js'
39
39
 
40
40
  export class Authorization implements DirectiveFamily<Directive, Extension> {
@@ -42,7 +42,7 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
42
42
  public readonly name: string = 'auth'
43
43
  public readonly mandatory: boolean = true
44
44
 
45
- private readonly schemes = {} as unknown as Schemes
45
+ private readonly components: Components = {}
46
46
  private readonly discovery = {} as unknown as Discovery
47
47
  private tokens: Component | null = null
48
48
  private bans: Component | null = null
@@ -132,7 +132,7 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
132
132
  if (identity === null)
133
133
  return
134
134
 
135
- if (identity.scheme === PRIMARY && !identity.refresh)
135
+ if (identity.provider === PRIMARY && !identity.refresh)
136
136
  return
137
137
 
138
138
  if (await this.banned(identity))
@@ -140,7 +140,7 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
140
140
 
141
141
  // a token carries the roles it was issued with, and the refresh is where they are read again;
142
142
  // any other scheme has just read them, unless a Role directive already did
143
- if (identity.scheme === PRIMARY)
143
+ if (identity.provider === PRIMARY)
144
144
  identity.roles = await Role.get(identity, this.discovery.roles)
145
145
  else
146
146
  identity.roles ??= await Role.get(identity, this.discovery.roles)
@@ -170,25 +170,40 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
170
170
  throw new http.TooManyRequests(retry)
171
171
 
172
172
  const [scheme, credentials] = split(authorization)
173
- const provider = providerOf(scheme)
173
+ const candidates = providersOf(scheme)
174
174
 
175
- if (provider === undefined)
175
+ if (candidates === undefined)
176
176
  throw new http.Unauthorized(`Unknown authentication scheme '${scheme}'`)
177
177
 
178
- this.schemes[scheme] ??= await this.discovery[provider]
178
+ let provider: Remote | null = null
179
+ let result: AuthenticationResult | null = null
179
180
 
180
- const result = await this.schemes[scheme].invoke<AuthenticationResult>('authenticate', {
181
- input: {
182
- scheme,
183
- authority,
184
- credentials
185
- }
186
- })
181
+ // the first provider that does not decline has claimed the credentials, and answers
182
+ // for them: a rejection from one that never recognized them is not the rejection
183
+ for (const candidate of candidates) {
184
+ this.components[candidate] ??= await this.discovery[candidate]
185
+
186
+ const answer = await this.components[candidate]!.invoke<AuthenticationResult>('authenticate', {
187
+ input: {
188
+ scheme,
189
+ authority,
190
+ credentials
191
+ }
192
+ })
193
+
194
+ if (declined(answer))
195
+ continue
196
+
197
+ provider = candidate
198
+ result = answer
199
+
200
+ break
201
+ }
187
202
 
188
- if (result instanceof Error) {
189
- const code: string | unknown = (result as unknown as { code: string }).code
203
+ if (result === null || result instanceof Error) {
204
+ const code = result === null ? UNRECOGNIZED : codeOf(result)
190
205
 
191
- if (typeof code === 'string') {
206
+ if (code !== undefined) {
192
207
  console.info('Authentication failed', { code })
193
208
 
194
209
  context.rejection = code
@@ -201,9 +216,10 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
201
216
 
202
217
  const identity = result.identity
203
218
 
204
- if (scheme !== PRIMARY && (await this.banned(identity))) throw new http.Unauthorized()
219
+ if (provider !== PRIMARY && (await this.banned(identity))) throw new http.Unauthorized()
205
220
 
206
221
  identity.scheme = scheme
222
+ identity.provider = provider!
207
223
  identity.refresh = result.refresh
208
224
 
209
225
  return identity
@@ -228,6 +244,17 @@ export class Authorization implements DirectiveFamily<Directive, Extension> {
228
244
  }
229
245
  }
230
246
 
247
+ /** Whether a provider says the credentials are not of its kind, so the next is asked. */
248
+ function declined (result: AuthenticationResult): boolean {
249
+ return result instanceof Error && codeOf(result) === UNRECOGNIZED
250
+ }
251
+
252
+ function codeOf (result: Error): string | undefined {
253
+ const code: string | unknown = (result as unknown as { code: string }).code
254
+
255
+ return typeof code === 'string' ? code : undefined
256
+ }
257
+
231
258
  /** Whether the permissions admit the method on the path the request is routed by. */
232
259
  export function permits (permissions: Record<string, string[]>, method: string, pathname: string): boolean {
233
260
  return Object.entries(permissions).some(([pattern, methods]) =>
@@ -3,14 +3,14 @@ import { console } from 'openspan'
3
3
  import * as http from '../../HTTP/index.js'
4
4
  import { split } from './split.js'
5
5
  import { create } from './create.js'
6
- import { INCEPTION, UNKNOWN, provider as providerOf } from './schemes.js'
6
+ import { INCEPTION, UNKNOWN, providers as providersOf } from './schemes.js'
7
7
  import { Role } from './Role.js'
8
8
  import type { Component } from '@toa.io/core'
9
- import type { Maybe } from '@toa.io/types'
10
- import type { Directive, Discovery, Identity, Context, Schemes, Ban } from './types.js'
9
+ import type { Maybe } from '@toa.io/core'
10
+ import type { Directive, Discovery, Identity, Context, Components, Ban } from './types.js'
11
11
 
12
12
  export class Incept implements Directive {
13
- private static readonly schemes: Schemes = {} as unknown as Schemes
13
+ private static readonly components: Components = {}
14
14
  private static discovery: Discovery
15
15
  private static bans: Component | null = null
16
16
 
@@ -26,12 +26,17 @@ export class Incept implements Directive {
26
26
 
27
27
  public static async incept (context: Context, id: string): Promise<Identity> {
28
28
  const [scheme, credentials] = split(context.request.headers.authorization!)
29
- const provider = providerOf(scheme)
29
+ const candidates = providersOf(scheme)
30
30
 
31
- if (provider === undefined)
31
+ if (candidates === undefined)
32
32
  throw new http.BadRequest('Authentication scheme is not supported')
33
33
 
34
- if (!INCEPTION.includes(provider))
34
+ // a scheme several providers claim is incepted by the one that incepts; `Bearer`
35
+ // carries an access token this gateway issued, and an identity cannot be incepted
36
+ // from a credential it would itself have to exist to hold
37
+ const provider = candidates.find((candidate) => INCEPTION.includes(candidate))
38
+
39
+ if (provider === undefined)
35
40
  throw new http.Unauthorized()
36
41
 
37
42
  Incept.bans ??= await Incept.discovery.bans
@@ -41,9 +46,9 @@ export class Incept implements Directive {
41
46
  if (ban.banned)
42
47
  throw new http.Unauthorized()
43
48
 
44
- Incept.schemes[scheme] ??= await Incept.discovery[provider]
49
+ Incept.components[provider] ??= await Incept.discovery[provider]
45
50
 
46
- const identity = await Incept.schemes[scheme].invoke<Maybe<Identity>>('incept', {
51
+ const identity = await Incept.components[provider]!.invoke<Maybe<Identity>>('incept', {
47
52
  input: {
48
53
  scheme,
49
54
  authority: context.authority,
@@ -1,14 +1,18 @@
1
1
  import { it } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
- import { provider } from './schemes.js'
4
+ import { providers } from './schemes.js'
5
5
 
6
6
  it('should resolve a known scheme', () => {
7
- assert.equal(provider('basic'), 'basic')
8
- assert.equal(provider('bearer'), 'federation')
7
+ assert.deepEqual(providers('basic'), ['basic'])
8
+ assert.deepEqual(providers('code'), ['federation'])
9
+ })
10
+
11
+ it('should resolve every provider claiming a scheme, in order', () => {
12
+ assert.deepEqual(providers('bearer'), ['tokens', 'federation'])
9
13
  })
10
14
 
11
15
  it('should not resolve a property of Object.prototype', () => {
12
16
  for (const name of ['constructor', '__proto__', 'toString', 'hasOwnProperty'])
13
- assert.equal(provider(name), undefined)
17
+ assert.equal(providers(name), undefined)
14
18
  })
@@ -1,24 +1,37 @@
1
1
  import type { Remote, Scheme } from './types.js'
2
2
 
3
- export const PROVIDERS: Record<Scheme, Remote> = {
4
- basic: 'basic',
5
- token: 'tokens',
6
- bearer: 'federation',
7
- code: 'federation',
8
- otp: 'otp'
3
+ /**
4
+ * The providers of a scheme, in the order they are asked. More than one claims `Bearer`:
5
+ * an OpenID `id_token` is federated, and a token this gateway issued is presented that way
6
+ * by an OAuth client. Which of them a credential belongs to is each provider's own to say,
7
+ * so the order here is only which is asked first.
8
+ */
9
+ export const PROVIDERS: Record<Scheme, Remote[]> = {
10
+ basic: ['basic'],
11
+ token: ['tokens'],
12
+ bearer: ['tokens', 'federation'],
13
+ code: ['federation'],
14
+ otp: ['otp']
9
15
  }
10
16
 
11
17
  export const INCEPTION: Remote[] = ['basic', 'federation']
12
- export const PRIMARY: Scheme = 'token'
18
+ export const PRIMARY: Remote = 'tokens'
13
19
 
14
20
  /**
15
- * The provider of a scheme, or nothing for a scheme that is not one — including a name a
21
+ * The providers of a scheme, or nothing for a scheme that is not one — including a name a
16
22
  * plain object answers on its own, like `constructor`.
17
23
  */
18
- export function provider (scheme: string): Remote | undefined {
24
+ export function providers (scheme: string): Remote[] | undefined {
19
25
  return Object.hasOwn(PROVIDERS, scheme) ? PROVIDERS[scheme as Scheme] : undefined
20
26
  }
21
27
 
28
+ /**
29
+ * What a provider answers when the credentials are not of its kind. The next provider of
30
+ * the scheme is asked; a provider that declines has done nothing, so asking is free of
31
+ * effect — which asking one that rejects would not be.
32
+ */
33
+ export const UNRECOGNIZED = 'UNRECOGNIZED'
34
+
22
35
  /**
23
36
  * The rejection of credentials that are verifiable and owned by no identity: the one
24
37
  * inception goes ahead with.