Cargo's current system of having the registry
token sent verbatim in the
Authorization header is too flexible and makes it too easy to build insecure registries. This RFC restricts the form that the registry
token can take to either JWTs or asymmetric tokens. For compatibility, the restrictions only apply to authenticated private registries using RFC#3139.
The Cargo team is concerned about stabilizing RFC#3139 as-is, since it allows private registry implementations to send any value in the
Authorization header, which can lead to an insecure registry.
RFC#3231 defines a completely new authentication scheme using asymmetric tokens that is significantly more secure but is difficult to integrate into existing authentication systems. To support these existing authentication systems, Cargo should also support short-lived tokens. In particular, Cargo should be compatible with systems like GitHub OIDC to allow CI builds to publish crates to a private registry without using shared secrets.
As a compromise between allowing arbitrary tokens and requiring asymmetric tokens, this RFC proposes allowing JWTs to be sent in the
Authorization header in addition to asymmetric tokens. JWTs are used by multiple large identity providers and can have short expiration times that make them less dangerous if leaked.
Tokens for private Cargo registries must be either asymmetric tokens as defined by RFC#3231, or JWTs (JSON Web Tokens).
Cargo will validate that the token for a registry is a JWT by inspecting the header portion of the token. The expiration claim must be set so Cargo can validate that the token is not expired and has a validity period of less than XX days.
Cargo will restrict the forms of tokens used by authenticated private registries (as defined by RFC#3139) to either JWTs (JSON Web Tokens) or asymmetric tokens.
The validation of the token will be performed when Cargo detects an authenticated private registry by either the presence of
auth-required: true in
config.json or the registry sending an
HTTP 401 when accessing
config.json (for sparse registries).
If the registry is configured to use asymmetric tokens as defined by RFC#3231, the request can continue. Otherwise Cargo will validate that the token as a JWT.
To validate a JWT, Cargo first remove the
Bearer prefix from the token. The remainder of the token will be parsed as either a JWS (JSON Web Signature), or JWE (JSON Web Encryption).
For JWS, the token must be of the form:
[header].[payload].[signature]. Cargo will decode the header portion as a JSON object and validate the following:
algmust not be
Cargo will validate the payload portion as follows
expclaim must be set to a date not more than XX (subject to bikeshedding) days in the future.
nbfclaim (if present) is set to a date in the past.
For JWE, the token must be of the form:
Cargo will decode the header portion as a JSON object and validate the following:
algmust not be
expmust be set to a date not more than XX (subject to bikeshedding) days in the future. This claim must be replicated into the header so that it can be decoded by Cargo.
Cargo does not perform any cryptographic validation of the token.
This restricts the tokens that Cargo will allow for private registries. Registries that have existing authentication systems that are not based on JWTs will need to either migrate to JWTs or asymmetric tokens.
Requiring short expiration times means users will need to rotate tokens frequently or use a credential provider to generate them.
Rationale and alternatives
This proposal is fundamentally a compromise between allowing the token to be any value, and requiring registries to use asymmetric tokens.
Alternatives considered include:
- Stabilize sending any token to a private registry. We can stabilize a supported asymmetric scheme when it's ready. Pro: fast and every registry happy. Con: registries can do insecure things.
- When RFC#3231 is ready, stabilize requiring its use to use RFC#3139. Pro: does not allow insecure registry implementations. Con: large registry providers unhappy, not great support for GitHub OIDC.
- Redesign RFC#3231 to use JWTs and be compatible with GitHub OIDC. Con: not a small project.
- Require that tokens for private registries come from a credential provider. Pro: ensures long-lived tokens are more securely stored. Con: doesn't address the registry storing the tokens insecurely.
NuGet, NPM, Python, and Maven all allow long-lived tokens to be used. NuGet and Python both support credential providers to generate short-lived tokens automatically.
How many days is reasonable for expiration?
Should token formats other than JWT be allowed?
Since this design encourages short-lived tokens, users will need to be able to easily generate them. For CI pipelines, there is usually a token already available. However, for developer machines, users will want to set up a credential process that can generate the short lived tokens from their identity provider. The credential process feature is currently unstable and could be extended to better support generating short-lived tokens.