libauth_rs/providers/
mod.rs1#[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::StytchProvider;
14
15use crate::error::{AuthError, AuthResult};
16use crate::types::{AuthProvider as AuthProviderType, AuthToken, UserContext};
17use async_trait::async_trait;
18
19#[async_trait]
21pub trait AuthProvider: Send + Sync {
22 fn name(&self) -> &'static str;
24
25 fn provider_type(&self) -> AuthProviderType;
27
28 async fn can_handle(&self, token: &AuthToken) -> bool;
31
32 async fn authenticate(&self, token: &AuthToken) -> AuthResult<UserContext>;
35
36 fn expected_issuers(&self) -> Vec<String> {
39 Vec::new()
40 }
41
42 async fn refresh_token(&self, _refresh_token: &str) -> AuthResult<String> {
44 Err(AuthError::Unknown {
45 message: "Token refresh not supported by this provider".to_string(),
46 provider: Some(self.name().to_string()),
47 })
48 }
49
50 async fn revoke_token(&self, _token: &AuthToken) -> AuthResult<()> {
52 Ok(())
55 }
56
57 async fn check_permission(&self, user: &UserContext, permission: &str) -> AuthResult<bool> {
60 Ok(user
62 .metadata
63 .get(&format!("permission_{}", permission))
64 .and_then(|v| v.as_bool())
65 .unwrap_or(false))
66 }
67
68 async fn get_user_roles(&self, user: &UserContext) -> AuthResult<Vec<String>> {
71 Ok(user.get_roles())
73 }
74
75 async fn check_role(&self, user: &UserContext, role: &str) -> AuthResult<bool> {
77 let roles = self.get_user_roles(user).await?;
78 Ok(roles.iter().any(|r| r == role))
79 }
80
81 async fn check_organization(&self, user: &UserContext, org_id: &str) -> AuthResult<bool> {
83 Ok(user
85 .metadata
86 .get("organization_id")
87 .or_else(|| user.metadata.get("org_id"))
88 .or_else(|| user.metadata.get("tenant_id"))
89 .and_then(|v| v.as_str())
90 .map(|id| id == org_id)
91 .unwrap_or(false))
92 }
93}
94
95#[derive(Debug, Clone, Default)]
97pub struct AuthConfig {
98 #[cfg(feature = "clerk")]
99 pub clerk_publishable_key: Option<String>,
100 #[cfg(feature = "clerk")]
101 pub clerk_secret_key: Option<String>,
102
103 #[cfg(feature = "stytch")]
104 pub stytch_project_id: Option<String>,
105 #[cfg(feature = "stytch")]
106 pub stytch_secret: Option<String>,
107
108 #[cfg(feature = "msal")]
109 pub azure_client_id: Option<String>,
110 #[cfg(feature = "msal")]
111 pub azure_client_secret: Option<String>,
112 #[cfg(feature = "msal")]
113 pub azure_tenant_id: Option<String>,
114
115 pub issuer_to_provider: Option<std::collections::HashMap<String, AuthProviderType>>,
118}
119
120impl AuthConfig {
121 pub fn with_issuer_mapping(
124 mut self,
125 issuer: impl Into<String>,
126 provider: AuthProviderType,
127 ) -> Self {
128 if self.issuer_to_provider.is_none() {
129 self.issuer_to_provider = Some(std::collections::HashMap::new());
130 }
131 if let Some(ref mut map) = self.issuer_to_provider {
132 map.insert(issuer.into(), provider);
133 }
134 self
135 }
136
137 pub fn get_provider_for_issuer(&self, issuer: &str) -> Option<AuthProviderType> {
139 self.issuer_to_provider
140 .as_ref()
141 .and_then(|map| map.get(issuer).copied())
142 }
143}