libauth_rs/provider/
mod.rs

1#[cfg(feature = "clerk")]
2pub mod clerk;
3#[cfg(feature = "msal")]
4pub mod msal;
5#[cfg(feature = "stytch")]
6pub mod stytch;
7
8#[cfg(feature = "clerk")]
9pub use clerk::ClerkProvider;
10#[cfg(feature = "msal")]
11pub use msal::MsalProvider;
12#[cfg(feature = "stytch")]
13pub use stytch::{
14    StytchB2BM2MProvider, StytchConsumerM2MProvider, StytchConsumerSessionProvider, StytchProvider,
15};
16
17use crate::error::AuthError;
18use crate::provider::stytch::types::Stytch;
19use crate::types::AuthProvider as AuthProviderType;
20
21// Re-export authentication traits
22
23/// Configuration for authentication providers
24#[derive(Debug, Clone, Default)]
25pub struct Provider {
26    pub config: Config,
27
28    /// Optional: Issuer-to-provider mapping for routing based on JWT issuer (iss) claim
29    /// Maps issuer strings (e.g., "<https://clerk.example.com>", "<https://login.microsoftonline.com>") to provider types
30    pub issuer_to_provider: Option<std::collections::HashMap<String, AuthProviderType>>,
31}
32
33impl Provider {
34    /// Add an issuer-to-provider mapping
35    /// Use this to map JWT issuer (iss) claims to specific providers
36    pub fn with_issuer_mapping(
37        mut self,
38        issuer: impl Into<String>,
39        provider: AuthProviderType,
40    ) -> Self {
41        if self.issuer_to_provider.is_none() {
42            self.issuer_to_provider = Some(std::collections::HashMap::new());
43        }
44        if let Some(ref mut map) = self.issuer_to_provider {
45            map.insert(issuer.into(), provider);
46        }
47        self
48    }
49
50    /// Get provider for a given issuer
51    pub fn get_provider_for_issuer(&self, issuer: &str) -> Option<AuthProviderType> {
52        self.issuer_to_provider
53            .as_ref()
54            .and_then(|map| map.get(issuer).copied())
55    }
56}
57
58/// Modern, type-safe configuration for authentication providers
59///
60/// This enum provides a cleaner API for configuring providers compared to the legacy
61/// AuthConfig struct. Each variant contains only the fields needed for that specific provider.
62///
63/// # Example
64/// ```rust,ignore
65/// use libauth_rs::providers::ProviderConfig;
66///
67/// let config = ProviderConfig::Stytch {
68///     project_id: "project-test-123".to_string(),
69///     project_secret: "secret-test-456".to_string(),
70///     client_id: None,
71/// };
72/// ```
73#[derive(Debug, Clone, Default)]
74pub enum Config {
75    /// Stytch authentication provider
76    /// Registers all Stytch providers (B2B + Consumer, Session + M2M)
77    #[cfg(feature = "stytch")]
78    Stytch {
79        project_id: String,
80        project_secret: String,
81        m2m_client_id: Option<String>,
82        m2m_client_secret: Option<String>,
83    },
84
85    /// Clerk authentication provider
86    #[cfg(feature = "clerk")]
87    Clerk {
88        publishable_key: Option<String>,
89        secret_key: String,
90    },
91
92    /// Microsoft Azure AD / MSAL authentication provider
93    #[cfg(feature = "msal")]
94    Msal {
95        tenant_id: String,
96        client_id: String,
97        client_secret: Option<String>,
98    },
99    #[default]
100    None,
101}
102
103impl From<Config> for Stytch {
104    fn from(value: Config) -> Self {
105        match value {
106            Config::Stytch {
107                project_id,
108                project_secret,
109                m2m_client_id,
110                m2m_client_secret,
111            } => Stytch {
112                project_id,
113                project_secret,
114                m2m_client_id,
115                m2m_client_secret,
116            },
117            _ => panic!("Not a Stytch provider"),
118        }
119    }
120}
121
122impl Config {
123    #[cfg(feature = "stytch")]
124    pub fn as_stytch(&self) -> Result<Stytch, AuthError> {
125        Ok(Stytch::from(self.clone()))
126    }
127
128    #[cfg(feature = "clerk")]
129    pub fn as_clerk(&self) -> Result<(Option<String>, String), AuthError> {
130        match self {
131            Config::Clerk {
132                publishable_key,
133                secret_key,
134            } => Ok((publishable_key.clone(), secret_key.clone())),
135            _ => Err(AuthError::ConfigurationError {
136                message: "Not a Clerk provider config".to_string(),
137            }),
138        }
139    }
140
141    #[cfg(feature = "msal")]
142    pub fn as_msal(&self) -> Result<(String, String, Option<String>), AuthError> {
143        match self {
144            Config::Msal {
145                tenant_id,
146                client_id,
147                client_secret,
148            } => Ok((tenant_id.clone(), client_id.clone(), client_secret.clone())),
149            _ => Err(AuthError::ConfigurationError {
150                message: "Not an MSAL provider config".to_string(),
151            }),
152        }
153    }
154}