From 0ef6851219c58d52263fd96cb87c18e72a928fd2 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Tue, 6 May 2025 17:19:15 -0500 Subject: [PATCH] Make the audience claim in compute JWTs a vector (#11845) According to RFC 7519, `aud` is generally an array of StringOrURI, but in special cases may be a single StringOrURI value. To accomodate future control plane work where a single token may work for multiple services, make the claim a vector. Link: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3 Signed-off-by: Tristan Partin --- compute_tools/src/http/middleware/authorize.rs | 2 +- control_plane/src/endpoint.rs | 4 ++-- libs/compute_api/src/requests.rs | 7 +++++-- test_runner/regress/test_compute_http.py | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/compute_tools/src/http/middleware/authorize.rs b/compute_tools/src/http/middleware/authorize.rs index 2afc57ad9c..a82f46e062 100644 --- a/compute_tools/src/http/middleware/authorize.rs +++ b/compute_tools/src/http/middleware/authorize.rs @@ -79,7 +79,7 @@ impl AsyncAuthorizeRequest for Authorize { )); }; - if audience != COMPUTE_AUDIENCE { + if !audience.iter().any(|a| a == COMPUTE_AUDIENCE) { return Err(JsonResponse::error( StatusCode::UNAUTHORIZED, "invalid audience in authorization token claims", diff --git a/control_plane/src/endpoint.rs b/control_plane/src/endpoint.rs index fe6a93eb5e..be73661a3c 100644 --- a/control_plane/src/endpoint.rs +++ b/control_plane/src/endpoint.rs @@ -635,8 +635,8 @@ impl Endpoint { pub fn generate_jwt(&self, scope: Option) -> Result { self.env.generate_auth_token(&ComputeClaims { audience: match scope { - Some(ComputeClaimsScope::Admin) => Some(COMPUTE_AUDIENCE.to_owned()), - _ => Some(self.endpoint_id.clone()), + Some(ComputeClaimsScope::Admin) => Some(vec![COMPUTE_AUDIENCE.to_owned()]), + _ => None, }, compute_id: match scope { Some(ComputeClaimsScope::Admin) => None, diff --git a/libs/compute_api/src/requests.rs b/libs/compute_api/src/requests.rs index 40d34eccea..bbab271474 100644 --- a/libs/compute_api/src/requests.rs +++ b/libs/compute_api/src/requests.rs @@ -10,9 +10,9 @@ use crate::spec::{ComputeSpec, ExtVersion, PgIdent}; /// The value to place in the [`ComputeClaims::audience`] claim. pub static COMPUTE_AUDIENCE: &str = "compute"; +/// Available scopes for a compute's JWT. #[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "snake_case")] -/// Available scopes for a compute's JWT. pub enum ComputeClaimsScope { /// An admin-scoped token allows access to all of `compute_ctl`'s authorized /// facilities. @@ -48,8 +48,11 @@ pub struct ComputeClaims { /// /// See [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3) for /// more information. + /// + /// TODO: Remove the [`Option`] wrapper when control plane learns to send + /// the claim. #[serde(rename = "aud")] - pub audience: Option, + pub audience: Option>, } /// Request of the /configure API diff --git a/test_runner/regress/test_compute_http.py b/test_runner/regress/test_compute_http.py index ce31ff0fe6..9846d44ce2 100644 --- a/test_runner/regress/test_compute_http.py +++ b/test_runner/regress/test_compute_http.py @@ -56,9 +56,9 @@ def test_compute_admin_scope_claim(neon_simple_env: NeonEnv, audience: str | Non endpoint = env.endpoints.create_start("main") - data = {"scope": str(ComputeClaimsScope.ADMIN)} + data: dict[str, str | list[str]] = {"scope": str(ComputeClaimsScope.ADMIN)} if audience: - data["aud"] = audience + data["aud"] = [audience] token = jwt.encode(data, env.auth_keys.priv, algorithm="EdDSA")