feat: add typestate OidcClient builder

The previous generator functions for `OidcClient` have been replaced by
a Builder.
With this change the suggested changes by #14 and #21 have been
implemented.
This commit is contained in:
Paul Zinselmeyer 2025-02-18 21:26:56 +01:00
parent 6d7fc3c7f1
commit 58369449cf
Signed by: pfzetto
GPG key ID: B471A1AF06C895FD
6 changed files with 324 additions and 167 deletions

View file

@ -3,7 +3,6 @@
#![deny(warnings)]
#![doc = include_str!("../README.md")]
use crate::error::Error;
use http::Uri;
use openidconnect::{
core::{
@ -13,12 +12,13 @@ use openidconnect::{
CoreResponseMode, CoreResponseType, CoreRevocableToken, CoreRevocationErrorResponse,
CoreSubjectIdentifierType, CoreTokenIntrospectionResponse, CoreTokenType,
},
AccessToken, ClientId, ClientSecret, CsrfToken, EmptyExtraTokenFields, EndpointMaybeSet,
EndpointNotSet, EndpointSet, IdTokenFields, IssuerUrl, Nonce, PkceCodeVerifier, RefreshToken,
StandardErrorResponse, StandardTokenResponse,
AccessToken, CsrfToken, EmptyExtraTokenFields, EndpointMaybeSet, EndpointNotSet, EndpointSet,
IdTokenFields, Nonce, PkceCodeVerifier, RefreshToken, StandardErrorResponse,
StandardTokenResponse,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub mod builder;
pub mod error;
mod extractor;
mod middleware;
@ -99,118 +99,14 @@ pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
/// OpenID Connect Client
#[derive(Clone)]
pub struct OidcClient<AC: AdditionalClaims> {
scopes: Vec<String>,
client_id: String,
scopes: Vec<Box<str>>,
oidc_request_parameters: Vec<Box<str>>,
client_id: Box<str>,
client: Client<AC>,
http_client: reqwest::Client,
application_base_url: Uri,
end_session_endpoint: Option<Uri>,
}
impl<AC: AdditionalClaims> OidcClient<AC> {
/// create a new [`OidcClient`] from an existing [`ProviderMetadata`].
pub fn from_provider_metadata(
provider_metadata: ProviderMetadata,
application_base_url: Uri,
client_id: String,
client_secret: Option<String>,
scopes: Vec<String>,
) -> Result<Self, Error> {
let end_session_endpoint = provider_metadata
.additional_metadata()
.end_session_endpoint
.clone()
.map(Uri::from_maybe_shared)
.transpose()
.map_err(Error::InvalidEndSessionEndpoint)?;
let client = Client::from_provider_metadata(
provider_metadata,
ClientId::new(client_id.clone()),
client_secret.map(ClientSecret::new),
);
Ok(Self {
scopes,
client,
client_id,
application_base_url,
end_session_endpoint,
http_client: reqwest::Client::default(),
})
}
/// create a new [`OidcClient`] from an existing [`ProviderMetadata`].
pub fn from_provider_metadata_and_client(
provider_metadata: ProviderMetadata,
application_base_url: Uri,
client_id: String,
client_secret: Option<String>,
scopes: Vec<String>,
http_client: reqwest::Client,
) -> Result<Self, Error> {
let end_session_endpoint = provider_metadata
.additional_metadata()
.end_session_endpoint
.clone()
.map(Uri::from_maybe_shared)
.transpose()
.map_err(Error::InvalidEndSessionEndpoint)?;
let client = Client::from_provider_metadata(
provider_metadata,
ClientId::new(client_id.clone()),
client_secret.map(ClientSecret::new),
);
Ok(Self {
scopes,
client,
client_id,
application_base_url,
end_session_endpoint,
http_client,
})
}
/// 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> {
let provider_metadata =
ProviderMetadata::discover_async(IssuerUrl::new(issuer)?, &client).await?;
Self::from_provider_metadata_and_client(
provider_metadata,
application_base_url,
client_id,
client_secret,
scopes,
client,
)
}
auth_context_class: Option<Box<str>>,
}
/// an empty struct to be used as the default type for the additional claims generic