feat: add additional constructors for OidcClient

Adds `discover_new_with_client` and `from_provider_metadata` functions
to `OidcClient` to allow for custom client configurations as requested
in #12.
This commit is contained in:
Paul Zinselmeyer 2024-05-17 16:09:25 +02:00
parent 43406661f6
commit c9f63180b3
Signed by: pfzetto
GPG key ID: B471A1AF06C895FD

View file

@ -14,9 +14,9 @@ use openidconnect::{
CoreRevocationErrorResponse, CoreSubjectIdentifierType, CoreTokenIntrospectionResponse, CoreRevocationErrorResponse, CoreSubjectIdentifierType, CoreTokenIntrospectionResponse,
CoreTokenType, CoreTokenType,
}, },
reqwest::async_http_client, AccessToken, ClientId, ClientSecret, CsrfToken, EmptyExtraTokenFields, HttpRequest,
AccessToken, ClientId, ClientSecret, CsrfToken, EmptyExtraTokenFields, IdTokenFields, HttpResponse, IdTokenFields, IssuerUrl, Nonce, PkceCodeVerifier, RefreshToken,
IssuerUrl, Nonce, PkceCodeVerifier, RefreshToken, StandardErrorResponse, StandardTokenResponse, StandardErrorResponse, StandardTokenResponse,
}; };
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
@ -72,7 +72,7 @@ type Client<AC> = openidconnect::Client<
CoreRevocationErrorResponse, CoreRevocationErrorResponse,
>; >;
type ProviderMetadata = openidconnect::ProviderMetadata< pub type ProviderMetadata = openidconnect::ProviderMetadata<
AdditionalProviderMetadata, AdditionalProviderMetadata,
CoreAuthDisplay, CoreAuthDisplay,
CoreClientAuthMethod, CoreClientAuthMethod,
@ -103,17 +103,14 @@ pub struct OidcClient<AC: AdditionalClaims> {
} }
impl<AC: AdditionalClaims> OidcClient<AC> { impl<AC: AdditionalClaims> OidcClient<AC> {
/// create a new [`OidcClient`] by fetching the required information from the /// create a new [`OidcClient`] from an existing [`ProviderMetadata`].
/// `/.well-known/openid-configuration` endpoint of the issuer. pub fn from_provider_metadata(
pub async fn discover_new( provider_metadata: ProviderMetadata,
application_base_url: Uri, application_base_url: Uri,
issuer: String,
client_id: String, client_id: String,
client_secret: Option<String>, client_secret: Option<String>,
scopes: Vec<String>, scopes: Vec<String>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let provider_metadata =
ProviderMetadata::discover_async(IssuerUrl::new(issuer)?, async_http_client).await?;
let end_session_endpoint = provider_metadata let end_session_endpoint = provider_metadata
.additional_metadata() .additional_metadata()
.end_session_endpoint .end_session_endpoint
@ -134,6 +131,79 @@ impl<AC: AdditionalClaims> OidcClient<AC> {
end_session_endpoint, end_session_endpoint,
}) })
} }
/// create a new [`OidcClient`] by fetching the required information from the
/// `/.well-known/openid-configuration` endpoint of the issuer.
pub async fn discover_new(
application_base_url: Uri,
issuer: String,
client_id: String,
client_secret: Option<String>,
scopes: Vec<String>,
) -> Result<Self, Error> {
let client = reqwest::Client::default();
Self::discover_new_with_client(
application_base_url,
issuer,
client_id,
client_secret,
scopes,
&client,
)
.await
}
/// create a new [`OidcClient`] by fetching the required information from the
/// `/.well-known/openid-configuration` endpoint of the issuer using the provided
/// `reqwest::Client`.
pub async fn discover_new_with_client(
application_base_url: Uri,
issuer: String,
client_id: String,
client_secret: Option<String>,
scopes: Vec<String>,
client: &reqwest::Client,
) -> Result<Self, Error> {
// modified version of `openidconnect::reqwest::async_client::async_http_client`.
let async_http_client = |request: HttpRequest| async move {
let mut request_builder = client
.request(request.method, request.url.as_str())
.body(request.body);
for (name, value) in &request.headers {
request_builder = request_builder.header(name.as_str(), value.as_bytes());
}
let request = request_builder
.build()
.map_err(openidconnect::reqwest::Error::Reqwest)?;
let response = client
.execute(request)
.await
.map_err(openidconnect::reqwest::Error::Reqwest)?;
let status_code = response.status();
let headers = response.headers().to_owned();
let chunks = response
.bytes()
.await
.map_err(openidconnect::reqwest::Error::Reqwest)?;
Ok(HttpResponse {
status_code,
headers,
body: chunks.to_vec(),
})
};
let provider_metadata =
ProviderMetadata::discover_async(IssuerUrl::new(issuer)?, async_http_client).await?;
Self::from_provider_metadata(
provider_metadata,
application_base_url,
client_id,
client_secret,
scopes,
)
}
} }
/// an empty struct to be used as the default type for the additional claims generic /// an empty struct to be used as the default type for the additional claims generic
@ -172,7 +242,7 @@ struct AuthenticatedSession<AC: AdditionalClaims> {
/// additional metadata that is discovered on client creation via the /// additional metadata that is discovered on client creation via the
/// `.well-knwon/openid-configuration` endpoint. /// `.well-knwon/openid-configuration` endpoint.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
struct AdditionalProviderMetadata { pub struct AdditionalProviderMetadata {
end_session_endpoint: Option<String>, end_session_endpoint: Option<String>,
} }
impl openidconnect::AdditionalProviderMetadata for AdditionalProviderMetadata {} impl openidconnect::AdditionalProviderMetadata for AdditionalProviderMetadata {}