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

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 (243) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/components/identity.basic/events/principal.js +5 -1
  3. package/components/identity.basic/manifest.toa.yaml +11 -1
  4. package/components/identity.basic/operations/authenticate.js +9 -5
  5. package/components/identity.basic/operations/authenticate.js.map +1 -1
  6. package/components/identity.basic/operations/check.js +2 -1
  7. package/components/identity.basic/operations/check.js.map +1 -1
  8. package/components/identity.basic/operations/incept.js +4 -5
  9. package/components/identity.basic/operations/incept.js.map +1 -1
  10. package/components/identity.basic/operations/lib/credentials.d.ts +5 -0
  11. package/components/identity.basic/operations/lib/credentials.js +12 -0
  12. package/components/identity.basic/operations/lib/credentials.js.map +1 -0
  13. package/components/identity.basic/operations/transit.d.ts +2 -0
  14. package/components/identity.basic/operations/transit.js +12 -2
  15. package/components/identity.basic/operations/transit.js.map +1 -1
  16. package/components/identity.basic/operations/types.d.ts +11 -1
  17. package/components/identity.basic/source/authenticate.ts +11 -5
  18. package/components/identity.basic/source/check.ts +2 -1
  19. package/components/identity.basic/source/incept.ts +5 -5
  20. package/components/identity.basic/source/lib/credentials.test.ts +18 -0
  21. package/components/identity.basic/source/lib/credentials.ts +13 -0
  22. package/components/identity.basic/source/transit.ts +16 -4
  23. package/components/identity.basic/source/types.ts +13 -1
  24. package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
  25. package/components/identity.federation/events/principal.js +1 -0
  26. package/components/identity.federation/manifest.toa.yaml +5 -1
  27. package/components/identity.federation/operations/authenticate.js +3 -3
  28. package/components/identity.federation/operations/authenticate.js.map +1 -1
  29. package/components/identity.federation/operations/create.js +5 -1
  30. package/components/identity.federation/operations/create.js.map +1 -1
  31. package/components/identity.federation/operations/lib/decode.js +5 -1
  32. package/components/identity.federation/operations/lib/decode.js.map +1 -1
  33. package/components/identity.federation/operations/lib/exchange.js +3 -3
  34. package/components/identity.federation/operations/lib/exchange.js.map +1 -1
  35. package/components/identity.federation/operations/list.js +2 -1
  36. package/components/identity.federation/operations/list.js.map +1 -1
  37. package/components/identity.federation/operations/types/configuration.d.ts +2 -1
  38. package/components/identity.federation/source/authenticate.ts +3 -3
  39. package/components/identity.federation/source/create.ts +5 -1
  40. package/components/identity.federation/source/lib/decode.ts +5 -1
  41. package/components/identity.federation/source/lib/exchange.ts +3 -3
  42. package/components/identity.federation/source/list.test.ts +1 -1
  43. package/components/identity.federation/source/list.ts +2 -1
  44. package/components/identity.federation/source/types/configuration.ts +2 -1
  45. package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
  46. package/components/identity.keys/manifest.toa.yaml +18 -1
  47. package/components/identity.keys/operations/disable.d.ts +5 -0
  48. package/components/identity.keys/operations/disable.js +5 -0
  49. package/components/identity.keys/operations/disable.js.map +1 -0
  50. package/components/identity.keys/operations/revoke.d.ts +15 -0
  51. package/components/identity.keys/operations/revoke.js +12 -0
  52. package/components/identity.keys/operations/revoke.js.map +1 -0
  53. package/components/identity.keys/receivers/identity.bans.created.js +1 -0
  54. package/components/identity.keys/receivers/identity.bans.updated.js +1 -0
  55. package/components/identity.keys/receivers/identity.roles.deleted.js +1 -0
  56. package/components/identity.keys/source/disable.ts +9 -0
  57. package/components/identity.keys/source/revoke.ts +30 -0
  58. package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
  59. package/components/identity.otp/manifest.toa.yaml +6 -0
  60. package/components/identity.otp/operations/authenticate.js +13 -1
  61. package/components/identity.otp/operations/authenticate.js.map +1 -1
  62. package/components/identity.otp/operations/issue.js +3 -2
  63. package/components/identity.otp/operations/issue.js.map +1 -1
  64. package/components/identity.otp/operations/lib/Context.d.ts +1 -0
  65. package/components/identity.otp/source/authenticate.ts +19 -1
  66. package/components/identity.otp/source/issue.ts +3 -2
  67. package/components/identity.otp/source/lib/Context.ts +1 -0
  68. package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
  69. package/components/identity.passkeys/operations/authenticate.js +2 -1
  70. package/components/identity.passkeys/operations/authenticate.js.map +1 -1
  71. package/components/identity.passkeys/operations/challenge.js +3 -3
  72. package/components/identity.passkeys/operations/challenge.js.map +1 -1
  73. package/components/identity.passkeys/operations/delete.js +2 -1
  74. package/components/identity.passkeys/operations/delete.js.map +1 -1
  75. package/components/identity.passkeys/operations/list.js +2 -1
  76. package/components/identity.passkeys/operations/list.js.map +1 -1
  77. package/components/identity.passkeys/source/authenticate.ts +2 -1
  78. package/components/identity.passkeys/source/challenge.ts +3 -3
  79. package/components/identity.passkeys/source/delete.ts +2 -1
  80. package/components/identity.passkeys/source/list.ts +2 -1
  81. package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
  82. package/components/identity.roles/manifest.toa.yaml +4 -0
  83. package/components/identity.roles/operations/grant.js +5 -2
  84. package/components/identity.roles/operations/grant.js.map +1 -1
  85. package/components/identity.roles/source/grant.ts +6 -2
  86. package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
  87. package/components/identity.tokens/manifest.toa.yaml +3 -0
  88. package/components/identity.tokens/operations/authenticate.js +4 -3
  89. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  90. package/components/identity.tokens/operations/decrypt.js +7 -1
  91. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  92. package/components/identity.tokens/operations/encrypt.js +3 -2
  93. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  94. package/components/identity.tokens/operations/issue.js +2 -1
  95. package/components/identity.tokens/operations/issue.js.map +1 -1
  96. package/components/identity.tokens/operations/lib/types.d.ts +3 -0
  97. package/components/identity.tokens/source/authenticate.test.ts +26 -2
  98. package/components/identity.tokens/source/authenticate.ts +4 -3
  99. package/components/identity.tokens/source/decrypt.test.ts +35 -1
  100. package/components/identity.tokens/source/decrypt.ts +10 -1
  101. package/components/identity.tokens/source/encrypt.test.ts +15 -0
  102. package/components/identity.tokens/source/encrypt.ts +4 -2
  103. package/components/identity.tokens/source/issue.ts +2 -1
  104. package/components/identity.tokens/source/lib/types.ts +4 -0
  105. package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
  106. package/documentation/authorities.md +3 -0
  107. package/documentation/components.md +31 -11
  108. package/documentation/identity.md +38 -6
  109. package/documentation/io.md +2 -2
  110. package/documentation/ip.md +24 -0
  111. package/documentation/passkeys.md +6 -0
  112. package/features/auth.assert.feature +2 -4
  113. package/features/auth.claims.feature +1 -0
  114. package/features/auth.incept.feature +46 -0
  115. package/features/authorities.feature +39 -0
  116. package/features/authorities.federation.feature +1 -0
  117. package/features/identity.bans.feature +63 -0
  118. package/features/identity.basic.feature +95 -3
  119. package/features/identity.feature +151 -0
  120. package/features/identity.federation.feature +5 -0
  121. package/features/identity.otp.feature +41 -0
  122. package/features/identity.roles.feature +252 -0
  123. package/features/identity.tokens.custom.feature +145 -0
  124. package/features/map.feature +2 -0
  125. package/features/steps/Gateway.ts +8 -2
  126. package/features/steps/Realtime.ts +1 -1
  127. package/package.json +5 -5
  128. package/readme.md +3 -0
  129. package/schemas/annotation.cos.yaml +16 -0
  130. package/source/Annotation.ts +14 -0
  131. package/source/Composition.ts +5 -5
  132. package/source/Directive.test.ts +3 -1
  133. package/source/Directive.ts +6 -2
  134. package/source/Endpoint.ts +4 -2
  135. package/source/Factory.ts +12 -9
  136. package/source/HTTP/Context.ts +12 -2
  137. package/source/HTTP/Server.ts +16 -3
  138. package/source/HTTP/address.test.ts +22 -0
  139. package/source/HTTP/address.ts +17 -0
  140. package/source/Query.test.ts +15 -1
  141. package/source/Query.ts +3 -1
  142. package/source/RTD/Directives.ts +10 -1
  143. package/source/Remotes.test.ts +7 -12
  144. package/source/Remotes.ts +5 -5
  145. package/source/const.ts +3 -0
  146. package/source/deployment.ts +7 -1
  147. package/source/directives/auth/Assert.ts +3 -0
  148. package/source/directives/auth/Authorization.test.ts +24 -0
  149. package/source/directives/auth/Authorization.ts +80 -11
  150. package/source/directives/auth/Incept.ts +9 -4
  151. package/source/directives/auth/Role.test.ts +1 -1
  152. package/source/directives/auth/Role.ts +2 -1
  153. package/source/directives/auth/schemes.test.ts +14 -0
  154. package/source/directives/auth/schemes.ts +14 -0
  155. package/source/directives/auth/types.ts +3 -0
  156. package/source/directives/io/IO.ts +10 -3
  157. package/source/directives/io/lib/throttle/Keys.ts +10 -3
  158. package/source/directives/io/lib/throttle/Quotas.test.ts +14 -4
  159. package/source/directives/io/lib/throttle/Quotas.ts +15 -2
  160. package/source/directives/io/lib/throttle/Sync.test.ts +7 -9
  161. package/source/directives/io/lib/throttle/Sync.ts +4 -7
  162. package/source/directives/io/lib/throttle/Warning.test.ts +32 -0
  163. package/source/directives/io/lib/throttle/Warning.ts +30 -0
  164. package/source/directives/io/lib/throttle/components/Component.ts +2 -1
  165. package/source/directives/io/lib/throttle/components/IP.ts +19 -28
  166. package/source/directives/io/lib/throttle/index.ts +1 -0
  167. package/source/redact.test.ts +27 -0
  168. package/source/redact.ts +21 -0
  169. package/source/schemas.test.ts +17 -0
  170. package/transpiled/Annotation.d.ts +10 -0
  171. package/transpiled/Composition.d.ts +3 -3
  172. package/transpiled/Composition.js +4 -4
  173. package/transpiled/Composition.js.map +1 -1
  174. package/transpiled/Directive.d.ts +3 -2
  175. package/transpiled/Directive.js +3 -1
  176. package/transpiled/Directive.js.map +1 -1
  177. package/transpiled/Endpoint.js +3 -2
  178. package/transpiled/Endpoint.js.map +1 -1
  179. package/transpiled/Factory.d.ts +3 -3
  180. package/transpiled/Factory.js +11 -8
  181. package/transpiled/Factory.js.map +1 -1
  182. package/transpiled/HTTP/Context.d.ts +4 -0
  183. package/transpiled/HTTP/Context.js +7 -2
  184. package/transpiled/HTTP/Context.js.map +1 -1
  185. package/transpiled/HTTP/Server.d.ts +5 -0
  186. package/transpiled/HTTP/Server.js +8 -3
  187. package/transpiled/HTTP/Server.js.map +1 -1
  188. package/transpiled/HTTP/address.d.ts +7 -0
  189. package/transpiled/HTTP/address.js +14 -0
  190. package/transpiled/HTTP/address.js.map +1 -0
  191. package/transpiled/Query.js +3 -1
  192. package/transpiled/Query.js.map +1 -1
  193. package/transpiled/RTD/Directives.d.ts +9 -1
  194. package/transpiled/Remotes.d.ts +3 -3
  195. package/transpiled/Remotes.js +4 -4
  196. package/transpiled/Remotes.js.map +1 -1
  197. package/transpiled/const.d.ts +2 -0
  198. package/transpiled/const.js +2 -0
  199. package/transpiled/const.js.map +1 -1
  200. package/transpiled/deployment.js +4 -0
  201. package/transpiled/deployment.js.map +1 -1
  202. package/transpiled/directives/auth/Assert.js +2 -0
  203. package/transpiled/directives/auth/Assert.js.map +1 -1
  204. package/transpiled/directives/auth/Authorization.d.ts +8 -0
  205. package/transpiled/directives/auth/Authorization.js +61 -11
  206. package/transpiled/directives/auth/Authorization.js.map +1 -1
  207. package/transpiled/directives/auth/Incept.d.ts +3 -1
  208. package/transpiled/directives/auth/Incept.js +8 -4
  209. package/transpiled/directives/auth/Incept.js.map +1 -1
  210. package/transpiled/directives/auth/Role.js +2 -1
  211. package/transpiled/directives/auth/Role.js.map +1 -1
  212. package/transpiled/directives/auth/schemes.d.ts +10 -0
  213. package/transpiled/directives/auth/schemes.js +12 -0
  214. package/transpiled/directives/auth/schemes.js.map +1 -1
  215. package/transpiled/directives/auth/types.d.ts +2 -0
  216. package/transpiled/directives/io/IO.d.ts +4 -1
  217. package/transpiled/directives/io/IO.js +7 -3
  218. package/transpiled/directives/io/IO.js.map +1 -1
  219. package/transpiled/directives/io/lib/throttle/Keys.d.ts +2 -1
  220. package/transpiled/directives/io/lib/throttle/Keys.js +7 -2
  221. package/transpiled/directives/io/lib/throttle/Keys.js.map +1 -1
  222. package/transpiled/directives/io/lib/throttle/Quotas.d.ts +3 -2
  223. package/transpiled/directives/io/lib/throttle/Quotas.js +8 -1
  224. package/transpiled/directives/io/lib/throttle/Quotas.js.map +1 -1
  225. package/transpiled/directives/io/lib/throttle/Sync.d.ts +2 -3
  226. package/transpiled/directives/io/lib/throttle/Sync.js +1 -3
  227. package/transpiled/directives/io/lib/throttle/Sync.js.map +1 -1
  228. package/transpiled/directives/io/lib/throttle/Warning.d.ts +11 -0
  229. package/transpiled/directives/io/lib/throttle/Warning.js +24 -0
  230. package/transpiled/directives/io/lib/throttle/Warning.js.map +1 -0
  231. package/transpiled/directives/io/lib/throttle/components/Component.d.ts +2 -1
  232. package/transpiled/directives/io/lib/throttle/components/IP.d.ts +10 -2
  233. package/transpiled/directives/io/lib/throttle/components/IP.js +18 -23
  234. package/transpiled/directives/io/lib/throttle/components/IP.js.map +1 -1
  235. package/transpiled/directives/io/lib/throttle/index.d.ts +1 -0
  236. package/transpiled/directives/io/lib/throttle/index.js +1 -0
  237. package/transpiled/directives/io/lib/throttle/index.js.map +1 -1
  238. package/transpiled/redact.d.ts +5 -0
  239. package/transpiled/redact.js +17 -0
  240. package/transpiled/redact.js.map +1 -0
  241. package/tsconfig.tsbuildinfo +1 -1
  242. package/components/exposition.atom/manifest.toa.yaml +0 -21
  243. package/components/exposition.atom/operations/meter.js +0 -5
package/CHANGELOG.md CHANGED
@@ -3,6 +3,37 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.282](https://github.com/toa-io/toa/compare/v1.0.0-alpha.281...v1.0.0-alpha.282) (2026-09-03)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **exposition:** bouncer requires ip ([d470494](https://github.com/toa-io/toa/commit/d470494fdb42372a434d945205e93e27bca8880b))
11
+ * **exposition:** bound OTP guessing ([44b5a6a](https://github.com/toa-io/toa/commit/44b5a6a1f743cca4bd80b0b7e9f9691af83fb2ab))
12
+ * **exposition:** do not delegate the right to delegate ([cf1f83f](https://github.com/toa-io/toa/commit/cf1f83f6bb1051b8b399c632bb81f08a2fd9fb14))
13
+ * **exposition:** keep credentials out of the logs ([17a5529](https://github.com/toa-io/toa/commit/17a55296ec530b6f3c3a1d4e746499e52ef1c126))
14
+ * **exposition:** leave custom tokens unrefreshed ([ebec3d5](https://github.com/toa-io/toa/commit/ebec3d5ea421e8c780f7c7afd781c588851ef8fa))
15
+ * **exposition:** match token permissions on the routed path ([6c888f5](https://github.com/toa-io/toa/commit/6c888f5e51a379c0cdf46e8e3b3b0fa406328de6))
16
+ * **exposition:** name the client address header in the authentication annotation ([433ca3a](https://github.com/toa-io/toa/commit/433ca3ae19bc2c68fa26f6205e559ea7e652fcb9))
17
+ * **exposition:** never key on the connection address ([ad719af](https://github.com/toa-io/toa/commit/ad719afb89455a9cbdb97f144928b233aababb73))
18
+ * **exposition:** quote criteria values built from requests ([af3bf79](https://github.com/toa-io/toa/commit/af3bf794d8668a45a4390ad16ffa04aa4fe078e8))
19
+ * **exposition:** read roles again when a token is refreshed ([96c59ca](https://github.com/toa-io/toa/commit/96c59cae39a1b023e6b2b05720ea0682d4c746ad))
20
+ * **exposition:** read the client address from a header the deployment names ([216c2d8](https://github.com/toa-io/toa/commit/216c2d8f21bdb67c7d4387708c80f806f1c72360))
21
+ * **exposition:** refuse rejected credentials before inception ([48e76a8](https://github.com/toa-io/toa/commit/48e76a8f421b95492d256b5c7a9ca61f71f0d7fd))
22
+ * **exposition:** require the audience of a trusted issuer ([0d13604](https://github.com/toa-io/toa/commit/0d13604e3707ed2fcac01500beb27d992314c549))
23
+ * **exposition:** resolve an authentication scheme by its own name only ([ae116d1](https://github.com/toa-io/toa/commit/ae116d1813fc1e5a42b5263daedb752703eb08f0))
24
+ * **exposition:** resolve the client address once, into the request context ([2f36347](https://github.com/toa-io/toa/commit/2f36347d948bab0c421ff63105312549fffc660a))
25
+ * **exposition:** revoke custom tokens through their keys ([359d882](https://github.com/toa-io/toa/commit/359d8825315fce010ea8ac3700d2778b48362f72))
26
+ * **exposition:** revoke custom tokens when a role is revoked ([191a0c6](https://github.com/toa-io/toa/commit/191a0c603ef53dcab5ccd005dd7296d9c9bfaa0b))
27
+ * **exposition:** scope the principal to an authority ([98a0d36](https://github.com/toa-io/toa/commit/98a0d36267523eb1a1aa58742744cd39a21e94e9))
28
+ * **exposition:** split Basic credentials at the first colon ([596e5dc](https://github.com/toa-io/toa/commit/596e5dc2e6e589007dc7914374e1592ecc71c997))
29
+ * **exposition:** validate the request authority ([68686c9](https://github.com/toa-io/toa/commit/68686c9340effb148532607caa1abbaaa5dc0519))
30
+
31
+ ### Features
32
+
33
+ * **core:** a Host for extensions ([6315eac](https://github.com/toa-io/toa/commit/6315eac1e9fb05be472272de06385c54ad5934ce))
34
+ * **exposition:** meter failed authentications per address ([98b07d3](https://github.com/toa-io/toa/commit/98b07d307815ee4a2a0f30f189cbcfc9d78a4d62))
35
+
36
+
6
37
  # [1.0.0-alpha.278](https://github.com/toa-io/toa/compare/v1.0.0-alpha.277...v1.0.0-alpha.278) (2026-09-03)
7
38
 
8
39
  **Note:** Version bump only for package @toa.io/extensions.exposition
@@ -1,5 +1,9 @@
1
1
  export const condition = function (event, context) {
2
- return event.state.username === context.configuration.principal
2
+ const principal = context.configuration.principal
3
+
4
+ return principal !== undefined &&
5
+ event.state.authority === principal.authority &&
6
+ event.state.username === principal.username
3
7
  }
4
8
 
5
9
  export const payload = function (event) {
@@ -172,7 +172,17 @@ configuration:
172
172
  description: Hashing pepper, a secret
173
173
  type: string
174
174
  principal:
175
- type: string
175
+ description: Basic credentials whose Identity is granted the `system` role
176
+ type: object
177
+ properties:
178
+ authority:
179
+ type: string
180
+ username:
181
+ type: string
182
+ required:
183
+ - authority
184
+ - username
185
+ additionalProperties: false
176
186
  username:
177
187
  type: array
178
188
  items:
@@ -1,10 +1,14 @@
1
1
  import { compare } from 'bcryptjs';
2
+ import { quote } from '@toa.io/generic';
3
+ import { split } from './lib/credentials.js';
2
4
  export async function computation(input, context) {
3
- const [username, password] = Buffer
4
- .from(input.credentials, 'base64')
5
- .toString()
6
- .split(':');
7
- const query = { criteria: `authority==${input.authority};username=='${username}'` };
5
+ const pair = split(input.credentials);
6
+ if (pair === null)
7
+ return ERR_NOT_FOUND;
8
+ const [username, password] = pair;
9
+ const query = {
10
+ criteria: `authority==${quote(input.authority)};username==${quote(username)}`
11
+ };
8
12
  const credentials = await context.local.observe({ query });
9
13
  if (credentials instanceof Error)
10
14
  return credentials;
@@ -1 +1 @@
1
- {"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../source/authenticate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAIlC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAE,KAAY,EAAE,OAAgB;IAC/D,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM;SAChC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC;SACjC,QAAQ,EAAE;SACV,KAAK,CAAC,GAAG,CAAC,CAAA;IAEb,MAAM,KAAK,GAAU,EAAE,QAAQ,EAAE,cAAc,KAAK,CAAC,SAAS,eAAe,QAAQ,GAAG,EAAE,CAAA;IAC1F,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAE1D,IAAI,WAAW,YAAY,KAAK;QAC9B,OAAO,WAAW,CAAA;IAEpB,IAAI,WAAW,KAAK,IAAI;QACtB,OAAO,aAAa,CAAA;IAEtB,MAAM,KAAK,GAAG,QAAQ,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IAExD,IAAI,KAAK;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,CAAA;;QAE3C,OAAO,qBAAqB,CAAA;AAChC,CAAC;AAED,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,aAAc,SAAQ,KAAK;IAC1C,IAAI,GAAG,WAAW,CAAA;CACnC,CAAC,EAAE,CAAA;AAEJ,MAAM,qBAAqB,GAAG,IAAI,CAAC,MAAM,qBAAsB,SAAQ,KAAK;IAC1D,IAAI,GAAG,mBAAmB,CAAA;CAC3C,CAAC,EAAE,CAAA"}
1
+ {"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../source/authenticate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAG5C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAE,KAAY,EAAE,OAAgB;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAErC,IAAI,IAAI,KAAK,IAAI;QACf,OAAO,aAAa,CAAA;IAEtB,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAA;IAEjC,MAAM,KAAK,GAAU;QACnB,QAAQ,EAAE,cAAc,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,KAAK,CAAC,QAAQ,CAAC,EAAE;KAC9E,CAAA;IACD,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IAE1D,IAAI,WAAW,YAAY,KAAK;QAC9B,OAAO,WAAW,CAAA;IAEpB,IAAI,WAAW,KAAK,IAAI;QACtB,OAAO,aAAa,CAAA;IAEtB,MAAM,KAAK,GAAG,QAAQ,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACvE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IAExD,IAAI,KAAK;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,CAAA;;QAE3C,OAAO,qBAAqB,CAAA;AAChC,CAAC;AAED,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,aAAc,SAAQ,KAAK;IAC1C,IAAI,GAAG,WAAW,CAAA;CACnC,CAAC,EAAE,CAAA;AAEJ,MAAM,qBAAqB,GAAG,IAAI,CAAC,MAAM,qBAAsB,SAAQ,KAAK;IAC1D,IAAI,GAAG,mBAAmB,CAAA;CAC3C,CAAC,EAAE,CAAA"}
@@ -1,7 +1,8 @@
1
+ import { quote } from '@toa.io/generic';
1
2
  export async function computation(input, context) {
2
3
  const username = Buffer.from(input.username, 'base64url').toString();
3
4
  const found = await context.local.observe({
4
- query: { criteria: `authority==${input.authority};username=='${username}'` }
5
+ query: { criteria: `authority==${quote(input.authority)};username==${quote(username)}` }
5
6
  });
6
7
  if (found instanceof Error)
7
8
  return found;
@@ -1 +1 @@
1
- {"version":3,"file":"check.js","sourceRoot":"","sources":["../source/check.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,WAAW,CAAE,KAAY,EAAE,OAAgB;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAA;IAEpE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;QACxC,KAAK,EAAE,EAAE,QAAQ,EAAE,cAAc,KAAK,CAAC,SAAS,eAAe,QAAQ,GAAG,EAAE;KAC7E,CAAC,CAAA;IAEF,IAAI,KAAK,YAAY,KAAK;QACxB,OAAO,KAAK,CAAA;IAEd,IAAI,KAAK,KAAK,IAAI;QAChB,OAAO,IAAI,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../source/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAGvC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAE,KAAY,EAAE,OAAgB;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAA;IAEpE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;QACxC,KAAK,EAAE,EAAE,QAAQ,EAAE,cAAc,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE;KACzF,CAAC,CAAA;IAEF,IAAI,KAAK,YAAY,KAAK;QACxB,OAAO,KAAK,CAAA;IAEd,IAAI,KAAK,KAAK,IAAI;QAChB,OAAO,IAAI,CAAA;AACf,CAAC"}
@@ -1,10 +1,9 @@
1
+ import { split } from './lib/credentials.js';
1
2
  export async function effect(input, context) {
2
- const [username, password] = Buffer
3
- .from(input.credentials, 'base64')
4
- .toString()
5
- .split(':');
6
- if (typeof password !== 'string')
3
+ const pair = split(input.credentials);
4
+ if (pair === null)
7
5
  return INVALID_CREDENTIALS;
6
+ const [username, password] = pair;
8
7
  const request = {
9
8
  input: {
10
9
  authority: input.authority,
@@ -1 +1 @@
1
- {"version":3,"file":"incept.js","sourceRoot":"","sources":["../source/incept.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,MAAM,CAAE,KAAY,EAAE,OAAgB;IAC1D,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM;SAChC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC;SACjC,QAAQ,EAAE;SACV,KAAK,CAAC,GAAG,CAAC,CAAA;IAEb,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAC9B,OAAO,mBAAmB,CAAA;IAE5B,MAAM,OAAO,GAAG;QACd,KAAK,EAAE;YACL,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ;YACR,QAAQ;SACT;QACD,KAAK,EAAE;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;SACb;KACF,CAAA;IAED,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,uBAAwB,SAAQ,KAAK;IAC1D,IAAI,GAAG,qBAAqB,CAAA;CAC7C,CAAC,EAAE,CAAA"}
1
+ {"version":3,"file":"incept.js","sourceRoot":"","sources":["../source/incept.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAI5C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAE,KAAY,EAAE,OAAgB;IAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAErC,IAAI,IAAI,KAAK,IAAI;QACf,OAAO,mBAAmB,CAAA;IAE5B,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAA;IAEjC,MAAM,OAAO,GAAG;QACd,KAAK,EAAE;YACL,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ;YACR,QAAQ;SACT;QACD,KAAK,EAAE;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;SACb;KACF,CAAA;IAED,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAC7C,CAAC;AAED,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,uBAAwB,SAAQ,KAAK;IAC1D,IAAI,GAAG,qBAAqB,CAAA;CAC7C,CAAC,EAAE,CAAA"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The user-id and the password of Basic credentials. RFC 7617 forbids a colon in the
3
+ * user-id and allows any number of them in the password, so the split is at the first one.
4
+ */
5
+ export declare function split(credentials: string): [string, string] | null;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The user-id and the password of Basic credentials. RFC 7617 forbids a colon in the
3
+ * user-id and allows any number of them in the password, so the split is at the first one.
4
+ */
5
+ export function split(credentials) {
6
+ const decoded = Buffer.from(credentials, 'base64').toString();
7
+ const colon = decoded.indexOf(':');
8
+ if (colon === -1)
9
+ return null;
10
+ return [decoded.slice(0, colon), decoded.slice(colon + 1)];
11
+ }
12
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../source/lib/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAE,WAAmB;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAElC,IAAI,KAAK,KAAK,CAAC,CAAC;QACd,OAAO,IAAI,CAAA;IAEb,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAA;AAC5D,CAAC"}
@@ -5,8 +5,10 @@ export declare class Transition implements Operation {
5
5
  private pepper;
6
6
  private principal?;
7
7
  private tokens;
8
+ private keys;
8
9
  private usernameRx;
9
10
  private passwordRx;
10
11
  mount(context: Context): void;
12
+ private locked;
11
13
  execute(input: TransitInput, object: Entity): Promise<Maybe<IdOutput>>;
12
14
  }
@@ -4,6 +4,7 @@ export class Transition {
4
4
  pepper = '';
5
5
  principal;
6
6
  tokens = undefined;
7
+ keys = undefined;
7
8
  usernameRx = [];
8
9
  passwordRx = [];
9
10
  mount(context) {
@@ -11,21 +12,30 @@ export class Transition {
11
12
  this.pepper = context.configuration.pepper?.unwrap() ?? '';
12
13
  this.principal = context.configuration.principal;
13
14
  this.tokens = context.remote.identity.tokens;
15
+ this.keys = context.remote.identity.keys;
14
16
  this.usernameRx = toRx(context.configuration.username);
15
17
  this.passwordRx = toRx(context.configuration.password);
16
18
  }
19
+ locked(object) {
20
+ return this.principal !== undefined &&
21
+ object.authority === this.principal.authority &&
22
+ object.username === this.principal.username;
23
+ }
17
24
  async execute(input, object) {
18
25
  const deleted = object._deleted !== undefined && object._deleted !== null;
19
26
  const existent = object._version !== 0 && !deleted;
20
27
  if (existent) {
21
28
  if (input.inception === true)
22
29
  return ERR_EXISTS;
23
- await this.tokens.revoke({ query: { id: object.id } });
30
+ await Promise.all([
31
+ this.tokens.revoke({ query: { id: object.id } }),
32
+ this.keys.revoke({ input: { identity: object.id } })
33
+ ]);
24
34
  }
25
35
  else
26
36
  object.authority = input.authority;
27
37
  if (input.username !== undefined) {
28
- if (existent && object.username === this.principal)
38
+ if (existent && this.locked(object))
29
39
  return ERR_PRINCIPAL_LOCKED;
30
40
  if (invalid(input.username, this.usernameRx))
31
41
  return ERR_INVALID_USERNAME;
@@ -1 +1 @@
1
- {"version":3,"file":"transit.js","sourceRoot":"","sources":["../source/transit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transit.js","sourceRoot":"","sources":["../source/transit.ts"],"names":[],"mappings":"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"}
@@ -11,16 +11,23 @@ export interface Context {
11
11
  tokens: {
12
12
  revoke: Call<void, IdentityTokensRevokeInput>;
13
13
  };
14
+ keys: {
15
+ revoke: Call<void, IdentityKeysRevokeInput>;
16
+ };
14
17
  };
15
18
  };
16
19
  configuration: {
17
20
  readonly rounds: number;
18
21
  readonly pepper?: Secret;
19
- readonly principal?: string;
22
+ readonly principal?: Principal;
20
23
  readonly username: string[];
21
24
  readonly password: string[];
22
25
  };
23
26
  }
27
+ export interface Principal {
28
+ authority: string;
29
+ username: string;
30
+ }
24
31
  export interface Entity {
25
32
  id: string;
26
33
  authority: string;
@@ -48,4 +55,7 @@ export interface IdOutput {
48
55
  interface IdentityTokensRevokeInput {
49
56
  query: Query;
50
57
  }
58
+ interface IdentityKeysRevokeInput {
59
+ identity: string;
60
+ }
51
61
  export {};
@@ -1,14 +1,20 @@
1
1
  import { compare } from 'bcryptjs'
2
+ import { quote } from '@toa.io/generic'
2
3
  import { type Query, type Maybe } from '@toa.io/types'
4
+ import { split } from './lib/credentials.js'
3
5
  import { type Context } from './types.js'
4
6
 
5
7
  export async function computation (input: Input, context: Context): Promise<Maybe<Output>> {
6
- const [username, password] = Buffer
7
- .from(input.credentials, 'base64')
8
- .toString()
9
- .split(':')
8
+ const pair = split(input.credentials)
10
9
 
11
- const query: Query = { criteria: `authority==${input.authority};username=='${username}'` }
10
+ if (pair === null)
11
+ return ERR_NOT_FOUND
12
+
13
+ const [username, password] = pair
14
+
15
+ const query: Query = {
16
+ criteria: `authority==${quote(input.authority)};username==${quote(username)}`
17
+ }
12
18
  const credentials = await context.local.observe({ query })
13
19
 
14
20
  if (credentials instanceof Error)
@@ -1,10 +1,11 @@
1
+ import { quote } from '@toa.io/generic'
1
2
  import type { Context } from './types.js'
2
3
 
3
4
  export async function computation (input: Input, context: Context): Promise<void | null | Error> {
4
5
  const username = Buffer.from(input.username, 'base64url').toString()
5
6
 
6
7
  const found = await context.local.observe({
7
- query: { criteria: `authority==${input.authority};username=='${username}'` }
8
+ query: { criteria: `authority==${quote(input.authority)};username==${quote(username)}` }
8
9
  })
9
10
 
10
11
  if (found instanceof Error)
@@ -1,15 +1,15 @@
1
+ import { split } from './lib/credentials.js'
1
2
  import type { Context } from './types.js'
2
3
  import type { Maybe } from '@toa.io/types'
3
4
 
4
5
  export async function effect (input: Input, context: Context): Promise<Maybe<Output>> {
5
- const [username, password] = Buffer
6
- .from(input.credentials, 'base64')
7
- .toString()
8
- .split(':')
6
+ const pair = split(input.credentials)
9
7
 
10
- if (typeof password !== 'string')
8
+ if (pair === null)
11
9
  return INVALID_CREDENTIALS
12
10
 
11
+ const [username, password] = pair
12
+
13
13
  const request = {
14
14
  input: {
15
15
  authority: input.authority,
@@ -0,0 +1,18 @@
1
+ import { it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { split } from './credentials.js'
5
+
6
+ const encode = (value: string): string => Buffer.from(value).toString('base64')
7
+
8
+ it('should split at the first colon', () => {
9
+ assert.deepEqual(split(encode('user:pa:ss:word')), ['user', 'pa:ss:word'])
10
+ })
11
+
12
+ it('should accept an empty password', () => {
13
+ assert.deepEqual(split(encode('user:')), ['user', ''])
14
+ })
15
+
16
+ it('should refuse credentials without a colon', () => {
17
+ assert.equal(split(encode('user')), null)
18
+ })
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The user-id and the password of Basic credentials. RFC 7617 forbids a colon in the
3
+ * user-id and allows any number of them in the password, so the split is at the first one.
4
+ */
5
+ export function split (credentials: string): [string, string] | null {
6
+ const decoded = Buffer.from(credentials, 'base64').toString()
7
+ const colon = decoded.indexOf(':')
8
+
9
+ if (colon === -1)
10
+ return null
11
+
12
+ return [decoded.slice(0, colon), decoded.slice(colon + 1)]
13
+ }
@@ -1,12 +1,13 @@
1
1
  import { genSalt, hash } from 'bcryptjs'
2
2
  import type { Maybe, Operation } from '@toa.io/types'
3
- import type { Context, Entity, TransitInput, IdOutput } from './types.js'
3
+ import type { Context, Entity, Principal, TransitInput, IdOutput } from './types.js'
4
4
 
5
5
  export class Transition implements Operation {
6
6
  private rounds: number = 10
7
7
  private pepper: string = ''
8
- private principal?: string
8
+ private principal?: Principal
9
9
  private tokens: Tokens = undefined as unknown as Tokens
10
+ private keys: Keys = undefined as unknown as Keys
10
11
  private usernameRx: RegExp[] = []
11
12
  private passwordRx: RegExp[] = []
12
13
 
@@ -15,11 +16,18 @@ export class Transition implements Operation {
15
16
  this.pepper = context.configuration.pepper?.unwrap() ?? ''
16
17
  this.principal = context.configuration.principal
17
18
  this.tokens = context.remote.identity.tokens
19
+ this.keys = context.remote.identity.keys
18
20
 
19
21
  this.usernameRx = toRx(context.configuration.username)
20
22
  this.passwordRx = toRx(context.configuration.password)
21
23
  }
22
24
 
25
+ private locked (object: Entity): boolean {
26
+ return this.principal !== undefined &&
27
+ object.authority === this.principal.authority &&
28
+ object.username === this.principal.username
29
+ }
30
+
23
31
  public async execute (input: TransitInput, object: Entity): Promise<Maybe<IdOutput>> {
24
32
  const deleted = object._deleted !== undefined && object._deleted !== null
25
33
  const existent = object._version !== 0 && !deleted
@@ -28,12 +36,15 @@ export class Transition implements Operation {
28
36
  if (input.inception === true)
29
37
  return ERR_EXISTS
30
38
 
31
- await this.tokens.revoke({ query: { id: object.id } })
39
+ await Promise.all([
40
+ this.tokens.revoke({ query: { id: object.id } }),
41
+ this.keys.revoke({ input: { identity: object.id } })
42
+ ])
32
43
  } else
33
44
  object.authority = input.authority
34
45
 
35
46
  if (input.username !== undefined) {
36
- if (existent && object.username === this.principal)
47
+ if (existent && this.locked(object))
37
48
  return ERR_PRINCIPAL_LOCKED
38
49
 
39
50
  if (invalid(input.username, this.usernameRx))
@@ -85,3 +96,4 @@ const ERR_EXISTS = new (class ExistsError extends Error {
85
96
  })()
86
97
 
87
98
  type Tokens = Context['remote']['identity']['tokens']
99
+ type Keys = Context['remote']['identity']['keys']
@@ -12,17 +12,25 @@ export interface Context {
12
12
  tokens: {
13
13
  revoke: Call<void, IdentityTokensRevokeInput>
14
14
  }
15
+ keys: {
16
+ revoke: Call<void, IdentityKeysRevokeInput>
17
+ }
15
18
  }
16
19
  }
17
20
  configuration: {
18
21
  readonly rounds: number
19
22
  readonly pepper?: Secret
20
- readonly principal?: string
23
+ readonly principal?: Principal
21
24
  readonly username: string[]
22
25
  readonly password: string[]
23
26
  }
24
27
  }
25
28
 
29
+ export interface Principal {
30
+ authority: string
31
+ username: string
32
+ }
33
+
26
34
  export interface Entity {
27
35
  id: string
28
36
  authority: string
@@ -55,3 +63,7 @@ export interface IdOutput {
55
63
  interface IdentityTokensRevokeInput {
56
64
  query: Query
57
65
  }
66
+
67
+ interface IdentityKeysRevokeInput {
68
+ identity: string
69
+ }