kiosk-server 0.5.20 → 0.6.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/README.md +63 -41
- data/app/controllers/kiosk/server/auth_controller.rb +18 -62
- data/app/controllers/kiosk/server/discovery_controller.rb +25 -3
- data/app/controllers/kiosk/server/jwks_controller.rb +2 -2
- data/app/controllers/kiosk/server/kyc_attestation_controller.rb +1 -4
- data/app/controllers/kiosk/server/oauth_device_authorization_controller.rb +11 -29
- data/app/controllers/kiosk/server/oauth_registration_controller.rb +34 -0
- data/app/controllers/kiosk/server/oauth_token_controller.rb +33 -32
- data/app/models/kiosk/checkout_mandate.rb +22 -0
- data/app/models/kiosk/settlement.rb +1 -1
- data/lib/generators/kiosk/install/install_generator.rb +0 -5
- data/lib/generators/kiosk/install/templates/create_kiosk_mandates.rb.tt +4 -5
- data/lib/kiosk/payable.rb +19 -25
- data/lib/kiosk/server/account_binding.rb +19 -58
- data/lib/kiosk/server/agent_identity_providers/default_agent_idp.rb +18 -16
- data/lib/kiosk/server/agent_rotation.rb +62 -0
- data/lib/kiosk/server/assistant_key.rb +86 -0
- data/lib/kiosk/server/auth_toll.rb +1 -1
- data/lib/kiosk/server/binding_module_gate.rb +1 -1
- data/lib/kiosk/server/checkout.rb +57 -0
- data/lib/kiosk/server/client.rb +61 -0
- data/lib/kiosk/server/client_assertion.rb +153 -0
- data/lib/kiosk/server/client_registration.rb +94 -0
- data/lib/kiosk/server/configuration_extension.rb +56 -44
- data/lib/kiosk/server/device_code_grant.rb +38 -69
- data/lib/kiosk/server/dpop.rb +121 -0
- data/lib/kiosk/server/eddsa.rb +38 -0
- data/lib/kiosk/server/engine.rb +14 -11
- data/lib/kiosk/server/errors.rb +7 -0
- data/lib/kiosk/server/events_connection.rb +15 -5
- data/lib/kiosk/server/executor/repeat.rb +13 -12
- data/lib/kiosk/server/executor/spending_cap.rb +25 -26
- data/lib/kiosk/server/executor.rb +82 -101
- data/lib/kiosk/server/identity_resolution.rb +12 -3
- data/lib/kiosk/server/jwk.rb +83 -0
- data/lib/kiosk/server/jwks.rb +6 -0
- data/lib/kiosk/server/jwt_issuer.rb +7 -13
- data/lib/kiosk/server/kyc_verifier.rb +2 -2
- data/lib/kiosk/server/link_code.rb +9 -18
- data/lib/kiosk/server/linked_assistants.rb +1 -1
- data/lib/kiosk/server/mandate_verifier.rb +97 -139
- data/lib/kiosk/server/oauth_answers.rb +30 -0
- data/lib/kiosk/server/oauth_error.rb +30 -0
- data/lib/kiosk/server/open_api.rb +22 -20
- data/lib/kiosk/server/payment_claim.rb +44 -75
- data/lib/kiosk/server/receipts.rb +29 -0
- data/lib/kiosk/server/request_validation.rb +1 -2
- data/lib/kiosk/server/revocation_stores.rb +8 -0
- data/lib/kiosk/server/schema_definitions.rb +33 -64
- data/lib/kiosk/server/schemas/auth.schema.json +368 -57
- data/lib/kiosk/server/schemas/binding.schema.json +23 -58
- data/lib/kiosk/server/schemas/kyc.schema.json +19 -76
- data/lib/kiosk/server/schemas/mandates.schema.json +122 -38
- data/lib/kiosk/server/sd_jwt.rb +104 -0
- data/lib/kiosk/server/signing_key.rb +18 -52
- data/lib/kiosk/server/verb_wire.rb +1 -1
- data/lib/kiosk/server/version.rb +1 -1
- data/lib/kiosk/server/well_known.rb +268 -204
- data/lib/kiosk/server/wire_rendering.rb +1 -1
- data/lib/kiosk/server.rb +2 -0
- data/lib/kiosk/skill_link.rb +6 -2
- data/listen.py +38 -8
- metadata +26 -17
- data/app/models/kiosk/cart_mandate.rb +0 -21
- data/lib/generators/kiosk/install/templates/create_kiosk_auth_challenges.rb.tt +0 -16
- data/lib/kiosk/server/agent_login.rb +0 -49
- data/lib/kiosk/server/agent_registration.rb +0 -88
- data/lib/kiosk/server/auth_challenge.rb +0 -33
- data/lib/kiosk/server/auth_challenge_stores.rb +0 -74
- data/lib/kiosk/server/pop_verifier.rb +0 -87
|
@@ -2,101 +2,297 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://kiosk.tech/spec/schemas/auth.schema.json",
|
|
4
4
|
"$comment": "VENDORED copy of the normative kiosk.tech schema at spec/schemas/auth.schema.json. It is here so RequestValidation can hold a reserved-plane request body to it at runtime, and so e2e/schema_conformance.rb can validate this origin's LIVE wire bytes against the PUBLISHED schema -- both with no second checkout and no network fetch. Identical to the upstream copy as PARSED JSON once this $comment is removed.",
|
|
5
|
-
"title": "Kiosk registration and
|
|
6
|
-
"description": "The wire objects of Section 5:
|
|
5
|
+
"title": "Kiosk client registration and tokens (OAuth)",
|
|
6
|
+
"description": "The wire objects of Section 5: RFC 7591 open client registration of the AI assistant's public key, RFC 7523 client assertions signed with that key (the private_key_jwt method), and DPoP-bound access tokens from the client_credentials grant (RFC 9449). The roots are open: they state what MUST be present and the shape of each member.",
|
|
7
7
|
"$defs": {
|
|
8
|
-
"
|
|
8
|
+
"publicJwk": {
|
|
9
9
|
"type": "object",
|
|
10
|
-
"description": "Section 5.1 -- the
|
|
11
|
-
"required": [
|
|
10
|
+
"description": "Section 5.1 -- the AI assistant's public key as an RFC 7517 JWK: an Ed25519 key (RFC 8037), or a P-256 key where the operator accepts ES256. A member that only a private key has is refused. Other members (kid, use, alg) are allowed and ignored.",
|
|
11
|
+
"required": [
|
|
12
|
+
"kty",
|
|
13
|
+
"crv",
|
|
14
|
+
"x"
|
|
15
|
+
],
|
|
12
16
|
"properties": {
|
|
13
|
-
"
|
|
17
|
+
"kty": {
|
|
18
|
+
"enum": [
|
|
19
|
+
"OKP",
|
|
20
|
+
"EC"
|
|
21
|
+
],
|
|
22
|
+
"description": "The key type: OKP for an Ed25519 key, which signs EdDSA; EC for a P-256 key, which signs ES256."
|
|
23
|
+
},
|
|
24
|
+
"crv": {
|
|
25
|
+
"enum": [
|
|
26
|
+
"Ed25519",
|
|
27
|
+
"P-256"
|
|
28
|
+
],
|
|
29
|
+
"description": "The curve: Ed25519 for OKP, P-256 for EC."
|
|
30
|
+
},
|
|
31
|
+
"x": {
|
|
14
32
|
"type": "string",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"description": "The base64url public key of an OKP key, or the base64url x coordinate of an EC key."
|
|
17
35
|
},
|
|
18
|
-
"
|
|
19
|
-
"type": "
|
|
20
|
-
"
|
|
36
|
+
"y": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 1,
|
|
39
|
+
"description": "The base64url y coordinate of an EC key."
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"if": {
|
|
43
|
+
"properties": {
|
|
44
|
+
"kty": {
|
|
45
|
+
"const": "EC"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"then": {
|
|
50
|
+
"required": [
|
|
51
|
+
"y"
|
|
52
|
+
],
|
|
53
|
+
"properties": {
|
|
54
|
+
"crv": {
|
|
55
|
+
"const": "P-256"
|
|
56
|
+
}
|
|
21
57
|
}
|
|
58
|
+
},
|
|
59
|
+
"else": {
|
|
60
|
+
"properties": {
|
|
61
|
+
"crv": {
|
|
62
|
+
"const": "Ed25519"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"not": {
|
|
67
|
+
"required": [
|
|
68
|
+
"d"
|
|
69
|
+
]
|
|
22
70
|
}
|
|
23
71
|
},
|
|
24
|
-
"
|
|
72
|
+
"jwks": {
|
|
25
73
|
"type": "object",
|
|
26
|
-
"description": "Section 5.
|
|
27
|
-
"required": [
|
|
74
|
+
"description": "Section 5.1 -- an RFC 7517 JWK Set that holds exactly one key, the AI assistant's public key.",
|
|
75
|
+
"required": [
|
|
76
|
+
"keys"
|
|
77
|
+
],
|
|
28
78
|
"properties": {
|
|
29
|
-
"
|
|
30
|
-
"type": "
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
79
|
+
"keys": {
|
|
80
|
+
"type": "array",
|
|
81
|
+
"minItems": 1,
|
|
82
|
+
"maxItems": 1,
|
|
83
|
+
"items": {
|
|
84
|
+
"$ref": "#/$defs/publicJwk"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"registrationRequest": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"description": "Section 5.1 -- the body of POST <endpoint>/oauth/register, an RFC 7591 open registration: no initial access token and no human. The operator ignores every member it does not know (RFC 7591 Section 2). An optional proof-of-work proof rides in the `Kiosk-PoW` request header, never in this body.",
|
|
92
|
+
"required": [
|
|
93
|
+
"jwks"
|
|
94
|
+
],
|
|
95
|
+
"properties": {
|
|
96
|
+
"jwks": {
|
|
97
|
+
"$ref": "#/$defs/jwks"
|
|
36
98
|
},
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"description": "
|
|
99
|
+
"token_endpoint_auth_method": {
|
|
100
|
+
"const": "private_key_jwt",
|
|
101
|
+
"description": "The one client authentication method: a client assertion signed with the registered key (RFC 7523)."
|
|
40
102
|
},
|
|
41
|
-
"
|
|
103
|
+
"grant_types": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"items": {
|
|
106
|
+
"enum": [
|
|
107
|
+
"client_credentials",
|
|
108
|
+
"urn:ietf:params:oauth:grant-type:device_code"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"description": "A subset of the grant types this origin serves. The operator registers the client for every grant type it serves and answers that list."
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"clientInformation": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"description": "Section 5.1 -- the 201 answer to POST <endpoint>/oauth/register (RFC 7591 Section 3.2.1), and the 200 answer to POST <endpoint>/auth/rotate (Section 5.7). Registering a key that is registered already answers the same client again.",
|
|
118
|
+
"required": [
|
|
119
|
+
"client_id",
|
|
120
|
+
"jwks",
|
|
121
|
+
"token_endpoint_auth_method",
|
|
122
|
+
"grant_types",
|
|
123
|
+
"dpop_bound_access_tokens"
|
|
124
|
+
],
|
|
125
|
+
"properties": {
|
|
126
|
+
"client_id": {
|
|
42
127
|
"type": "string",
|
|
43
|
-
"
|
|
128
|
+
"minLength": 1,
|
|
129
|
+
"description": "The client identifier. It is also the `agent_id` of the assistant account, stable across a rotation and a rebind."
|
|
44
130
|
},
|
|
45
|
-
"
|
|
131
|
+
"client_id_issued_at": {
|
|
46
132
|
"type": "integer",
|
|
47
|
-
"description": "
|
|
133
|
+
"description": "When the client was registered, in Unix seconds."
|
|
134
|
+
},
|
|
135
|
+
"jwks": {
|
|
136
|
+
"$ref": "#/$defs/jwks"
|
|
137
|
+
},
|
|
138
|
+
"token_endpoint_auth_method": {
|
|
139
|
+
"const": "private_key_jwt"
|
|
140
|
+
},
|
|
141
|
+
"grant_types": {
|
|
142
|
+
"type": "array",
|
|
143
|
+
"minItems": 1,
|
|
144
|
+
"items": {
|
|
145
|
+
"enum": [
|
|
146
|
+
"client_credentials",
|
|
147
|
+
"urn:ietf:params:oauth:grant-type:device_code"
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
"description": "`client_credentials`, and the device-code grant where the origin serves account binding."
|
|
151
|
+
},
|
|
152
|
+
"dpop_bound_access_tokens": {
|
|
153
|
+
"const": true,
|
|
154
|
+
"description": "RFC 9449 Section 5.2: every token request carries a DPoP proof."
|
|
48
155
|
}
|
|
49
156
|
}
|
|
50
157
|
},
|
|
51
|
-
"
|
|
158
|
+
"registrationError": {
|
|
52
159
|
"type": "object",
|
|
53
|
-
"description": "Section 5.
|
|
54
|
-
"required": [
|
|
160
|
+
"description": "Section 5.1 -- the 400 answer of POST <endpoint>/oauth/register (RFC 7591 Section 3.2.2). The toll on registration answers with problem documents instead (Section 10).",
|
|
161
|
+
"required": [
|
|
162
|
+
"error"
|
|
163
|
+
],
|
|
55
164
|
"properties": {
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"description": "The
|
|
165
|
+
"error": {
|
|
166
|
+
"const": "invalid_client_metadata",
|
|
167
|
+
"description": "The one registration error code: a member of the request is missing or not valid."
|
|
59
168
|
},
|
|
60
|
-
"
|
|
169
|
+
"error_description": {
|
|
61
170
|
"type": "string",
|
|
62
|
-
"description": "
|
|
171
|
+
"description": "Human-readable detail. An AI assistant branches on `error` and never on this string."
|
|
63
172
|
}
|
|
64
173
|
}
|
|
65
174
|
},
|
|
66
|
-
"
|
|
175
|
+
"clientAssertion": {
|
|
67
176
|
"type": "object",
|
|
68
|
-
"description": "Section 5.
|
|
69
|
-
"required": [
|
|
177
|
+
"description": "Section 5.2 -- the decoded payload of a client assertion (RFC 7523 Section 3), a compact JWS that the registered key signs. It is sent as `client_assertion`, with `client_assertion_type` = `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. It is a PAYLOAD, not a request body.",
|
|
178
|
+
"required": [
|
|
179
|
+
"iss",
|
|
180
|
+
"sub",
|
|
181
|
+
"aud",
|
|
182
|
+
"exp",
|
|
183
|
+
"jti"
|
|
184
|
+
],
|
|
70
185
|
"properties": {
|
|
71
|
-
"
|
|
186
|
+
"iss": {
|
|
72
187
|
"type": "string",
|
|
73
|
-
"
|
|
188
|
+
"minLength": 1,
|
|
189
|
+
"description": "The client_id."
|
|
74
190
|
},
|
|
75
|
-
"
|
|
191
|
+
"sub": {
|
|
76
192
|
"type": "string",
|
|
77
|
-
"
|
|
193
|
+
"minLength": 1,
|
|
194
|
+
"description": "The client_id, the same value as `iss`."
|
|
78
195
|
},
|
|
79
|
-
"
|
|
196
|
+
"aud": {
|
|
197
|
+
"description": "ORIGIN-BINDING. The issuer of the origin the AI assistant dialed, as its only value (Section 15.1). A string, or an array with that one string.",
|
|
198
|
+
"oneOf": [
|
|
199
|
+
{
|
|
200
|
+
"type": "string"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"type": "array",
|
|
204
|
+
"items": {
|
|
205
|
+
"type": "string"
|
|
206
|
+
},
|
|
207
|
+
"minItems": 1,
|
|
208
|
+
"maxItems": 1
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
"exp": {
|
|
213
|
+
"type": "integer",
|
|
214
|
+
"description": "Expiry. The operator refuses an assertion that has expired or that expires more than 300 seconds after its clock."
|
|
215
|
+
},
|
|
216
|
+
"jti": {
|
|
80
217
|
"type": "string",
|
|
81
|
-
"
|
|
218
|
+
"minLength": 1,
|
|
219
|
+
"description": "A unique id. The operator accepts each id of a client one time."
|
|
220
|
+
},
|
|
221
|
+
"iat": {
|
|
222
|
+
"type": "integer",
|
|
223
|
+
"description": "Optional: the time of signing."
|
|
82
224
|
}
|
|
83
225
|
}
|
|
84
226
|
},
|
|
85
|
-
"
|
|
227
|
+
"tokenResponse": {
|
|
86
228
|
"type": "object",
|
|
87
|
-
"description": "Section 5.3
|
|
88
|
-
"required": [
|
|
229
|
+
"description": "Section 5.3 -- the 200 answer of POST <endpoint>/oauth/token (RFC 6749 Section 5.1), for the client_credentials grant and for the device-code grant (Section 6.1). The principal (`user_id`, `agent_id`) is in the access token's claims.",
|
|
230
|
+
"required": [
|
|
231
|
+
"access_token",
|
|
232
|
+
"token_type",
|
|
233
|
+
"expires_in"
|
|
234
|
+
],
|
|
89
235
|
"properties": {
|
|
90
236
|
"access_token": {
|
|
91
237
|
"type": "string",
|
|
92
|
-
"description": "The access token, a compact
|
|
238
|
+
"description": "The access token, a compact EdDSA JWT. Its claims are `#/$defs/accessTokenClaims`."
|
|
239
|
+
},
|
|
240
|
+
"token_type": {
|
|
241
|
+
"const": "DPoP",
|
|
242
|
+
"description": "Always \"DPoP\": the token is bound to the registered key (Section 5.6)."
|
|
243
|
+
},
|
|
244
|
+
"expires_in": {
|
|
245
|
+
"type": "integer",
|
|
246
|
+
"description": "Seconds until the access token expires."
|
|
247
|
+
},
|
|
248
|
+
"scope": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"description": "The role of the assistant account, as the scope that RFC 6749 Section 5.1 grants. It is present only when the account has a role, and never null. An AI assistant MUST NOT treat its absence as an error."
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"oauthError": {
|
|
255
|
+
"type": "object",
|
|
256
|
+
"description": "Sections 5.3 and 6.1 -- the error answer of POST <endpoint>/oauth/token and POST <endpoint>/oauth/device_authorization (RFC 6749 Section 5.2). The status is 400 for every code except `invalid_client`, which is 401. The toll answers with problem documents instead (Section 6.4).",
|
|
257
|
+
"required": [
|
|
258
|
+
"error"
|
|
259
|
+
],
|
|
260
|
+
"properties": {
|
|
261
|
+
"error": {
|
|
262
|
+
"enum": [
|
|
263
|
+
"invalid_request",
|
|
264
|
+
"invalid_client",
|
|
265
|
+
"invalid_grant",
|
|
266
|
+
"unsupported_grant_type",
|
|
267
|
+
"invalid_dpop_proof",
|
|
268
|
+
"authorization_pending",
|
|
269
|
+
"slow_down",
|
|
270
|
+
"expired_token",
|
|
271
|
+
"access_denied"
|
|
272
|
+
],
|
|
273
|
+
"description": "The OAuth error code: four of RFC 6749 Section 5.2, `invalid_dpop_proof` of RFC 9449, and the four device-grant codes of RFC 8628 Section 3.5. A closed vocabulary: an operator MUST NOT emit a code outside it."
|
|
274
|
+
},
|
|
275
|
+
"error_description": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"description": "Human-readable detail. An AI assistant branches on `error` and never on this string."
|
|
93
278
|
}
|
|
94
279
|
}
|
|
95
280
|
},
|
|
96
281
|
"accessTokenClaims": {
|
|
97
282
|
"type": "object",
|
|
98
|
-
"description": "Section 5.4 -- the decoded claim set of `access_token`. A 3-part
|
|
99
|
-
"required": [
|
|
283
|
+
"description": "Section 5.4 -- the decoded claim set of `access_token`. A 3-part EdDSA JWT signed by the operator and verifiable statelessly against the JWKS of Section 4.4, bound to the identity's key and presented as `Authorization: DPoP` with a DPoP proof (Section 5.6).",
|
|
284
|
+
"required": [
|
|
285
|
+
"sub",
|
|
286
|
+
"agent_id",
|
|
287
|
+
"actor",
|
|
288
|
+
"iss",
|
|
289
|
+
"aud",
|
|
290
|
+
"iat",
|
|
291
|
+
"nbf",
|
|
292
|
+
"exp",
|
|
293
|
+
"jti",
|
|
294
|
+
"cnf"
|
|
295
|
+
],
|
|
100
296
|
"properties": {
|
|
101
297
|
"sub": {
|
|
102
298
|
"type": "string",
|
|
@@ -121,14 +317,129 @@
|
|
|
121
317
|
"aud": {
|
|
122
318
|
"description": "The operator issuer. A string, or the array spelling RFC 7519 permits.",
|
|
123
319
|
"oneOf": [
|
|
124
|
-
{
|
|
125
|
-
|
|
320
|
+
{
|
|
321
|
+
"type": "string"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"type": "array",
|
|
325
|
+
"items": {
|
|
326
|
+
"type": "string"
|
|
327
|
+
},
|
|
328
|
+
"minItems": 1
|
|
329
|
+
}
|
|
126
330
|
]
|
|
127
331
|
},
|
|
128
|
-
"iat": {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
332
|
+
"iat": {
|
|
333
|
+
"type": "integer",
|
|
334
|
+
"description": "Issued-at. Also what the revoked-before watermark of Section 15.4 is compared against."
|
|
335
|
+
},
|
|
336
|
+
"nbf": {
|
|
337
|
+
"type": "integer",
|
|
338
|
+
"description": "Not-before."
|
|
339
|
+
},
|
|
340
|
+
"exp": {
|
|
341
|
+
"type": "integer",
|
|
342
|
+
"description": "Expiry. Default lifetime is 1 hour; the durable credential is the private key, not the token."
|
|
343
|
+
},
|
|
344
|
+
"jti": {
|
|
345
|
+
"type": "string",
|
|
346
|
+
"description": "Unique token id."
|
|
347
|
+
},
|
|
348
|
+
"cnf": {
|
|
349
|
+
"type": "object",
|
|
350
|
+
"description": "RFC 9449 Section 6.1 -- the key the token is bound to. Only a DPoP proof that this key signs makes the token usable.",
|
|
351
|
+
"required": [
|
|
352
|
+
"jkt"
|
|
353
|
+
],
|
|
354
|
+
"properties": {
|
|
355
|
+
"jkt": {
|
|
356
|
+
"type": "string",
|
|
357
|
+
"minLength": 1,
|
|
358
|
+
"description": "The RFC 7638 thumbprint of the registered key."
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
"dpopProof": {
|
|
365
|
+
"type": "object",
|
|
366
|
+
"description": "Section 5.6 -- the decoded payload of the `DPoP` request header, a compact JWS whose header carries `typ: \"dpop+jwt\"`, the `alg` of the registered key (EdDSA, or ES256) and its public `jwk`. The AI assistant signs a new one for each token request and for each request that carries an access token.",
|
|
367
|
+
"required": [
|
|
368
|
+
"jti",
|
|
369
|
+
"htm",
|
|
370
|
+
"htu",
|
|
371
|
+
"iat"
|
|
372
|
+
],
|
|
373
|
+
"properties": {
|
|
374
|
+
"jti": {
|
|
375
|
+
"type": "string",
|
|
376
|
+
"minLength": 1,
|
|
377
|
+
"description": "A unique id for this proof."
|
|
378
|
+
},
|
|
379
|
+
"htm": {
|
|
380
|
+
"type": "string",
|
|
381
|
+
"minLength": 1,
|
|
382
|
+
"description": "The HTTP method of the request."
|
|
383
|
+
},
|
|
384
|
+
"htu": {
|
|
385
|
+
"type": "string",
|
|
386
|
+
"minLength": 1,
|
|
387
|
+
"description": "The URL of the request, with no query and no fragment. On the event stream it is `events_url` with `wss` written as `https`."
|
|
388
|
+
},
|
|
389
|
+
"iat": {
|
|
390
|
+
"type": "integer",
|
|
391
|
+
"description": "The time of signing. The operator refuses a proof more than 60 seconds from its clock."
|
|
392
|
+
},
|
|
393
|
+
"ath": {
|
|
394
|
+
"type": "string",
|
|
395
|
+
"minLength": 1,
|
|
396
|
+
"description": "The base64url SHA-256 of the ASCII access token, with no padding. REQUIRED on a request that carries an access token; absent on a token request."
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"rotationAssertion": {
|
|
401
|
+
"type": "object",
|
|
402
|
+
"description": "Section 5.7 -- the decoded payload of `client_assertion` in a rotation: a client assertion by the registered key that also names the new key.",
|
|
403
|
+
"allOf": [
|
|
404
|
+
{
|
|
405
|
+
"$ref": "#/$defs/clientAssertion"
|
|
406
|
+
}
|
|
407
|
+
],
|
|
408
|
+
"required": [
|
|
409
|
+
"new_key"
|
|
410
|
+
],
|
|
411
|
+
"properties": {
|
|
412
|
+
"new_key": {
|
|
413
|
+
"type": "string",
|
|
414
|
+
"minLength": 1,
|
|
415
|
+
"description": "The RFC 7638 thumbprint of the new key in `jwks`."
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
"rotationRequest": {
|
|
420
|
+
"type": "object",
|
|
421
|
+
"description": "Section 5.7 -- the body of POST <endpoint>/auth/rotate, a Kiosk extension. The answer is `#/$defs/clientInformation` with the new key; the AI assistant then asks the token endpoint for a token with the new key.",
|
|
422
|
+
"required": [
|
|
423
|
+
"client_assertion_type",
|
|
424
|
+
"client_assertion",
|
|
425
|
+
"new_client_assertion",
|
|
426
|
+
"jwks"
|
|
427
|
+
],
|
|
428
|
+
"properties": {
|
|
429
|
+
"client_assertion_type": {
|
|
430
|
+
"const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
|
431
|
+
},
|
|
432
|
+
"client_assertion": {
|
|
433
|
+
"type": "string",
|
|
434
|
+
"description": "A client assertion by the registered key. Its decoded payload is `#/$defs/rotationAssertion`."
|
|
435
|
+
},
|
|
436
|
+
"new_client_assertion": {
|
|
437
|
+
"type": "string",
|
|
438
|
+
"description": "A client assertion by the new key, for the same client_id. Its decoded payload is `#/$defs/clientAssertion`."
|
|
439
|
+
},
|
|
440
|
+
"jwks": {
|
|
441
|
+
"$ref": "#/$defs/jwks"
|
|
442
|
+
}
|
|
132
443
|
}
|
|
133
444
|
}
|
|
134
445
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"$id": "https://kiosk.tech/spec/schemas/binding.schema.json",
|
|
4
4
|
"$comment": "VENDORED copy of the normative kiosk.tech schema at spec/schemas/binding.schema.json. It is here so RequestValidation can hold a reserved-plane request body to it at runtime, and so e2e/schema_conformance.rb can validate this origin's LIVE wire bytes against the PUBLISHED schema -- both with no second checkout and no network fetch. Identical to the upstream copy as PARSED JSON once this $comment is removed.",
|
|
5
5
|
"title": "Kiosk account binding -- claim and link",
|
|
6
|
-
"description": "The wire objects of Section 6, the one-time ceremonies that bind
|
|
6
|
+
"description": "The wire objects of Section 6, the one-time ceremonies that bind a registered AI assistant to an existing operator account: claim, an RFC 8628 device grant, and link. Both need the human's approval and a client assertion by the registered key. The two /oauth/* requests are form-encoded and have no schema here; their answers are in auth.schema.json.",
|
|
7
7
|
"$defs": {
|
|
8
8
|
"deviceAuthorization": {
|
|
9
9
|
"type": "object",
|
|
@@ -46,56 +46,20 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"deviceTokenResponse": {
|
|
49
|
-
"
|
|
50
|
-
"description": "Section 6.1 step 3: the success answer to POST <endpoint>/oauth/token.
|
|
51
|
-
"required": ["access_token", "token_type", "expires_in"],
|
|
52
|
-
"properties": {
|
|
53
|
-
"access_token": {
|
|
54
|
-
"type": "string",
|
|
55
|
-
"description": "The access token. Its claims are auth.schema.json#/$defs/accessTokenClaims."
|
|
56
|
-
},
|
|
57
|
-
"token_type": {
|
|
58
|
-
"const": "Bearer",
|
|
59
|
-
"description": "Always \"Bearer\"."
|
|
60
|
-
},
|
|
61
|
-
"expires_in": {
|
|
62
|
-
"type": "integer",
|
|
63
|
-
"description": "Seconds until the access token expires."
|
|
64
|
-
},
|
|
65
|
-
"scope": {
|
|
66
|
-
"type": "string",
|
|
67
|
-
"description": "The approving human's role, as the scope that RFC 6749 Section 5.1 grants. It is present only when the binding carries a role, and never null. An AI assistant MUST NOT treat its absence as an error."
|
|
68
|
-
}
|
|
69
|
-
}
|
|
49
|
+
"$ref": "https://kiosk.tech/spec/schemas/auth.schema.json#/$defs/tokenResponse",
|
|
50
|
+
"description": "Section 6.1 step 3: the success answer to the device-code poll of POST <endpoint>/oauth/token. `scope` is the approving human's role."
|
|
70
51
|
},
|
|
71
52
|
"oauthError": {
|
|
72
|
-
"
|
|
73
|
-
"description": "Section 6.1: the error answer of both /oauth/* endpoints, the one error on this wire that is not an RFC 9457 problem document.
|
|
74
|
-
"required": ["error"],
|
|
75
|
-
"properties": {
|
|
76
|
-
"error": {
|
|
77
|
-
"enum": [
|
|
78
|
-
"authorization_pending",
|
|
79
|
-
"slow_down",
|
|
80
|
-
"expired_token",
|
|
81
|
-
"access_denied",
|
|
82
|
-
"invalid_grant",
|
|
83
|
-
"invalid_client",
|
|
84
|
-
"invalid_request",
|
|
85
|
-
"unsupported_grant_type"
|
|
86
|
-
],
|
|
87
|
-
"description": "The OAuth error code. A closed vocabulary: an operator MUST NOT emit anything outside it, or an AI assistant polling this ceremony has a branch it cannot take."
|
|
88
|
-
},
|
|
89
|
-
"error_description": {
|
|
90
|
-
"type": "string",
|
|
91
|
-
"description": "Human-readable detail. An AI assistant branches on `error` and never on this string."
|
|
92
|
-
}
|
|
93
|
-
}
|
|
53
|
+
"$ref": "https://kiosk.tech/spec/schemas/auth.schema.json#/$defs/oauthError",
|
|
54
|
+
"description": "Section 6.1: the error answer of both /oauth/* endpoints, the one error shape on this wire that is not an RFC 9457 problem document."
|
|
94
55
|
},
|
|
95
56
|
"linkCode": {
|
|
96
57
|
"type": "object",
|
|
97
58
|
"description": "Section 6.2 -- the 201 answer to POST <endpoint>/auth/link, called on the operator's own session by the signed-in human. 201 rather than 200 because the call MINTS a redeemable single-use credential.",
|
|
98
|
-
"required": [
|
|
59
|
+
"required": [
|
|
60
|
+
"link_code",
|
|
61
|
+
"expires_in"
|
|
62
|
+
],
|
|
99
63
|
"properties": {
|
|
100
64
|
"link_code": {
|
|
101
65
|
"type": "string",
|
|
@@ -109,35 +73,36 @@
|
|
|
109
73
|
},
|
|
110
74
|
"claimRequest": {
|
|
111
75
|
"type": "object",
|
|
112
|
-
"description": "Section 6.2 -- the body of POST <endpoint>/auth/claim, which redeems a link code. The
|
|
113
|
-
"required": [
|
|
76
|
+
"description": "Section 6.2 -- the body of POST <endpoint>/auth/claim, which redeems a link code. The client assertion is REQUIRED, so a leaked code alone can never bind a key its holder does not control. The answer is 204 No Content; the AI assistant then asks the token endpoint for a token under the human's account.",
|
|
77
|
+
"required": [
|
|
78
|
+
"code",
|
|
79
|
+
"client_assertion_type",
|
|
80
|
+
"client_assertion"
|
|
81
|
+
],
|
|
114
82
|
"properties": {
|
|
115
83
|
"code": {
|
|
116
84
|
"type": "string",
|
|
117
85
|
"description": "The `link_code` the human handed over."
|
|
118
86
|
},
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"description": "The AI assistant's public key, PEM-encoded."
|
|
87
|
+
"client_assertion_type": {
|
|
88
|
+
"const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
|
122
89
|
},
|
|
123
|
-
"
|
|
90
|
+
"client_assertion": {
|
|
124
91
|
"type": "string",
|
|
125
|
-
"description": "
|
|
92
|
+
"description": "A client assertion by the registered key (auth.schema.json#/$defs/clientAssertion)."
|
|
126
93
|
}
|
|
127
94
|
}
|
|
128
95
|
},
|
|
129
|
-
"claimResponse": {
|
|
130
|
-
"$ref": "https://kiosk.tech/spec/schemas/auth.schema.json#/$defs/registration",
|
|
131
|
-
"description": "Section 6.2: the 201 answer to POST <endpoint>/auth/claim, in the shape of the register answer. A new key, a rebound key and a repeated bind give answers that cannot be told apart (Section 6.3)."
|
|
132
|
-
},
|
|
133
96
|
"unlinkRequest": {
|
|
134
97
|
"type": "object",
|
|
135
98
|
"description": "Section 6.3 -- the body of POST <endpoint>/auth/unlink, called on the operator's own session. The response is 204 No Content and carries no body at all, which is why no response object appears here.",
|
|
136
|
-
"required": [
|
|
99
|
+
"required": [
|
|
100
|
+
"agent_id"
|
|
101
|
+
],
|
|
137
102
|
"properties": {
|
|
138
103
|
"agent_id": {
|
|
139
104
|
"type": "string",
|
|
140
|
-
"description": "The bound AI assistant to deactivate. Its tokens stop verifying and
|
|
105
|
+
"description": "The bound AI assistant (its client_id) to deactivate. Its tokens stop verifying and the token endpoint answers it `invalid_client` (Section 15.4)."
|
|
141
106
|
}
|
|
142
107
|
}
|
|
143
108
|
}
|