@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
@@ -54,10 +54,33 @@ The `Token` is the **primary** authentication scheme.
54
54
  If request originators use an alternative authentication scheme, they will receive a response
55
55
  containing `Token` credentials and will be required to switch to the `Token` scheme for any
56
56
  subsequent requests.
57
- Continued use of other authentication schemes will result in temporary blocking of requests.
58
57
 
59
58
  See [`identity.tokens` component](components.md#local-tokens).
60
59
 
60
+ ### Failed authentications
61
+
62
+ Credentials that are rejected can be metered per client address, whatever the scheme. An address
63
+ may fail `attempts` at once and earns them back at `attempts` per `interval` seconds; a request
64
+ carrying credentials from an address with nothing left is answered `429 Too Many Requests` with
65
+ `Retry-After`. Nothing is metered unless the exposition annotation carries `bouncer`;
66
+ `attempts` and `interval` default to 20 and 60. The address is the one the request context
67
+ resolved, see [Client address](ip.md): `bouncer` requires `ip`, in the same environment, and a
68
+ request without an address is not metered.
69
+
70
+ ```yaml
71
+ # context.toa.yaml
72
+
73
+ exposition:
74
+ ip: cf-connecting-ip
75
+ bouncer:
76
+ attempts: 20
77
+ interval: 60
78
+ ```
79
+
80
+ The count is shared by the gateway replicas through
81
+ [atomicity](../../../connectors/atomicity/readme.md) and decided in each process; nothing is read on
82
+ the request path.
83
+
61
84
  ### Bearer scheme
62
85
 
63
86
  OpenID tokens issued by trusted providers.
@@ -85,13 +108,15 @@ configuration:
85
108
  aud: <APPLE_CLIENT_ID>
86
109
  secret: <APPLE_CLIENT_SECRET> # enables Authorization Code Flow
87
110
  principal:
111
+ authority: example
88
112
  iss: https://accounts.google.com
89
113
  sub: 4218230498234
90
114
  assert: true
91
115
  ```
92
116
 
93
- `principal` specifies the values of the `iss` and `sub` claims of an Identity that will be granted
94
- with a `system` role.
117
+ `principal` specifies the authority and the values of the `iss` and `sub` claims of an Identity
118
+ that will be granted with a `system` role. The same subject in another authority is ordinary
119
+ credentials.
95
120
 
96
121
  `assert` indicates whether the Identity should be implicitly created when valid credentials for a
97
122
  non-existent Identity are provided (default `true`).
@@ -113,8 +138,8 @@ iss: code issuer
113
138
  for: redirect URI
114
139
  ```
115
140
 
116
- Trust configuration for the issuer requires `aud` and either `secret` or `signature`
117
- values to enable the Authorization Code Flow.
141
+ Trust configuration for the issuer requires either `secret` or `signature`
142
+ to enable the Authorization Code Flow.
118
143
 
119
144
  > If `aud` is an array, the first value is used.
120
145
 
@@ -159,13 +184,15 @@ GET /identity/ HTTP/1.1
159
184
  authentication: OTP dXNlcm5hbWU6MTIzNDU2
160
185
  ```
161
186
 
162
- OTP expiration time can be configured using the `identity.otp` configuration.
187
+ OTP expiration time and the number of failed attempts a username is allowed within it can be
188
+ configured using the `identity.otp` configuration.
163
189
 
164
190
  ```yaml
165
191
  # context.toa.yaml
166
192
  configuration:
167
193
  identity.otp:
168
194
  lifetime: 60 # seconds
195
+ attempts: 5
169
196
  ```
170
197
 
171
198
  ## Identity inception
@@ -223,6 +250,11 @@ Identity `2428c31ecb6e4a51a24ef52f0c4181b9` are created.
223
250
 
224
251
  Inception is supported for `Basic` and `Bearer` authentication schemes.
225
252
 
253
+ Inception goes ahead when the request carries no credentials, or credentials the scheme verified
254
+ and no Identity owns, which its `authenticate` operation reports as `NOT_FOUND`. Credentials
255
+ rejected for any other reason, a wrong password for one, are answered with `401 Unauthorized`
256
+ before the endpoint is called.
257
+
226
258
  ## Identity assertion
227
259
 
228
260
  `auth:assert` directive is used to ensure that given credentials are associated with an existing
@@ -97,8 +97,8 @@ and reports what it could not the next time it gets through.
97
97
  What a request is metered against. Give one, or a list — a list keys on the combination, so
98
98
  `[route, ip]` meters each address separately on each route.
99
99
 
100
- - `ip` — the client address, read from `X-Forwarded-For` where it is set and not private,
101
- and from the connection otherwise.
100
+ - `ip` — the client address, as the request context resolved it. See [Client address](ip.md) for
101
+ where it is read from and why.
102
102
  - `path` — the path the request came in on, `/users/1`.
103
103
  - `route` — the route as declared, `/users/:id`. Every path matching the route shares one budget,
104
104
  which `path` cannot do: keyed on `path`, walking ids is a way around the limit.
@@ -0,0 +1,24 @@
1
+ # Client address
2
+
3
+ The address a request comes from is resolved once, into the request context, and read from there
4
+ by whatever keys on it: the meter of [failed authentications](identity.md#failed-authentications)
5
+ and the `ip` key of [`io:throttle`](io.md#key-components).
6
+
7
+ ```yaml
8
+ # context.toa.yaml
9
+
10
+ exposition:
11
+ ip: cf-connecting-ip
12
+ ```
13
+
14
+ `ip` names the header the address is read from. In production the gateway sits behind a load
15
+ balancer, an ingress or a CDN: the connection it accepts comes from that machine, so every client
16
+ shares one address and nothing tells them apart. The edge is the one that saw the client connect,
17
+ and it writes the client's address into a header of its own, `CF-Connecting-IP`, `X-Real-IP` or
18
+ `X-Forwarded-For`, depending on the infrastructure. A client may send any of these headers too, so
19
+ the gateway trusts none of them on its own: the deployment names the one its edge overwrites, and
20
+ where the header holds a list, the last value is the one the edge appended.
21
+
22
+ The connection's address is never used. Without `ip`, or for a request that does not carry the
23
+ header, the request has no address: the bouncer and a throttle keyed on `ip` let it through and
24
+ say so in the log, once per hundred seconds rather than once per request.
@@ -3,3 +3,9 @@
3
3
  - [Standard](https://www.w3.org/TR/webauthn-2/)
4
4
  - [Features](../features/passkeys.feature)
5
5
  - [Credential management](credentials.md#passkeys)
6
+
7
+ ## Origin
8
+
9
+ The relying party is the client. The origin a passkey is registered under, and the RP ID derived
10
+ from it, come from the request, so a passkey belongs to the origin of the client that registered
11
+ it, and a client on another origin does not see it.
@@ -41,7 +41,7 @@ Feature: Identity assertion
41
41
  authorization: Token ${{ user.token }}
42
42
  """
43
43
 
44
- # invalid credentials
44
+ # wrong password for the credentials just created
45
45
  When the following request is received:
46
46
  """
47
47
  GET /echo/ HTTP/1.1
@@ -51,7 +51,5 @@ Feature: Identity assertion
51
51
  """
52
52
  Then the following reply is sent:
53
53
  """
54
- 422 Unprocessable Entity
55
-
56
- code: INVALID_PASSWORD
54
+ 401 Unauthorized
57
55
  """
@@ -9,6 +9,7 @@ Feature: Federated identity authentication
9
9
  """yaml
10
10
  trust:
11
11
  - iss: http://localhost:44444
12
+ aud: test
12
13
  """
13
14
 
14
15
  Scenario: Full claim
@@ -118,3 +118,49 @@ Feature: Identity inception
118
118
  id: ${{ identity.id }}
119
119
  foo: bar
120
120
  """
121
+
122
+ Scenario: Inception with a wrong password for existing credentials
123
+ Given the `identity.basic` database contains:
124
+ | _id | authority | username | password | _version |
125
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O | 1 |
126
+ And the `users` is running with the following manifest:
127
+ """yaml
128
+ exposition:
129
+ /:
130
+ POST:
131
+ io:input: [name]
132
+ io:output: true
133
+ incept: id
134
+ query: ~
135
+ endpoint: transit
136
+ """
137
+ # developer:wrong
138
+ When the following request is received:
139
+ """
140
+ POST /users/ HTTP/1.1
141
+ host: nex.toa.io
142
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
143
+ accept: application/yaml
144
+ content-type: application/yaml
145
+
146
+ name: Bill Smith
147
+ """
148
+ Then the following reply is sent:
149
+ """
150
+ 401 Unauthorized
151
+ """
152
+ # a token that does not decrypt
153
+ When the following request is received:
154
+ """
155
+ POST /users/ HTTP/1.1
156
+ host: nex.toa.io
157
+ authorization: Token garbage
158
+ accept: application/yaml
159
+ content-type: application/yaml
160
+
161
+ name: Bill Smith
162
+ """
163
+ Then the following reply is sent:
164
+ """
165
+ 401 Unauthorized
166
+ """
@@ -30,3 +30,42 @@ Feature: Authorities
30
30
  """
31
31
  200 OK
32
32
  """
33
+
34
+ Scenario: Rejecting a malformed authority
35
+ Given the annotation:
36
+ """yaml
37
+ /:
38
+ anonymous: true
39
+ GET:
40
+ dev:stub: Hello
41
+ """
42
+ When the following request is received:
43
+ """
44
+ GET / HTTP/1.1
45
+ host: zzz,identity==abc,authority==q
46
+ """
47
+ Then the following reply is sent:
48
+ """
49
+ 400 Bad Request
50
+ """
51
+
52
+ Scenario: Matching an authority regardless of case
53
+ Given the annotation:
54
+ """yaml
55
+ authorities:
56
+ example: the.example.com
57
+ /:
58
+ anonymous: true
59
+ GET:
60
+ map:authority: authority
61
+ dev:stub: Hello
62
+ """
63
+ When the following request is received:
64
+ """
65
+ GET / HTTP/1.1
66
+ host: The.Example.COM
67
+ """
68
+ Then the following reply is sent:
69
+ """
70
+ 200 OK
71
+ """
@@ -7,6 +7,7 @@ Feature: OIDC tokens with authorities
7
7
  """yaml
8
8
  trust:
9
9
  - iss: http://localhost:44444
10
+ aud: test
10
11
  """
11
12
 
12
13
 
@@ -135,3 +135,66 @@ Feature: Bans
135
135
  """
136
136
  401 Unauthorized
137
137
  """
138
+
139
+ Scenario: Banning an Identity revokes its custom tokens
140
+ Given the `identity.roles` database contains:
141
+ | _id | identity | role |
142
+ | 775a648d054e4ce1a65f8f17e5b51803 | efe3a65ebbee47ed95a73edd911ea328 | system:identity:bans |
143
+ And the `identity.keys` database is empty
144
+ And the `identity.tokens` configuration:
145
+ """yaml
146
+ cache:
147
+ ttl: 1
148
+ """
149
+ When the following request is received:
150
+ """
151
+ POST /identity/tokens/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
152
+ host: nex.toa.io
153
+ authorization: Basic dXNlcjoxMjM0NQ==
154
+ accept: application/yaml
155
+ content-type: application/yaml
156
+
157
+ label: Forever token
158
+ lifetime: 0
159
+ """
160
+ Then the following reply is sent:
161
+ """
162
+ 201 Created
163
+
164
+ token: ${{ token }}
165
+ """
166
+ When the following request is received:
167
+ """
168
+ GET /identity/ HTTP/1.1
169
+ host: nex.toa.io
170
+ authorization: Token ${{ token }}
171
+ """
172
+ Then the following reply is sent:
173
+ """
174
+ 200 OK
175
+ """
176
+ When the following request is received:
177
+ """
178
+ PUT /identity/bans/e8e4f9c2a68d419b861403d71fabc915/ HTTP/1.1
179
+ host: nex.toa.io
180
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
181
+ content-type: application/yaml
182
+
183
+ banned: true
184
+ """
185
+ Then the following reply is sent:
186
+ """
187
+ 200 OK
188
+ """
189
+ # the key is revoked eventually, and the runtime forgets it after `cache.ttl`
190
+ Then after 2 seconds
191
+ When the following request is received:
192
+ """
193
+ GET /identity/ HTTP/1.1
194
+ host: nex.toa.io
195
+ authorization: Token ${{ token }}
196
+ """
197
+ Then the following reply is sent:
198
+ """
199
+ 401 Unauthorized
200
+ """
@@ -88,7 +88,7 @@ Feature: Basic authentication
88
88
  200 OK
89
89
  """
90
90
 
91
- # username is taken
91
+ # username is taken: a wrong password for existing credentials is refused before the call
92
92
  When the following request is received:
93
93
  """
94
94
  POST /users/ HTTP/1.1
@@ -101,7 +101,7 @@ Feature: Basic authentication
101
101
  """
102
102
  Then the following reply is sent:
103
103
  """
104
- 409 Conflict
104
+ 401 Unauthorized
105
105
  """
106
106
 
107
107
  # credentials already exists
@@ -246,7 +246,9 @@ Feature: Basic authentication
246
246
  Scenario: Granting a `system` role to a Principal
247
247
  Given the `identity.basic` configuration:
248
248
  """yaml
249
- principal: root
249
+ principal:
250
+ authority: nex
251
+ username: root
250
252
  """
251
253
  And the annotation:
252
254
  """yaml
@@ -321,6 +323,64 @@ Feature: Basic authentication
321
323
  message: Principal username cannot be changed
322
324
  """
323
325
 
326
+ Scenario: Principal username in another authority is ordinary credentials
327
+ Given the `identity.basic` configuration:
328
+ """yaml
329
+ principal:
330
+ authority: nex
331
+ username: root
332
+ """
333
+ And the annotation:
334
+ """yaml
335
+ /:
336
+ io:output: true
337
+ GET:
338
+ auth:role: system:stub
339
+ dev:stub:
340
+ access: granted!
341
+ """
342
+ When the following request is received:
343
+ """
344
+ POST /identity/basic/ HTTP/1.1
345
+ host: the.other.com
346
+ accept: application/yaml
347
+ content-type: application/yaml
348
+
349
+ username: root
350
+ password: secret#1234
351
+ """
352
+ Then the following reply is sent:
353
+ """
354
+ 201 Created
355
+
356
+ id: ${{ id }}
357
+ """
358
+ Then after 0.1 seconds
359
+ When the following request is received:
360
+ """
361
+ GET /identity/roles/${{ id }}/ HTTP/1.1
362
+ host: the.other.com
363
+ authorization: Basic cm9vdDpzZWNyZXQjMTIzNA==
364
+ accept: application/yaml
365
+ """
366
+ Then the following reply is sent:
367
+ """
368
+ 200 OK
369
+
370
+ []
371
+ """
372
+ When the following request is received:
373
+ """
374
+ GET / HTTP/1.1
375
+ host: the.other.com
376
+ authorization: Basic cm9vdDpzZWNyZXQjMTIzNA==
377
+ accept: application/yaml
378
+ """
379
+ Then the following reply is sent:
380
+ """
381
+ 403 Forbidden
382
+ """
383
+
324
384
  Scenario: Creating an Identity using inception with existing credentials
325
385
  Given the `users` is running with the following manifest:
326
386
  """yaml
@@ -528,3 +588,35 @@ Feature: Basic authentication
528
588
 
529
589
  id: ${{ identity.id }}
530
590
  """
591
+
592
+ Scenario: Password containing colons
593
+ When the following request is received:
594
+ """
595
+ POST /identity/basic/ HTTP/1.1
596
+ host: nex.toa.io
597
+ accept: application/yaml
598
+ content-type: application/yaml
599
+
600
+ username: colon
601
+ password: pa:ss:word
602
+ """
603
+ Then the following reply is sent:
604
+ """
605
+ 201 Created
606
+
607
+ id: ${{ id }}
608
+ """
609
+ # colon:pa:ss:word
610
+ When the following request is received:
611
+ """
612
+ GET /identity/ HTTP/1.1
613
+ host: nex.toa.io
614
+ authorization: Basic Y29sb246cGE6c3M6d29yZA==
615
+ accept: application/yaml
616
+ """
617
+ Then the following reply is sent:
618
+ """
619
+ 200 OK
620
+
621
+ id: ${{ id }}
622
+ """
@@ -87,3 +87,154 @@ Feature: Identity resource
87
87
  """
88
88
  401 Unauthorized
89
89
  """
90
+
91
+ Scenario: Authenticating with a scheme named after an object property
92
+ When the following request is received:
93
+ """
94
+ GET /identity/ HTTP/1.1
95
+ host: nex.toa.io
96
+ authorization: Constructor abc
97
+ """
98
+ Then the following reply is sent:
99
+ """
100
+ 401 Unauthorized
101
+ """
102
+ When the following request is received:
103
+ """
104
+ GET /identity/ HTTP/1.1
105
+ host: nex.toa.io
106
+ authorization: __proto__ abc
107
+ """
108
+ Then the following reply is sent:
109
+ """
110
+ 401 Unauthorized
111
+ """
112
+
113
+ Scenario: Failing authentication repeatedly
114
+ Given the annotation:
115
+ """yaml
116
+ ip: x-real-ip
117
+ bouncer:
118
+ attempts: 2
119
+ interval: 60
120
+ """
121
+ # developer:wrong
122
+ When the following request is received:
123
+ """
124
+ GET /identity/ HTTP/1.1
125
+ host: nex.toa.io
126
+ x-real-ip: 203.0.113.9
127
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
128
+ """
129
+ Then the following reply is sent:
130
+ """
131
+ 401 Unauthorized
132
+ """
133
+ When the following request is received:
134
+ """
135
+ GET /identity/ HTTP/1.1
136
+ host: nex.toa.io
137
+ x-real-ip: 203.0.113.9
138
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
139
+ """
140
+ Then the following reply is sent:
141
+ """
142
+ 401 Unauthorized
143
+ """
144
+ # the address has spent its attempts, and earns one back in interval / attempts seconds
145
+ When the following request is received:
146
+ """
147
+ GET /identity/ HTTP/1.1
148
+ host: nex.toa.io
149
+ x-real-ip: 203.0.113.9
150
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
151
+ """
152
+ Then the following reply is sent:
153
+ """
154
+ 429 Too Many Requests
155
+ retry-after: 30
156
+ """
157
+ # a request without credentials is not metered
158
+ When the following request is received:
159
+ """
160
+ GET /identity/ HTTP/1.1
161
+ host: nex.toa.io
162
+ """
163
+ Then the following reply is sent:
164
+ """
165
+ 201 Created
166
+ """
167
+
168
+ Scenario: Failing authentication behind a trusted proxy
169
+ Given the annotation:
170
+ """yaml
171
+ ip: x-real-ip
172
+ bouncer:
173
+ attempts: 1
174
+ interval: 60
175
+ """
176
+ # developer:wrong
177
+ When the following request is received:
178
+ """
179
+ GET /identity/ HTTP/1.1
180
+ host: nex.toa.io
181
+ x-real-ip: 203.0.113.1
182
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
183
+ """
184
+ Then the following reply is sent:
185
+ """
186
+ 401 Unauthorized
187
+ """
188
+ When the following request is received:
189
+ """
190
+ GET /identity/ HTTP/1.1
191
+ host: nex.toa.io
192
+ x-real-ip: 203.0.113.1
193
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
194
+ """
195
+ Then the following reply is sent:
196
+ """
197
+ 429 Too Many Requests
198
+ """
199
+ # another address, as the proxy reports it
200
+ When the following request is received:
201
+ """
202
+ GET /identity/ HTTP/1.1
203
+ host: nex.toa.io
204
+ x-real-ip: 203.0.113.2
205
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
206
+ """
207
+ Then the following reply is sent:
208
+ """
209
+ 401 Unauthorized
210
+ """
211
+
212
+ Scenario: Failing authentication without a client address
213
+ Given the annotation:
214
+ """yaml
215
+ ip: x-real-ip
216
+ bouncer:
217
+ attempts: 1
218
+ interval: 60
219
+ """
220
+ # the header the address is read from is not sent, so nothing is metered
221
+ When the following request is received:
222
+ """
223
+ GET /identity/ HTTP/1.1
224
+ host: nex.toa.io
225
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
226
+ """
227
+ Then the following reply is sent:
228
+ """
229
+ 401 Unauthorized
230
+ """
231
+ When the following request is received:
232
+ """
233
+ GET /identity/ HTTP/1.1
234
+ host: nex.toa.io
235
+ authorization: Basic ZGV2ZWxvcGVyOndyb25n
236
+ """
237
+ Then the following reply is sent:
238
+ """
239
+ 401 Unauthorized
240
+ """
@@ -10,6 +10,7 @@ Feature: Identity Federation
10
10
  """yaml
11
11
  trust:
12
12
  - iss: http://localhost:44444
13
+ aud: test
13
14
  """
14
15
  And the IDP token for User is issued
15
16
  When the following request is received:
@@ -75,6 +76,7 @@ Feature: Identity Federation
75
76
  """yaml
76
77
  trust:
77
78
  - iss: http://localhost:44444
79
+ aud: test
78
80
  assert: false
79
81
  """
80
82
  Given the `users` is running with the following manifest:
@@ -170,7 +172,9 @@ Feature: Identity Federation
170
172
  """yaml
171
173
  trust:
172
174
  - iss: http://localhost:44444
175
+ aud: test
173
176
  principal:
177
+ authority: nex
174
178
  iss: http://localhost:44444
175
179
  sub: root
176
180
  """
@@ -218,6 +222,7 @@ Feature: Identity Federation
218
222
  """yaml
219
223
  trust:
220
224
  - iss: http://localhost:44444
225
+ aud: test
221
226
  """
222
227
  And the `identity.basic` database is empty
223
228