@kanonak-protocol/sdk 5.37.0 → 6.0.0
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.
- package/dist/auth/CredentialProvider.d.ts +69 -49
- package/dist/auth/ExecCredentialProvider.d.ts +55 -0
- package/dist/auth/OAuthDiscovery.d.ts +38 -10
- package/dist/auth/TokenCredentialProvider.d.ts +26 -6
- package/dist/auth/challenges.d.ts +17 -0
- package/dist/auth/credentialAnswer.d.ts +34 -0
- package/dist/auth/credentialConfig.d.ts +32 -0
- package/dist/auth/credentialedFetch.d.ts +33 -22
- package/dist/auth/index.d.ts +10 -4
- package/dist/auth/scope.d.ts +30 -0
- package/dist/browser.d.ts +4 -2
- package/dist/browser.js +1 -1
- package/dist/chunk-22DSABP6.js +1 -0
- package/dist/{chunk-HKX27KBZ.js → chunk-7F2EAT5Y.js} +1 -1
- package/dist/{chunk-LE26SEJU.js → chunk-A3HRXFVX.js} +1 -1
- package/dist/{chunk-4CXAZH3C.js → chunk-ADSDFJUO.js} +1 -1
- package/dist/{chunk-W5AOJ5SJ.js → chunk-GKIX3OD6.js} +1 -1
- package/dist/chunk-HKQTB65D.js +2 -0
- package/dist/{chunk-R4QCVB5M.js → chunk-O6SOU5Y2.js} +1 -1
- package/dist/chunk-PNBTIGX7.js +2 -0
- package/dist/{chunk-DONOB6RA.js → chunk-SBO34ENZ.js} +1 -1
- package/dist/chunk-TYVZXABI.js +155 -0
- package/dist/http/HttpError.d.ts +38 -0
- package/dist/http/kanonakFetch.d.ts +5 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +2 -1
- package/dist/parsing/index.js +1 -1
- package/dist/producer/index.js +1 -1
- package/dist/reasoning/index.js +1 -1
- package/dist/repositories/browser.js +1 -1
- package/dist/repositories/index.js +1 -1
- package/dist/search/index.js +1 -1
- package/dist/server/index.js +5 -5
- package/dist/transformations/index.js +2 -2
- package/dist/transformations/v3/CompiledTransformationV3.d.ts +28 -27
- package/dist/transformations/v3/TransformationUriConstantsV3.d.ts +27 -0
- package/dist/validation/index.js +1 -1
- package/package.json +5 -4
- package/vectors/credential-helper-vectors.json +1039 -0
- package/dist/chunk-2WUZKSLB.js +0 -2
- package/dist/chunk-3353RWDC.js +0 -1
- package/dist/chunk-EG47OOJC.js +0 -155
- package/dist/chunk-OOYGILH4.js +0 -2
|
@@ -0,0 +1,1039 @@
|
|
|
1
|
+
{
|
|
2
|
+
"protocolVersion": 1,
|
|
3
|
+
"description": "Conformance vectors for the exec credential helper protocol (kanonak-protocol/typescript#126). The CLI runs `<helper command line> get` or `... erase`, writes one JSON request to stdin and reads one JSON answer from stdout; a non-zero exit is an error and never falls back to anonymous. requests: the exact JSON the CLI sends. answers/eraseAnswers: a helper's answer to `request` at Unix time `now`, and whether the CLI accepts it (with the answer it keeps) or refuses it (with the problem code). scopes: whether a credential's scope contains a URL, which is where the CLI attaches it; invalidScopes are refused. challenges: how a WWW-Authenticate value is parsed into the request's challenges; malformedChallenges are errors. Cases are only added, never changed.",
|
|
4
|
+
"requests": [
|
|
5
|
+
{
|
|
6
|
+
"id": "get-pre-emptive",
|
|
7
|
+
"description": "The transport knows the origin is gated (RFC 9728 metadata, or an earlier challenge) and asks before sending: no challenges.",
|
|
8
|
+
"operation": "get",
|
|
9
|
+
"request": {
|
|
10
|
+
"version": 1,
|
|
11
|
+
"operation": "get",
|
|
12
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
13
|
+
"method": "GET",
|
|
14
|
+
"interactive": false
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "get-after-challenge",
|
|
19
|
+
"description": "A 401's challenges, parsed, in the server's order.",
|
|
20
|
+
"operation": "get",
|
|
21
|
+
"request": {
|
|
22
|
+
"version": 1,
|
|
23
|
+
"operation": "get",
|
|
24
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
25
|
+
"method": "GET",
|
|
26
|
+
"challenges": [
|
|
27
|
+
{
|
|
28
|
+
"scheme": "Bearer",
|
|
29
|
+
"params": {
|
|
30
|
+
"resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
|
|
31
|
+
"error": "invalid_token"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"interactive": false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "erase-rejected",
|
|
40
|
+
"description": "After a 401 to a request that carried a credential: the rejected credential's scope and the server's challenges. get follows once.",
|
|
41
|
+
"operation": "erase",
|
|
42
|
+
"request": {
|
|
43
|
+
"version": 1,
|
|
44
|
+
"operation": "erase",
|
|
45
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
46
|
+
"method": "GET",
|
|
47
|
+
"challenges": [
|
|
48
|
+
{
|
|
49
|
+
"scheme": "Bearer",
|
|
50
|
+
"params": {
|
|
51
|
+
"resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
|
|
52
|
+
"error": "invalid_token"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"interactive": false,
|
|
57
|
+
"scope": "https://registry.example.net/example.com"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"answers": [
|
|
62
|
+
{
|
|
63
|
+
"id": "credential",
|
|
64
|
+
"description": "Headers, a scope containing the resource, and an expiry in Unix seconds.",
|
|
65
|
+
"request": {
|
|
66
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
67
|
+
"method": "GET"
|
|
68
|
+
},
|
|
69
|
+
"now": 1791000000,
|
|
70
|
+
"answer": {
|
|
71
|
+
"version": 1,
|
|
72
|
+
"status": "credential",
|
|
73
|
+
"headers": {
|
|
74
|
+
"Authorization": "Bearer example-token"
|
|
75
|
+
},
|
|
76
|
+
"scope": "https://registry.example.net/example.com",
|
|
77
|
+
"expiresAt": 1791400000
|
|
78
|
+
},
|
|
79
|
+
"expected": {
|
|
80
|
+
"ok": true,
|
|
81
|
+
"answer": {
|
|
82
|
+
"status": "credential",
|
|
83
|
+
"headers": {
|
|
84
|
+
"Authorization": "Bearer example-token"
|
|
85
|
+
},
|
|
86
|
+
"scope": "https://registry.example.net/example.com",
|
|
87
|
+
"expiresAt": 1791400000
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"id": "credential-without-expiry",
|
|
93
|
+
"description": "No expiresAt: valid for as long as the process runs.",
|
|
94
|
+
"request": {
|
|
95
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
96
|
+
"method": "GET"
|
|
97
|
+
},
|
|
98
|
+
"now": 1791000000,
|
|
99
|
+
"answer": {
|
|
100
|
+
"version": 1,
|
|
101
|
+
"status": "credential",
|
|
102
|
+
"headers": {
|
|
103
|
+
"Authorization": "Bearer example-token"
|
|
104
|
+
},
|
|
105
|
+
"scope": "https://registry.example.net/example.com"
|
|
106
|
+
},
|
|
107
|
+
"expected": {
|
|
108
|
+
"ok": true,
|
|
109
|
+
"answer": {
|
|
110
|
+
"status": "credential",
|
|
111
|
+
"headers": {
|
|
112
|
+
"Authorization": "Bearer example-token"
|
|
113
|
+
},
|
|
114
|
+
"scope": "https://registry.example.net/example.com"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "credential-origin-scope",
|
|
120
|
+
"description": "The helper decides how wide the scope is, so long as it contains the resource.",
|
|
121
|
+
"request": {
|
|
122
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
123
|
+
"method": "GET"
|
|
124
|
+
},
|
|
125
|
+
"now": 1791000000,
|
|
126
|
+
"answer": {
|
|
127
|
+
"version": 1,
|
|
128
|
+
"status": "credential",
|
|
129
|
+
"headers": {
|
|
130
|
+
"Authorization": "Bearer example-token"
|
|
131
|
+
},
|
|
132
|
+
"scope": "https://registry.example.net/"
|
|
133
|
+
},
|
|
134
|
+
"expected": {
|
|
135
|
+
"ok": true,
|
|
136
|
+
"answer": {
|
|
137
|
+
"status": "credential",
|
|
138
|
+
"headers": {
|
|
139
|
+
"Authorization": "Bearer example-token"
|
|
140
|
+
},
|
|
141
|
+
"scope": "https://registry.example.net/"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "several-headers",
|
|
147
|
+
"description": "Any header-based scheme: the transport attaches the headers verbatim.",
|
|
148
|
+
"request": {
|
|
149
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
150
|
+
"method": "GET"
|
|
151
|
+
},
|
|
152
|
+
"now": 1791000000,
|
|
153
|
+
"answer": {
|
|
154
|
+
"version": 1,
|
|
155
|
+
"status": "credential",
|
|
156
|
+
"headers": {
|
|
157
|
+
"Authorization": "DPoP abc",
|
|
158
|
+
"DPoP": "eyJ0eXAiOiJkcG9wK2p3dCJ9.e30.sig"
|
|
159
|
+
},
|
|
160
|
+
"scope": "https://registry.example.net/example.com"
|
|
161
|
+
},
|
|
162
|
+
"expected": {
|
|
163
|
+
"ok": true,
|
|
164
|
+
"answer": {
|
|
165
|
+
"status": "credential",
|
|
166
|
+
"headers": {
|
|
167
|
+
"Authorization": "DPoP abc",
|
|
168
|
+
"DPoP": "eyJ0eXAiOiJkcG9wK2p3dCJ9.e30.sig"
|
|
169
|
+
},
|
|
170
|
+
"scope": "https://registry.example.net/example.com"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "declined",
|
|
176
|
+
"description": "Declining is not an error: it means no credential for this resource.",
|
|
177
|
+
"request": {
|
|
178
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
179
|
+
"method": "GET"
|
|
180
|
+
},
|
|
181
|
+
"now": 1791000000,
|
|
182
|
+
"answer": {
|
|
183
|
+
"version": 1,
|
|
184
|
+
"status": "declined",
|
|
185
|
+
"reason": "not enrolled for example.com"
|
|
186
|
+
},
|
|
187
|
+
"expected": {
|
|
188
|
+
"ok": true,
|
|
189
|
+
"answer": {
|
|
190
|
+
"status": "declined",
|
|
191
|
+
"reason": "not enrolled for example.com"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"id": "declined-without-reason",
|
|
197
|
+
"description": "The reason is optional.",
|
|
198
|
+
"request": {
|
|
199
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
200
|
+
"method": "GET"
|
|
201
|
+
},
|
|
202
|
+
"now": 1791000000,
|
|
203
|
+
"answer": {
|
|
204
|
+
"version": 1,
|
|
205
|
+
"status": "declined"
|
|
206
|
+
},
|
|
207
|
+
"expected": {
|
|
208
|
+
"ok": true,
|
|
209
|
+
"answer": {
|
|
210
|
+
"status": "declined"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"id": "unknown-fields-are-ignored",
|
|
216
|
+
"description": "Fields the protocol does not define are ignored, so a newer helper can add some.",
|
|
217
|
+
"request": {
|
|
218
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
219
|
+
"method": "GET"
|
|
220
|
+
},
|
|
221
|
+
"now": 1791000000,
|
|
222
|
+
"answer": {
|
|
223
|
+
"version": 1,
|
|
224
|
+
"status": "credential",
|
|
225
|
+
"headers": {
|
|
226
|
+
"Authorization": "Bearer example-token"
|
|
227
|
+
},
|
|
228
|
+
"scope": "https://registry.example.net/example.com",
|
|
229
|
+
"tokenType": "Bearer",
|
|
230
|
+
"issuedAt": 1791000000
|
|
231
|
+
},
|
|
232
|
+
"expected": {
|
|
233
|
+
"ok": true,
|
|
234
|
+
"answer": {
|
|
235
|
+
"status": "credential",
|
|
236
|
+
"headers": {
|
|
237
|
+
"Authorization": "Bearer example-token"
|
|
238
|
+
},
|
|
239
|
+
"scope": "https://registry.example.net/example.com"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "not-an-object",
|
|
245
|
+
"description": "The answer is one JSON object.",
|
|
246
|
+
"request": {
|
|
247
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
248
|
+
"method": "GET"
|
|
249
|
+
},
|
|
250
|
+
"now": 1791000000,
|
|
251
|
+
"answer": [
|
|
252
|
+
{
|
|
253
|
+
"version": 1,
|
|
254
|
+
"status": "credential",
|
|
255
|
+
"headers": {
|
|
256
|
+
"Authorization": "Bearer example-token"
|
|
257
|
+
},
|
|
258
|
+
"scope": "https://registry.example.net/example.com"
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"expected": {
|
|
262
|
+
"ok": false,
|
|
263
|
+
"problem": "malformed"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"id": "version-missing",
|
|
268
|
+
"description": "Every answer carries the protocol version.",
|
|
269
|
+
"request": {
|
|
270
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
271
|
+
"method": "GET"
|
|
272
|
+
},
|
|
273
|
+
"now": 1791000000,
|
|
274
|
+
"answer": {
|
|
275
|
+
"status": "credential",
|
|
276
|
+
"headers": {
|
|
277
|
+
"Authorization": "Bearer example-token"
|
|
278
|
+
},
|
|
279
|
+
"scope": "https://registry.example.net/example.com"
|
|
280
|
+
},
|
|
281
|
+
"expected": {
|
|
282
|
+
"ok": false,
|
|
283
|
+
"problem": "version"
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"id": "version-2",
|
|
288
|
+
"description": "Only version 1 is spoken.",
|
|
289
|
+
"request": {
|
|
290
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
291
|
+
"method": "GET"
|
|
292
|
+
},
|
|
293
|
+
"now": 1791000000,
|
|
294
|
+
"answer": {
|
|
295
|
+
"version": 2,
|
|
296
|
+
"status": "credential",
|
|
297
|
+
"headers": {
|
|
298
|
+
"Authorization": "Bearer example-token"
|
|
299
|
+
},
|
|
300
|
+
"scope": "https://registry.example.net/example.com"
|
|
301
|
+
},
|
|
302
|
+
"expected": {
|
|
303
|
+
"ok": false,
|
|
304
|
+
"problem": "version"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"id": "status-unknown",
|
|
309
|
+
"description": "status is credential or declined.",
|
|
310
|
+
"request": {
|
|
311
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
312
|
+
"method": "GET"
|
|
313
|
+
},
|
|
314
|
+
"now": 1791000000,
|
|
315
|
+
"answer": {
|
|
316
|
+
"version": 1,
|
|
317
|
+
"status": "granted",
|
|
318
|
+
"headers": {
|
|
319
|
+
"Authorization": "Bearer example-token"
|
|
320
|
+
},
|
|
321
|
+
"scope": "https://registry.example.net/example.com"
|
|
322
|
+
},
|
|
323
|
+
"expected": {
|
|
324
|
+
"ok": false,
|
|
325
|
+
"problem": "status"
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"id": "headers-missing",
|
|
330
|
+
"description": "A credential carries headers.",
|
|
331
|
+
"request": {
|
|
332
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
333
|
+
"method": "GET"
|
|
334
|
+
},
|
|
335
|
+
"now": 1791000000,
|
|
336
|
+
"answer": {
|
|
337
|
+
"version": 1,
|
|
338
|
+
"status": "credential",
|
|
339
|
+
"scope": "https://registry.example.net/example.com"
|
|
340
|
+
},
|
|
341
|
+
"expected": {
|
|
342
|
+
"ok": false,
|
|
343
|
+
"problem": "headers"
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"id": "headers-empty",
|
|
348
|
+
"description": "At least one header.",
|
|
349
|
+
"request": {
|
|
350
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
351
|
+
"method": "GET"
|
|
352
|
+
},
|
|
353
|
+
"now": 1791000000,
|
|
354
|
+
"answer": {
|
|
355
|
+
"version": 1,
|
|
356
|
+
"status": "credential",
|
|
357
|
+
"headers": {},
|
|
358
|
+
"scope": "https://registry.example.net/example.com"
|
|
359
|
+
},
|
|
360
|
+
"expected": {
|
|
361
|
+
"ok": false,
|
|
362
|
+
"problem": "headers"
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"id": "header-value-not-a-string",
|
|
367
|
+
"description": "Header values are strings.",
|
|
368
|
+
"request": {
|
|
369
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
370
|
+
"method": "GET"
|
|
371
|
+
},
|
|
372
|
+
"now": 1791000000,
|
|
373
|
+
"answer": {
|
|
374
|
+
"version": 1,
|
|
375
|
+
"status": "credential",
|
|
376
|
+
"headers": {
|
|
377
|
+
"Authorization": 42
|
|
378
|
+
},
|
|
379
|
+
"scope": "https://registry.example.net/example.com"
|
|
380
|
+
},
|
|
381
|
+
"expected": {
|
|
382
|
+
"ok": false,
|
|
383
|
+
"problem": "headers"
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"id": "header-value-with-newline",
|
|
388
|
+
"description": "A header value never carries CR or LF.",
|
|
389
|
+
"request": {
|
|
390
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
391
|
+
"method": "GET"
|
|
392
|
+
},
|
|
393
|
+
"now": 1791000000,
|
|
394
|
+
"answer": {
|
|
395
|
+
"version": 1,
|
|
396
|
+
"status": "credential",
|
|
397
|
+
"headers": {
|
|
398
|
+
"Authorization": "Bearer a\r\nX-Injected: 1"
|
|
399
|
+
},
|
|
400
|
+
"scope": "https://registry.example.net/example.com"
|
|
401
|
+
},
|
|
402
|
+
"expected": {
|
|
403
|
+
"ok": false,
|
|
404
|
+
"problem": "headers"
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"id": "header-name-invalid",
|
|
409
|
+
"description": "Header names are RFC 9110 tokens.",
|
|
410
|
+
"request": {
|
|
411
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
412
|
+
"method": "GET"
|
|
413
|
+
},
|
|
414
|
+
"now": 1791000000,
|
|
415
|
+
"answer": {
|
|
416
|
+
"version": 1,
|
|
417
|
+
"status": "credential",
|
|
418
|
+
"headers": {
|
|
419
|
+
"Bad Header": "x"
|
|
420
|
+
},
|
|
421
|
+
"scope": "https://registry.example.net/example.com"
|
|
422
|
+
},
|
|
423
|
+
"expected": {
|
|
424
|
+
"ok": false,
|
|
425
|
+
"problem": "headers"
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"id": "scope-missing",
|
|
430
|
+
"description": "A credential names its scope.",
|
|
431
|
+
"request": {
|
|
432
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
433
|
+
"method": "GET"
|
|
434
|
+
},
|
|
435
|
+
"now": 1791000000,
|
|
436
|
+
"answer": {
|
|
437
|
+
"version": 1,
|
|
438
|
+
"status": "credential",
|
|
439
|
+
"headers": {
|
|
440
|
+
"Authorization": "Bearer example-token"
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
"expected": {
|
|
444
|
+
"ok": false,
|
|
445
|
+
"problem": "scope"
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"id": "scope-http",
|
|
450
|
+
"description": "Scopes are https.",
|
|
451
|
+
"request": {
|
|
452
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
453
|
+
"method": "GET"
|
|
454
|
+
},
|
|
455
|
+
"now": 1791000000,
|
|
456
|
+
"answer": {
|
|
457
|
+
"version": 1,
|
|
458
|
+
"status": "credential",
|
|
459
|
+
"headers": {
|
|
460
|
+
"Authorization": "Bearer example-token"
|
|
461
|
+
},
|
|
462
|
+
"scope": "http://registry.example.net/example.com"
|
|
463
|
+
},
|
|
464
|
+
"expected": {
|
|
465
|
+
"ok": false,
|
|
466
|
+
"problem": "scope"
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
"id": "scope-with-query",
|
|
471
|
+
"description": "A scope has no query.",
|
|
472
|
+
"request": {
|
|
473
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
474
|
+
"method": "GET"
|
|
475
|
+
},
|
|
476
|
+
"now": 1791000000,
|
|
477
|
+
"answer": {
|
|
478
|
+
"version": 1,
|
|
479
|
+
"status": "credential",
|
|
480
|
+
"headers": {
|
|
481
|
+
"Authorization": "Bearer example-token"
|
|
482
|
+
},
|
|
483
|
+
"scope": "https://registry.example.net/?publisher=example.com"
|
|
484
|
+
},
|
|
485
|
+
"expected": {
|
|
486
|
+
"ok": false,
|
|
487
|
+
"problem": "scope"
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"id": "scope-excludes-resource",
|
|
492
|
+
"description": "The scope must contain the resource asked for.",
|
|
493
|
+
"request": {
|
|
494
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
495
|
+
"method": "GET"
|
|
496
|
+
},
|
|
497
|
+
"now": 1791000000,
|
|
498
|
+
"answer": {
|
|
499
|
+
"version": 1,
|
|
500
|
+
"status": "credential",
|
|
501
|
+
"headers": {
|
|
502
|
+
"Authorization": "Bearer example-token"
|
|
503
|
+
},
|
|
504
|
+
"scope": "https://registry.example.net/example.org"
|
|
505
|
+
},
|
|
506
|
+
"expected": {
|
|
507
|
+
"ok": false,
|
|
508
|
+
"problem": "scope-excludes-resource"
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"id": "scope-on-a-partial-segment",
|
|
513
|
+
"description": "Containment is on whole segments.",
|
|
514
|
+
"request": {
|
|
515
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
516
|
+
"method": "GET"
|
|
517
|
+
},
|
|
518
|
+
"now": 1791000000,
|
|
519
|
+
"answer": {
|
|
520
|
+
"version": 1,
|
|
521
|
+
"status": "credential",
|
|
522
|
+
"headers": {
|
|
523
|
+
"Authorization": "Bearer example-token"
|
|
524
|
+
},
|
|
525
|
+
"scope": "https://registry.example.net/example.co"
|
|
526
|
+
},
|
|
527
|
+
"expected": {
|
|
528
|
+
"ok": false,
|
|
529
|
+
"problem": "scope-excludes-resource"
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
"id": "scope-other-origin",
|
|
534
|
+
"description": "Another origin never contains the resource.",
|
|
535
|
+
"request": {
|
|
536
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
537
|
+
"method": "GET"
|
|
538
|
+
},
|
|
539
|
+
"now": 1791000000,
|
|
540
|
+
"answer": {
|
|
541
|
+
"version": 1,
|
|
542
|
+
"status": "credential",
|
|
543
|
+
"headers": {
|
|
544
|
+
"Authorization": "Bearer example-token"
|
|
545
|
+
},
|
|
546
|
+
"scope": "https://example.com/"
|
|
547
|
+
},
|
|
548
|
+
"expected": {
|
|
549
|
+
"ok": false,
|
|
550
|
+
"problem": "scope-excludes-resource"
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
"id": "expires-at-not-a-number",
|
|
555
|
+
"description": "expiresAt is Unix seconds.",
|
|
556
|
+
"request": {
|
|
557
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
558
|
+
"method": "GET"
|
|
559
|
+
},
|
|
560
|
+
"now": 1791000000,
|
|
561
|
+
"answer": {
|
|
562
|
+
"version": 1,
|
|
563
|
+
"status": "credential",
|
|
564
|
+
"headers": {
|
|
565
|
+
"Authorization": "Bearer example-token"
|
|
566
|
+
},
|
|
567
|
+
"scope": "https://registry.example.net/example.com",
|
|
568
|
+
"expiresAt": "2026-10-08T00:00:00Z"
|
|
569
|
+
},
|
|
570
|
+
"expected": {
|
|
571
|
+
"ok": false,
|
|
572
|
+
"problem": "expires-at"
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"id": "expired",
|
|
577
|
+
"description": "An expired credential is never attached: expiresAt must be after now.",
|
|
578
|
+
"request": {
|
|
579
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
580
|
+
"method": "GET"
|
|
581
|
+
},
|
|
582
|
+
"now": 1791000000,
|
|
583
|
+
"answer": {
|
|
584
|
+
"version": 1,
|
|
585
|
+
"status": "credential",
|
|
586
|
+
"headers": {
|
|
587
|
+
"Authorization": "Bearer example-token"
|
|
588
|
+
},
|
|
589
|
+
"scope": "https://registry.example.net/example.com",
|
|
590
|
+
"expiresAt": 1791000000
|
|
591
|
+
},
|
|
592
|
+
"expected": {
|
|
593
|
+
"ok": false,
|
|
594
|
+
"problem": "expired"
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
"id": "reason-not-a-string",
|
|
599
|
+
"description": "A decline's reason is a string.",
|
|
600
|
+
"request": {
|
|
601
|
+
"resource": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
602
|
+
"method": "GET"
|
|
603
|
+
},
|
|
604
|
+
"now": 1791000000,
|
|
605
|
+
"answer": {
|
|
606
|
+
"version": 1,
|
|
607
|
+
"status": "declined",
|
|
608
|
+
"reason": [
|
|
609
|
+
"no"
|
|
610
|
+
]
|
|
611
|
+
},
|
|
612
|
+
"expected": {
|
|
613
|
+
"ok": false,
|
|
614
|
+
"problem": "reason"
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
"eraseAnswers": [
|
|
619
|
+
{
|
|
620
|
+
"id": "erased",
|
|
621
|
+
"answer": {
|
|
622
|
+
"version": 1,
|
|
623
|
+
"status": "erased"
|
|
624
|
+
},
|
|
625
|
+
"expected": {
|
|
626
|
+
"ok": true,
|
|
627
|
+
"answer": {
|
|
628
|
+
"status": "erased"
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
"description": "The one erase answer."
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"id": "erased-with-extra-fields",
|
|
635
|
+
"answer": {
|
|
636
|
+
"version": 1,
|
|
637
|
+
"status": "erased",
|
|
638
|
+
"removed": 1
|
|
639
|
+
},
|
|
640
|
+
"expected": {
|
|
641
|
+
"ok": true,
|
|
642
|
+
"answer": {
|
|
643
|
+
"status": "erased"
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
"description": "Extra fields are ignored."
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
"id": "empty-object",
|
|
650
|
+
"answer": {},
|
|
651
|
+
"expected": {
|
|
652
|
+
"ok": false,
|
|
653
|
+
"problem": "version"
|
|
654
|
+
},
|
|
655
|
+
"description": "Empty output is not an answer: erase prints {\"version\": 1, \"status\": \"erased\"}."
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
"id": "wrong-status",
|
|
659
|
+
"answer": {
|
|
660
|
+
"version": 1,
|
|
661
|
+
"status": "declined"
|
|
662
|
+
},
|
|
663
|
+
"expected": {
|
|
664
|
+
"ok": false,
|
|
665
|
+
"problem": "status"
|
|
666
|
+
},
|
|
667
|
+
"description": "erase answers erased, even for a credential it no longer holds."
|
|
668
|
+
}
|
|
669
|
+
],
|
|
670
|
+
"scopes": [
|
|
671
|
+
{
|
|
672
|
+
"id": "origin-contains-every-path",
|
|
673
|
+
"description": "A scope of an origin's root covers every path on it.",
|
|
674
|
+
"scope": "https://registry.example.net/",
|
|
675
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
676
|
+
"contains": true
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
"id": "origin-without-trailing-slash",
|
|
680
|
+
"description": "An origin written without a trailing slash is the same scope.",
|
|
681
|
+
"scope": "https://registry.example.net",
|
|
682
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
683
|
+
"contains": true
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
"id": "publisher-prefix-contains-its-packages",
|
|
687
|
+
"description": "A registry serving many publishers scopes a credential to one publisher's path.",
|
|
688
|
+
"scope": "https://registry.example.net/example.com",
|
|
689
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
690
|
+
"contains": true
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"id": "prefix-contains-itself",
|
|
694
|
+
"description": "The prefix's own path is inside it.",
|
|
695
|
+
"scope": "https://registry.example.net/example.com",
|
|
696
|
+
"url": "https://registry.example.net/example.com",
|
|
697
|
+
"contains": true
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
"id": "trailing-slash-prefix",
|
|
701
|
+
"description": "A prefix written with a trailing slash matches the same paths.",
|
|
702
|
+
"scope": "https://registry.example.net/example.com/",
|
|
703
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
704
|
+
"contains": true
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
"id": "trailing-slash-prefix-contains-its-bare-path",
|
|
708
|
+
"description": "/example.com/ and /example.com name the same segment.",
|
|
709
|
+
"scope": "https://registry.example.net/example.com/",
|
|
710
|
+
"url": "https://registry.example.net/example.com",
|
|
711
|
+
"contains": true
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
"id": "segment-boundary",
|
|
715
|
+
"description": "The prefix matches whole path segments: /example.com never covers /example.com.evil.",
|
|
716
|
+
"scope": "https://registry.example.net/example.com",
|
|
717
|
+
"url": "https://registry.example.net/example.com.evil/core/1.0.0.kan.yml",
|
|
718
|
+
"contains": false
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
"id": "partial-segment",
|
|
722
|
+
"description": "A prefix that ends mid-segment covers nothing beyond that exact segment.",
|
|
723
|
+
"scope": "https://registry.example.net/example",
|
|
724
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
725
|
+
"contains": false
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
"id": "sibling-publisher",
|
|
729
|
+
"description": "Another publisher on the same registry is outside.",
|
|
730
|
+
"scope": "https://registry.example.net/example.com",
|
|
731
|
+
"url": "https://registry.example.net/example.org/core/1.0.0.kan.yml",
|
|
732
|
+
"contains": false
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
"id": "other-host",
|
|
736
|
+
"description": "Another host is outside, so a redirect to a CDN never carries the credential.",
|
|
737
|
+
"scope": "https://registry.example.net/",
|
|
738
|
+
"url": "https://cdn.example.net/example.com/core/1.0.0.kan.yml",
|
|
739
|
+
"contains": false
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
"id": "subdomain-is-another-origin",
|
|
743
|
+
"description": "A subdomain is another origin.",
|
|
744
|
+
"scope": "https://example.com/",
|
|
745
|
+
"url": "https://pkg.example.com/core/1.0.0.kan.yml",
|
|
746
|
+
"contains": false
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
"id": "other-port",
|
|
750
|
+
"description": "Another port is another origin.",
|
|
751
|
+
"scope": "https://example.com/",
|
|
752
|
+
"url": "https://example.com:8443/core/1.0.0.kan.yml",
|
|
753
|
+
"contains": false
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"id": "default-port-is-the-same-origin",
|
|
757
|
+
"description": "An explicit default port is the same origin.",
|
|
758
|
+
"scope": "https://example.com:443/",
|
|
759
|
+
"url": "https://example.com/core/1.0.0.kan.yml",
|
|
760
|
+
"contains": true
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
"id": "host-is-case-insensitive",
|
|
764
|
+
"description": "Hosts compare case-insensitively.",
|
|
765
|
+
"scope": "https://Registry.Example.NET/example.com",
|
|
766
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
767
|
+
"contains": true
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"id": "path-is-case-sensitive",
|
|
771
|
+
"description": "Paths compare case-sensitively.",
|
|
772
|
+
"scope": "https://registry.example.net/Example.com",
|
|
773
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml",
|
|
774
|
+
"contains": false
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
"id": "http-url-is-never-inside",
|
|
778
|
+
"description": "A credential is never attached to plain http.",
|
|
779
|
+
"scope": "https://example.com/",
|
|
780
|
+
"url": "http://example.com/core/1.0.0.kan.yml",
|
|
781
|
+
"contains": false
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
"id": "url-query-is-ignored",
|
|
785
|
+
"description": "A query on the requested URL does not change which path it names.",
|
|
786
|
+
"scope": "https://registry.example.net/example.com",
|
|
787
|
+
"url": "https://registry.example.net/example.com/core/1.0.0.kan.yml?download=1",
|
|
788
|
+
"contains": true
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
"id": "dot-segments-are-resolved-first",
|
|
792
|
+
"description": "Dot segments are resolved before matching: this URL names /example.org/...",
|
|
793
|
+
"scope": "https://registry.example.net/example.com",
|
|
794
|
+
"url": "https://registry.example.net/example.com/../example.org/core/1.0.0.kan.yml",
|
|
795
|
+
"contains": false
|
|
796
|
+
}
|
|
797
|
+
],
|
|
798
|
+
"invalidScopes": [
|
|
799
|
+
{
|
|
800
|
+
"id": "http",
|
|
801
|
+
"scope": "http://example.com/",
|
|
802
|
+
"description": "Only https."
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
"id": "relative",
|
|
806
|
+
"scope": "/example.com",
|
|
807
|
+
"description": "A scope is an absolute URL."
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
"id": "bare-host",
|
|
811
|
+
"scope": "example.com",
|
|
812
|
+
"description": "A host alone is not a URL."
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"id": "query",
|
|
816
|
+
"scope": "https://example.com/?publisher=example.com",
|
|
817
|
+
"description": "A query cannot bound a path prefix."
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
"id": "empty-query",
|
|
821
|
+
"scope": "https://example.com/?",
|
|
822
|
+
"description": "Even an empty query."
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
"id": "fragment",
|
|
826
|
+
"scope": "https://example.com/#core",
|
|
827
|
+
"description": "Nor can a fragment."
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
"id": "userinfo",
|
|
831
|
+
"scope": "https://user@example.com/",
|
|
832
|
+
"description": "A scope never carries userinfo."
|
|
833
|
+
}
|
|
834
|
+
],
|
|
835
|
+
"challenges": [
|
|
836
|
+
{
|
|
837
|
+
"id": "bearer-resource-metadata",
|
|
838
|
+
"description": "RFC 9728 §5.1: a 401 naming the resource's metadata.",
|
|
839
|
+
"wwwAuthenticate": "Bearer resource_metadata=\"https://registry.example.net/.well-known/oauth-protected-resource/example.com\", error=\"invalid_token\"",
|
|
840
|
+
"challenges": [
|
|
841
|
+
{
|
|
842
|
+
"scheme": "Bearer",
|
|
843
|
+
"params": {
|
|
844
|
+
"resource_metadata": "https://registry.example.net/.well-known/oauth-protected-resource/example.com",
|
|
845
|
+
"error": "invalid_token"
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
]
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
"id": "insufficient-scope",
|
|
852
|
+
"description": "RFC 6750 §3.1: valid but not enough; the transport asks again with no erase.",
|
|
853
|
+
"wwwAuthenticate": "Bearer error=\"insufficient_scope\", scope=\"packages:read\"",
|
|
854
|
+
"challenges": [
|
|
855
|
+
{
|
|
856
|
+
"scheme": "Bearer",
|
|
857
|
+
"params": {
|
|
858
|
+
"error": "insufficient_scope",
|
|
859
|
+
"scope": "packages:read"
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
]
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
"id": "bare-scheme",
|
|
866
|
+
"description": "A scheme with no parameters.",
|
|
867
|
+
"wwwAuthenticate": "Bearer",
|
|
868
|
+
"challenges": [
|
|
869
|
+
{
|
|
870
|
+
"scheme": "Bearer",
|
|
871
|
+
"params": {}
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
"id": "two-challenges",
|
|
877
|
+
"description": "Several challenges keep the server's order (RFC 9449 servers send DPoP and Bearer together).",
|
|
878
|
+
"wwwAuthenticate": "DPoP algs=\"ES256 PS256\", Bearer realm=\"example\"",
|
|
879
|
+
"challenges": [
|
|
880
|
+
{
|
|
881
|
+
"scheme": "DPoP",
|
|
882
|
+
"params": {
|
|
883
|
+
"algs": "ES256 PS256"
|
|
884
|
+
}
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"scheme": "Bearer",
|
|
888
|
+
"params": {
|
|
889
|
+
"realm": "example"
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
]
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
"id": "param-names-are-case-insensitive",
|
|
896
|
+
"description": "Parameter names are lower-cased; values are kept.",
|
|
897
|
+
"wwwAuthenticate": "Bearer Error=\"invalid_token\"",
|
|
898
|
+
"challenges": [
|
|
899
|
+
{
|
|
900
|
+
"scheme": "Bearer",
|
|
901
|
+
"params": {
|
|
902
|
+
"error": "invalid_token"
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
"id": "scheme-keeps-its-spelling",
|
|
909
|
+
"description": "The scheme keeps the server's spelling; compare it case-insensitively.",
|
|
910
|
+
"wwwAuthenticate": "bearer error=\"invalid_token\"",
|
|
911
|
+
"challenges": [
|
|
912
|
+
{
|
|
913
|
+
"scheme": "bearer",
|
|
914
|
+
"params": {
|
|
915
|
+
"error": "invalid_token"
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
]
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
"id": "unquoted-token-value",
|
|
922
|
+
"description": "A parameter value may be a token instead of a quoted-string.",
|
|
923
|
+
"wwwAuthenticate": "Bearer error=invalid_token",
|
|
924
|
+
"challenges": [
|
|
925
|
+
{
|
|
926
|
+
"scheme": "Bearer",
|
|
927
|
+
"params": {
|
|
928
|
+
"error": "invalid_token"
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
]
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
"id": "quoted-pair",
|
|
935
|
+
"description": "Backslash escapes inside a quoted-string are removed.",
|
|
936
|
+
"wwwAuthenticate": "Bearer realm=\"the \\\"main\\\" registry\"",
|
|
937
|
+
"challenges": [
|
|
938
|
+
{
|
|
939
|
+
"scheme": "Bearer",
|
|
940
|
+
"params": {
|
|
941
|
+
"realm": "the \"main\" registry"
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
]
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
"id": "comma-inside-quotes",
|
|
948
|
+
"description": "A comma inside a quoted-string separates nothing.",
|
|
949
|
+
"wwwAuthenticate": "Bearer realm=\"a, b\", error=\"invalid_token\"",
|
|
950
|
+
"challenges": [
|
|
951
|
+
{
|
|
952
|
+
"scheme": "Bearer",
|
|
953
|
+
"params": {
|
|
954
|
+
"realm": "a, b",
|
|
955
|
+
"error": "invalid_token"
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
]
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
"id": "whitespace-around-equals",
|
|
962
|
+
"description": "BWS is allowed around '='.",
|
|
963
|
+
"wwwAuthenticate": "Bearer realm = \"example\"",
|
|
964
|
+
"challenges": [
|
|
965
|
+
{
|
|
966
|
+
"scheme": "Bearer",
|
|
967
|
+
"params": {
|
|
968
|
+
"realm": "example"
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
]
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
"id": "token68",
|
|
975
|
+
"description": "A challenge may carry a token68 instead of parameters.",
|
|
976
|
+
"wwwAuthenticate": "Negotiate YIIBhwYGKwYBBQUC",
|
|
977
|
+
"challenges": [
|
|
978
|
+
{
|
|
979
|
+
"scheme": "Negotiate",
|
|
980
|
+
"params": {},
|
|
981
|
+
"token68": "YIIBhwYGKwYBBQUC"
|
|
982
|
+
}
|
|
983
|
+
]
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
"id": "token68-with-padding",
|
|
987
|
+
"description": "A token68's '=' padding is not a parameter.",
|
|
988
|
+
"wwwAuthenticate": "Basic dXNlcjpwYXNz==",
|
|
989
|
+
"challenges": [
|
|
990
|
+
{
|
|
991
|
+
"scheme": "Basic",
|
|
992
|
+
"params": {},
|
|
993
|
+
"token68": "dXNlcjpwYXNz=="
|
|
994
|
+
}
|
|
995
|
+
]
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
"id": "empty-list-elements",
|
|
999
|
+
"description": "Empty list elements are skipped.",
|
|
1000
|
+
"wwwAuthenticate": ", Bearer realm=\"a\" ,, Basic realm=\"b\"",
|
|
1001
|
+
"challenges": [
|
|
1002
|
+
{
|
|
1003
|
+
"scheme": "Bearer",
|
|
1004
|
+
"params": {
|
|
1005
|
+
"realm": "a"
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
"scheme": "Basic",
|
|
1010
|
+
"params": {
|
|
1011
|
+
"realm": "b"
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
]
|
|
1015
|
+
}
|
|
1016
|
+
],
|
|
1017
|
+
"malformedChallenges": [
|
|
1018
|
+
{
|
|
1019
|
+
"id": "unterminated-quoted-string",
|
|
1020
|
+
"wwwAuthenticate": "Bearer realm=\"example",
|
|
1021
|
+
"description": "A quoted-string that never ends."
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
"id": "repeated-parameter",
|
|
1025
|
+
"wwwAuthenticate": "Bearer error=\"a\", error=\"b\"",
|
|
1026
|
+
"description": "RFC 9110: a parameter occurs once per challenge."
|
|
1027
|
+
},
|
|
1028
|
+
{
|
|
1029
|
+
"id": "missing-comma",
|
|
1030
|
+
"wwwAuthenticate": "Bearer realm=\"a\" error=\"b\"",
|
|
1031
|
+
"description": "Parameters are comma-separated."
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
"id": "no-space-after-scheme",
|
|
1035
|
+
"wwwAuthenticate": "Bearer=\"x\"",
|
|
1036
|
+
"description": "The scheme is followed by a space before its parameters."
|
|
1037
|
+
}
|
|
1038
|
+
]
|
|
1039
|
+
}
|