From 2032280cc8a5a1d4f6617561ca7c77a598d9f5bd Mon Sep 17 00:00:00 2001 From: dervoeti Date: Wed, 25 Feb 2026 21:18:42 +0100 Subject: [PATCH] feat: add support for specifying a clientAuthenticationMethod for OIDC --- .../stackable-operator/crds/DummyCluster.yaml | 10 +++ .../src/crd/authentication/oidc/mod.rs | 63 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/crates/stackable-operator/crds/DummyCluster.yaml b/crates/stackable-operator/crds/DummyCluster.yaml index e630b8b0e..7f2e06039 100644 --- a/crates/stackable-operator/crds/DummyCluster.yaml +++ b/crates/stackable-operator/crds/DummyCluster.yaml @@ -30,6 +30,16 @@ spec: description: This field contains OIDC-specific configuration. It is only required in case OIDC is used. nullable: true properties: + clientAuthenticationMethod: + default: client_secret_basic + description: 'The client authentication method used when communicating with the token endpoint. Defaults to `client_secret_basic`. The required contents of `clientCredentialsSecret` depend on the chosen method: secret-based methods (`client_secret_basic`, `client_secret_post`, `client_secret_jwt`) expect a client secret, while `private_key_jwt` expects a private key.' + enum: + - client_secret_basic + - client_secret_post + - client_secret_jwt + - private_key_jwt + - none + type: string clientCredentialsSecret: description: |- A reference to the OIDC client credentials secret. The secret contains diff --git a/crates/stackable-operator/src/crd/authentication/oidc/mod.rs b/crates/stackable-operator/src/crd/authentication/oidc/mod.rs index dd7aa35aa..10c7b436c 100644 --- a/crates/stackable-operator/src/crd/authentication/oidc/mod.rs +++ b/crates/stackable-operator/src/crd/authentication/oidc/mod.rs @@ -90,6 +90,50 @@ pub mod versioned { Keycloak, } + /// OAuth2 client authentication methods as defined in the OpenID Connect Core spec. + /// These methods are used by clients to authenticate to the authorization server + /// when using the token endpoint. + /// + /// See for details. + #[derive( + Clone, + Copy, + Debug, + Default, + Deserialize, + Eq, + Hash, + JsonSchema, + Ord, + PartialEq, + PartialOrd, + Serialize, + )] + #[serde(rename_all = "snake_case")] + pub enum ClientAuthenticationMethod { + /// Authenticate using HTTP Basic authentication with client_id and client_secret. + /// This is the default method according to the OIDC spec. + #[default] + #[serde(rename = "client_secret_basic")] + ClientSecretBasic, + + /// Send client_id and client_secret in the request body. + #[serde(rename = "client_secret_post")] + ClientSecretPost, + + /// Authenticate using a JWT signed with an HMAC SHA algorithm using the client_secret. + #[serde(rename = "client_secret_jwt")] + ClientSecretJwt, + + /// Authenticate using a JWT signed with the client's private key. + #[serde(rename = "private_key_jwt")] + PrivateKeyJwt, + + /// No client authentication (for public clients or implicit flow). + #[serde(rename = "none")] + None, + } + /// OIDC specific config options. These are set on the product config level. #[derive( Clone, Debug, Deserialize, Eq, Hash, JsonSchema, Ord, PartialEq, PartialOrd, Serialize, @@ -111,6 +155,25 @@ pub mod versioned { #[serde(default)] pub extra_scopes: Vec, + /// The OAuth2 client authentication method to use for token endpoint requests. + /// Defaults to [`ClientAuthenticationMethod::ClientSecretBasic`]. + /// + /// The contents and format of the `clientCredentialsSecret` are dependent on the selected + /// method. For example, [`ClientAuthenticationMethod::ClientSecretBasic`] and + /// [`ClientAuthenticationMethod::ClientSecretPost`] require a client secret string, whereas + /// [`ClientAuthenticationMethod::PrivateKeyJwt`] requires a private key. + /// + /// See [`ClientAuthenticationMethod`] for available options. + #[schemars( + description = "The client authentication method used when communicating with the token \ + endpoint. Defaults to `client_secret_basic`. The required contents of \ + `clientCredentialsSecret` depend on the chosen method: secret-based methods \ + (`client_secret_basic`, `client_secret_post`, `client_secret_jwt`) expect a client \ + secret, while `private_key_jwt` expects a private key." + )] + #[serde(default)] + pub client_authentication_method: ClientAuthenticationMethod, + // If desired, operators can add custom fields that are only needed for this specific product. // They need to create a struct holding them and pass that as `T`. #[serde(flatten)]