use super::OpenIdConfigurationError;
use serde::Deserialize;
use url::Url;
#[derive(Deserialize)]
pub struct OpenIdConfiguration {
issuer: Url,
authorization_endpoint: Url,
token_endpoint: Url,
jwks_uri: Url,
}
impl OpenIdConfiguration {
#[must_use]
pub const fn issuer(&self) -> &Url {
&self.issuer
}
#[must_use]
pub const fn authorization_endpoint(&self) -> &Url {
&self.authorization_endpoint
}
#[must_use]
pub const fn token_endpoint(&self) -> &Url {
&self.token_endpoint
}
#[must_use]
pub const fn jwks_uri(&self) -> &Url {
&self.jwks_uri
}
#[cfg(test)]
#[must_use]
pub const fn new_for_test(
issuer: Url,
authorization_endpoint: Url,
token_endpoint: Url,
jwks_uri: Url,
) -> Self {
Self {
issuer,
authorization_endpoint,
token_endpoint,
jwks_uri,
}
}
pub async fn fetch(issuer_url: Url) -> Result<Self, OpenIdConfigurationError> {
let discovery_url_str = format!(
"{}/.well-known/openid-configuration",
issuer_url.as_str().trim_end_matches('/')
);
let discovery_url = Url::parse(&discovery_url_str).map_err(|err| {
OpenIdConfigurationError::new(format!("invalid discovery URL: {err}"))
})?;
tracing::debug!("fetching OIDC discovery from {discovery_url}");
let doc = reqwest::get(discovery_url.as_str())
.await
.map_err(|err| OpenIdConfigurationError::new(format!("network error: {err}")))?
.json::<Self>()
.await
.map_err(|err| OpenIdConfigurationError::new(format!("JSON parse error: {err}")))?;
if doc.issuer != issuer_url {
return Err(OpenIdConfigurationError::new(format!(
"issuer mismatch (expected {issuer_url}, got {})",
doc.issuer
)));
}
Ok(doc)
}
}