@@ -114,7 +114,7 @@ apply to all parts of this PEP's specification:
114114 clients **MUST ** reject any URLs that do not meet this constraint.
115115
116116 In practice, this means that a discovery request to
117- ``https://upload.example.com/.well-known/pytp/ {key} `` can only
117+ ``https://upload.example.com/.well-known/pytp?discover= {key} `` can only
118118 return URLs with the ``upload.example.com `` host.
119119
120120* All client requests **SHOULD ** have an
@@ -157,39 +157,57 @@ The discovery mechanism is as follows:
157157 For the above example, the path component is
158158 ``/legacy/ ``.
159159
160- 3. The uploading client takes the SHA2-256 hash of the path component,
161- producing the *discovery key *.
160+ 3. The uploading client performs a query-safe URL encoding of the path component
161+ (i.e. percent-encoding as defined in :rfc: `3986 `, including encoding
162+ of forward slashes and spaces), producing the *discovery key *.
162163
163164 For the above example, the discovery key is
164- ``0cace9579789849db6e16d48df183951c8f17582200d84bc93c7678d6c8f78a7 ``. [#fn-hash ]_
165+ ``%2Flegacy%2F ``. [#fn-discovery-key ]_
165166
1661674. The uploading client constructs a *discovery URL * by taking the
167168 scheme and authority components (as defined in :rfc: `3986 `)
168- of the upload URL and appending ``/.well-known/pytp/ ``
169- and the discovery key.
169+ of the upload URL and appending ``/.well-known/pytp `` as the path.
170+ Then, the uploading client appends the discovery key as the value
171+ of the ``discover `` query parameter.
170172
171173 For the above example, the discovery URL is
172- ``https://upload.example.com/.well-known/pytp/af030c06750716b1b35852298fe852b90def13dcbd012a5fe5148470f1206bfc ``.
174+ ``https://upload.example.com/.well-known/pytp?discover=%2Flegacy%2F ``.
173175
1741765. The uploading client performs an HTTP GET request to the discovery URL.
175177
1761786. The server responds with a ``200 OK `` status code and a body
177179 containing a JSON object if the index supports Trusted Publishing
178- for the given upload URL. The JSON object **MUST ** contain the following
180+ for the given upload URL.
181+
182+ The JSON object **MUST ** contain the following
179183 fields:
180184
181185 - ``audience-endpoint ``: a string containing the URL of the OIDC
182186 audience endpoint to be used during token exchange.
183187 - ``token-mint-endpoint ``: a string containing the URL of the
184188 token minting endpoint to be used during token exchange.
185189
190+ Additionally, the JSON object **MAY ** contain the following fields:
191+
192+ - ``features ``: an array of strings indicating optional features
193+ supported by the index's Trusted Publishing implementation.
194+ The set of possible features is defined under `<Feature Negotiation _>`__.
195+
196+ - ``default-features ``: an array of strings indicating the default
197+ features used by the index's Trusted Publishing implementation
198+ if a request does not explicitly specify any features.
199+ If the ``default-features `` field is not present, the uploading client
200+ **MUST ** assume a default of ``["multi-use-token"] ``.
201+
186202 For the above example, a valid response body would be:
187203
188204 .. code-block :: json
189205
190206 {
191207 "audience-endpoint" : " https://upload.example.com/_/oidc/audience" ,
192- "token-mint-endpoint" : " https://upload.example.com/_/oidc/mint-token"
208+ "token-mint-endpoint" : " https://upload.example.com/_/oidc/mint-token" ,
209+ "features" : [" single-use-token" , " multi-use-token" ],
210+ "default-features" : [" multi-use-token" ]
193211 }
194212
195213 If the server does not support Trusted Publishing for the given
@@ -250,6 +268,20 @@ POST request **MUST** be a JSON object containing the following:
250268
251269- ``token ``: a string containing the identity credential
252270 obtained from the Trusted Publishing provider.
271+ - ``features ``: an **optional ** array of strings
272+ indicating the desired features for the minted upload credential.
273+ If this field is not provided by the client, the server **MUST ** use
274+ its own default features as specified in the
275+ ``default-features `` field during discovery.
276+
277+ For example, a valid request body would be:
278+
279+ .. code-block :: json
280+
281+ {
282+ "token" : " ey..." ,
283+ "features" : [" single-use-token" ]
284+ }
253285
254286 On success, the server responds with a ``200 OK `` status code and a body
255287containing a JSON object with the following fields:
@@ -275,6 +307,32 @@ containing a JSON object with the following fields:
275307On failure, the server **MUST ** respond with any standard HTTP
276308error code in the 400 or 500 range to indicate the appropriate error condition.
277309
310+ Feature Negotiation
311+ ~~~~~~~~~~~~~~~~~~~
312+
313+ The protocol defined in this PEP supports an *optional * mechanism for
314+ negotiating non-default features between the uploading client and the
315+ receiving index server. These features are advertised as an array of
316+ strings in the ``features `` field of the discovery response; the client
317+ can then request one or more features by including them in the ``features ``
318+ field of the token minting request.
319+
320+ The following features are defined:
321+
322+ - ``single-use-token ``: the tokens minted by the index server
323+ **MUST ** be single-use tokens. In other words, the token returned
324+ by the token minting endpoint **MUST ** only be usable for a single
325+ upload operation. Any subsequent upload attempts using the same
326+ token **MUST ** be rejected by the index server. Clients that request
327+ the ``single-use-token `` feature **MUST ** be prepared to perform
328+ multiple token minting operations if multiple upload operations
329+ are needed.
330+
331+ - ``multi-use-token ``: the tokens minted by the index server
332+ **MUST ** be multi-use tokens. In other words, the token returned
333+ by the token minting endpoint **MAY ** be usable for multiple
334+ upload operations until it expires.
335+
278336Security Implications
279337=====================
280338
@@ -389,17 +447,17 @@ This approach too has downsides:
389447Footnotes
390448=========
391449
392- .. [#fn-hash ]
450+ .. [#fn-discovery-key ]
393451
394452 The discovery key may be computed thusly:
395453
396454 .. code-block :: pycon
397455
398- >>> import hashlib
456+ >>> import urllib.parse
399457 >>> path = "/legacy/"
400- >>> key = hashlib.sha256(path.encode("utf-8")).hexdigest( )
458+ >>> key = urllib.parse.quote_plus(path )
401459 >>> print(key)
402- 0cace9579789849db6e16d48df183951c8f17582200d84bc93c7678d6c8f78a7
460+ '%2Flegacy%2F'
403461
404462 .. [#fn-oidc ] Widely used CI/CD and cloud providers variously implement "ambient"
405463 OIDC token retrieval mechanisms that aren't standardized.
0 commit comments