@@ -255,8 +255,8 @@ The choice of the root endpoint is left up to the index operator.
255255
256256.. _authentication :
257257
258- Authentication for Upload 2.0 API
259- ----------------------------------
258+ Authentication and Authorization
259+ --------------------------------
260260
261261All endpoints in this specification **MUST ** use standard HTTP authentication
262262mechanisms as defined in :rfc: `7235 `.
@@ -271,6 +271,35 @@ Authentication follows the standard HTTP pattern:
271271The specific authentication schemes (e.g., Bearer, Basic, Digest)
272272are determined by the index operator.
273273
274+ Authentication establishes the principal making a request. Authorization determines whether that principal
275+ may act on a particular session. All session endpoints defined in this specification -- that is, the URLs
276+ returned under the ``links `` key when a :ref: `publishing session <publishing-session-response >` or :ref: `file
277+ upload session <file-upload-session-response>` is created -- **MUST ** be authorized against the project's
278+ upload permissions. Specifically, a server **MUST ** verify, contemporaneously on each request, that the
279+ authenticated principal is currently authorized to upload to the project named by the session, and **MUST **
280+ respond with ``403 Forbidden `` if it is not.
281+
282+ Because this check is performed independently on each request, a session is **not ** tied to the exact
283+ credentials that created it:
284+
285+ - A principal that is granted upload permission after a session is opened may immediately participate in that
286+ session.
287+ - A principal whose upload permission is revoked while a session is open **MUST ** be denied with a
288+ ``403 Forbidden `` on any subsequent request, even if that principal created the session.
289+
290+ This denial is evaluated per request and is not "sticky": if a principal's permission is later restored, its
291+ subsequent requests are authorized again. An index **MAY ** apply a stricter policy, but this specification
292+ does not require one.
293+
294+ Servers **MUST ** perform this authorization check on at least every request that creates, modifies, completes,
295+ extends, cancels, or publishes a publishing session or file upload session. For upload mechanisms that
296+ transfer a file across more than one request (for example, chunked or multipart mechanisms), servers
297+ **SHOULD ** authorize each such request.
298+
299+ The unguessable :ref: `stage preview URL <staged-preview >` is a separate capability and is deliberately **not **
300+ governed by this authorization check; it grants read-only preview access to any client that holds the token,
301+ so that (for example) a CI job can install-test a staged release without project upload credentials.
302+
274303
275304.. _session-errors :
276305
@@ -374,9 +403,15 @@ the "regular" (i.e. :ref:`unstaged <staged-preview>`) access protocols,
374403If this first-release stage gets canceled,
375404then the index **SHOULD ** delete the project record, as if it were never uploaded.
376405
377- The session is owned by the user that created it,
378- and all subsequent requests **MUST ** be performed with the same credentials,
379- otherwise a ``403 Forbidden `` will be returned on those subsequent requests.
406+ A publishing session is **not ** bound to the specific credentials that created it. Instead, every request
407+ against the session **MUST ** be performed by an authenticated principal that is authorized to upload to the
408+ project at the time of that request, as described in :ref: `authentication `. A request from a principal that
409+ is not, or is no longer, so authorized **MUST ** receive a ``403 Forbidden ``.
410+
411+ For a first-release session on a project that does not yet exist, there are no existing project upload
412+ permissions to evaluate; the index instead authorizes the request according to its own name-registration
413+ policy, and **SHOULD ** treat the creating principal (and, where applicable, an organization it acts on behalf
414+ of) as authorized for the lifetime of the session.
380415
381416.. _index-specific-metadata :
382417
@@ -483,15 +518,18 @@ Multiple Session Creation Requests
483518++++++++++++++++++++++++++++++++++
484519
485520If a second attempt to create a session is received for the same name-version pair while a session for that
486- pair is still ``pending ``, then a new session is *not * created.
487- Instead, the server **MUST ** respond with a ``409 Conflict `` and **MUST ** include a ``Location `` header that
488- points to the :ref: `session status URL <publishing-session-status >`. Subsequent session creation requests
489- **MUST ** be performed with the same credentials as the pending session, otherwise a ``403 Forbidden `` is
490- returned.
521+ pair is still ``pending ``, then a new session is *not * created. Instead, the server **MUST ** respond with a
522+ ``409 Conflict `` and **MUST ** include a ``Location `` header that points to the :ref: `session status URL
523+ <publishing-session-status>`. Like every other session request, such a request **MUST ** be performed by a
524+ principal authorized to upload to the project (see :ref: `authentication `); a request from an unauthorized
525+ principal **MUST ** receive a ``403 Forbidden `` instead, which takes precedence over the ``409 Conflict `` so
526+ that the existence of the pending session is not disclosed. An authorized principal receives the ``409
527+ Conflict `` and ``Location `` header and may use the referenced session; this is how multiple authorized
528+ publishers (for example, distinct Trusted Publishing workflows) can contribute to the same session.
491529
492530Otherwise -- that is, when no session for that name-version pair is currently ``pending `` -- a new session is
493- created with the same ``201 Created `` response and payload, except that the :ref: `publishing session status URL
494- <publishing-session-status>`, ``session-token ``, and ``links.stage `` values **MUST ** be different.
531+ created with the same ``201 Created `` response and payload, except that the :ref: `publishing session status
532+ URL <publishing-session-status>`, ``session-token ``, and ``links.stage `` values **MUST ** be different.
495533
496534.. _publishing-session-links :
497535
@@ -1275,10 +1313,20 @@ rate limiting either the legacy API or Upload 2.0 API for empty packages or sess
12751313which supports organizations or :pep: `752 `-style implicit namespaces, could implement different rate limiting
12761314rules for different actors. Such implementations are left as index-specific policy decisions.
12771315
1278- It's possible for a user with upload permissions at the start of a session to lose such permissions while a
1279- session is open and before the entire session is published. Indexes should consider re-validating permissions
1280- at key points during the session lifecycle, such as on artifact uploads, file upload session completion,
1281- session extension requests, or at publish time.
1316+ Session access is authorized contemporaneously rather than being bound to the credentials that created the
1317+ session (see :ref: `authentication `). Indexes **MUST ** re-validate authorization on each session request --
1318+ including artifact uploads, file upload session completion, session extension requests, and publishing -- so
1319+ that a principal that loses upload permission while a session is open is denied on its subsequent requests,
1320+ and a principal that gains permission may join an open session.
1321+
1322+ This model has two consequences worth calling out. First, because every mutating operation is authorized
1323+ uniformly, any principal currently authorized to upload to the project may add to, cancel, or publish another
1324+ principal's open session. The blast radius is limited to the unpublished staging session, since published
1325+ artifacts are immutable and publishing is atomic. Second, the :ref: `stage preview URL <staged-preview >` is a
1326+ capability that is *not * gated by upload permission, so a principal whose permission is revoked mid-session --
1327+ but who has already obtained the stage URL -- retains read-only preview access to the staged files until the
1328+ session is published or canceled. This is a narrow and accepted limitation; an index that considers it a
1329+ concern can mitigate it by canceling the affected session, or by limiting session lifetimes and extensions.
12821330
12831331Staged releases, while useful for testing and embargoes, do provide some potential for larger scale hosting of
12841332malware which isn't detectable by third party, external scanning tools, because staged artifacts are only
@@ -1437,6 +1485,10 @@ Change History
14371485 for the same name-version pair is still ``pending ``. The previous wording referenced non-existent
14381486 ``processing ``/``complete `` publishing-session states.
14391487 * Clarify the wording of the **Multiple Sessions ** client recommendation example.
1488+ * Relax session access from the exact creating credentials to any principal authorized to upload to the
1489+ project, evaluated contemporaneously on each request. Adds an **Authentication and Authorization ** model,
1490+ handles permission changes mid-session, supports rotating Trusted Publishing tokens and multiple
1491+ publishers contributing to one session, and notes the related security implications.
14401492
14411493* `07-Dec-2025 <https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-2/101483/35 >`__
14421494
0 commit comments