From cba58c00c0d1c137972a62cd91eeb782528d23ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABlle=20Huisman?= Date: Fri, 23 May 2025 21:11:05 +0200 Subject: [PATCH] feat(shield-oidc): add additional provider metadata --- packages/methods/shield-oidc/src/lib.rs | 1 + packages/methods/shield-oidc/src/metadata.rs | 34 ++++++++++++ packages/methods/shield-oidc/src/method.rs | 3 +- packages/methods/shield-oidc/src/provider.rs | 54 ++++++++++++++++---- 4 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 packages/methods/shield-oidc/src/metadata.rs diff --git a/packages/methods/shield-oidc/src/lib.rs b/packages/methods/shield-oidc/src/lib.rs index e597c44..b3ce963 100644 --- a/packages/methods/shield-oidc/src/lib.rs +++ b/packages/methods/shield-oidc/src/lib.rs @@ -2,6 +2,7 @@ mod builders; mod claims; mod client; mod connection; +mod metadata; mod method; mod provider; mod session; diff --git a/packages/methods/shield-oidc/src/metadata.rs b/packages/methods/shield-oidc/src/metadata.rs new file mode 100644 index 0000000..768d4f1 --- /dev/null +++ b/packages/methods/shield-oidc/src/metadata.rs @@ -0,0 +1,34 @@ +use openidconnect::{ + AdditionalProviderMetadata, IntrospectionUrl, ProviderMetadata, RevocationUrl, + core::{ + CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClientAuthMethod, CoreGrantType, + CoreJsonWebKey, CoreJweContentEncryptionAlgorithm, CoreJweKeyManagementAlgorithm, + CoreResponseMode, CoreResponseType, CoreSubjectIdentifierType, + }, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct NonStandardProviderMetadata { + #[serde(default)] + pub introspection_endpoint: Option, + #[serde(default)] + pub revocation_endpoint: Option, +} + +impl AdditionalProviderMetadata for NonStandardProviderMetadata {} + +pub type OidcProviderMetadata = ProviderMetadata< + NonStandardProviderMetadata, + CoreAuthDisplay, + CoreClientAuthMethod, + CoreClaimName, + CoreClaimType, + CoreGrantType, + CoreJweContentEncryptionAlgorithm, + CoreJweKeyManagementAlgorithm, + CoreJsonWebKey, + CoreResponseMode, + CoreResponseType, + CoreSubjectIdentifierType, +>; diff --git a/packages/methods/shield-oidc/src/method.rs b/packages/methods/shield-oidc/src/method.rs index b33d044..204478b 100644 --- a/packages/methods/shield-oidc/src/method.rs +++ b/packages/methods/shield-oidc/src/method.rs @@ -425,8 +425,7 @@ impl Method for OidcMethod { _session: Session, options: &ShieldOptions, ) -> Result { - // TODO: Revocation URL is always `EndpointNotSet` when using `from_provider_metadata`, - // because `ProviderMetadata` does not support `introspection_endpoint` and `revocation_endpoint`. + // TODO: See [`OidcProvider::oidc_client`]. // let provider = match request.provider_id { // Some(provider_id) => self.oidc_provider_by_id_or_slug(&provider_id).await?, diff --git a/packages/methods/shield-oidc/src/provider.rs b/packages/methods/shield-oidc/src/provider.rs index 1d5b3e1..7b51d0e 100644 --- a/packages/methods/shield-oidc/src/provider.rs +++ b/packages/methods/shield-oidc/src/provider.rs @@ -1,19 +1,22 @@ use bon::Builder; use openidconnect::{ - AuthUrl, Client, ClientId, ClientSecret, EmptyAdditionalClaims, - EmptyAdditionalProviderMetadata, EndpointMaybeSet, EndpointNotSet, EndpointSet, IssuerUrl, - JsonWebKeySet, JsonWebKeySetUrl, ProviderMetadata, RedirectUrl, StandardErrorResponse, - TokenUrl, UserInfoUrl, + AuthUrl, Client, ClientId, ClientSecret, EmptyAdditionalClaims, EndpointMaybeSet, + EndpointNotSet, EndpointSet, IntrospectionUrl, IssuerUrl, JsonWebKeySet, JsonWebKeySetUrl, + RedirectUrl, RevocationUrl, StandardErrorResponse, TokenUrl, UserInfoUrl, core::{ CoreAuthDisplay, CoreAuthPrompt, CoreClient, CoreErrorResponseType, CoreGenderClaim, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, - CoreProviderMetadata, CoreRevocableToken, CoreRevocationErrorResponse, - CoreTokenIntrospectionResponse, CoreTokenResponse, + CoreRevocableToken, CoreRevocationErrorResponse, CoreTokenIntrospectionResponse, + CoreTokenResponse, }, }; use shield::{ConfigurationError, Provider}; -use crate::{client::async_http_client, method::OIDC_METHOD_ID}; +use crate::{ + client::async_http_client, + metadata::{NonStandardProviderMetadata, OidcProviderMetadata}, + method::OIDC_METHOD_ID, +}; type OidcClient = Client< EmptyAdditionalClaims, @@ -83,7 +86,7 @@ impl OidcProvider { let async_http_client = async_http_client()?; let provider_metadata = if let Some(discovery_url) = &self.discovery_url { - CoreProviderMetadata::discover_async( + OidcProviderMetadata::discover_async( // TODO: Consider stripping `/.well-known/openid-configuration` so `openidconnect` doesn't error. IssuerUrl::new(discovery_url.clone()) .map_err(|err| ConfigurationError::Invalid(err.to_string()))?, @@ -92,7 +95,7 @@ impl OidcProvider { .await .map_err(|err| ConfigurationError::Invalid(err.to_string()))? } else { - let mut provider_metadata = ProviderMetadata::new( + let mut provider_metadata = OidcProviderMetadata::new( IssuerUrl::new( self.issuer_url .clone() @@ -128,7 +131,24 @@ impl OidcProvider { CoreJwsSigningAlgorithm::EdDsa, CoreJwsSigningAlgorithm::None, ], - EmptyAdditionalProviderMetadata {}, + NonStandardProviderMetadata { + introspection_endpoint: self + .introspection_url + .as_ref() + .map(|introspection_url| { + IntrospectionUrl::new(introspection_url.clone()) + .map_err(|err| ConfigurationError::Invalid(err.to_string())) + }) + .transpose()?, + revocation_endpoint: self + .revocation_url + .as_ref() + .map(|revocation_url| { + RevocationUrl::new(revocation_url.clone()) + .map_err(|err| ConfigurationError::Invalid(err.to_string())) + }) + .transpose()?, + }, ); provider_metadata = provider_metadata.set_jwks( @@ -160,6 +180,18 @@ impl OidcProvider { self.client_secret.clone().map(ClientSecret::new), ); + // TODO: Upstream: _option version of these (and other) functions which set the type to EndpointMaybeSet. + + // if let Some(introspection_endpoint) = provider_metadata + // .additional_metadata() + // .introspection_endpoint + // { + // client = client.set_introspection_url(introspection_endpoint); + // } + // if let Some(revocation_url) = provider_metadata.additional_metadata().revocation_endpoint { + // client = client.set_introspection_url(revocation_url); + // } + if let Some(redirect_url) = &self.redirect_url { client = client.set_redirect_uri( RedirectUrl::new(redirect_url.clone()) @@ -167,7 +199,7 @@ impl OidcProvider { ); } - // TODO: Common client options. + // TODO: Client options. Ok(client) }