Skip to main content

authkestra_core/
discovery.rs

1use serde::{Deserialize, Serialize};
2
3use crate::error::AuthError;
4
5/// Metadata for an OpenID Connect provider.
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ProviderMetadata {
8    /// The issuer URL
9    pub issuer: String,
10    /// The authorization endpoint URL
11    pub authorization_endpoint: String,
12    /// The token endpoint URL
13    pub token_endpoint: String,
14    /// The JWKS URI
15    pub jwks_uri: String,
16    /// The userinfo endpoint URL, if available
17    pub userinfo_endpoint: Option<String>,
18    /// Scopes supported by the provider
19    pub scopes_supported: Option<Vec<String>>,
20    /// Response types supported by the provider
21    pub response_types_supported: Option<Vec<String>>,
22    /// ID token signing algorithms supported by the provider
23    pub id_token_signing_alg_values_supported: Option<Vec<String>>,
24}
25
26impl ProviderMetadata {
27    /// Fetches metadata from the issuer URL (appends /.well-known/openid-configuration)
28    pub async fn discover(issuer_url: &str, client: reqwest::Client) -> Result<Self, AuthError> {
29        let mut url = url::Url::parse(issuer_url)
30            .map_err(|e| AuthError::Discovery(format!("Invalid issuer URL: {}", e)))?;
31
32        {
33            let mut path = url
34                .path_segments_mut()
35                .map_err(|_| AuthError::Discovery("Cannot append to issuer URL".to_string()))?;
36            path.push(".well-known");
37            path.push("openid-configuration");
38        }
39
40        let metadata = client
41            .get(url)
42            .send()
43            .await
44            .map_err(|_| AuthError::Network)?
45            .json::<ProviderMetadata>()
46            .await
47            .map_err(|e| AuthError::Discovery(format!("Failed to parse metadata: {}", e)))?;
48
49        Ok(metadata)
50    }
51}