Skip to main content

openauth_core/
context.rs

1//! Request and runtime context contracts.
2
3pub mod request_state;
4
5mod builder;
6mod origins;
7mod plugins;
8mod secrets;
9
10use crate::auth::trusted_origins::{matches_origin_pattern, OriginMatchSettings};
11use crate::cookies::AuthCookies;
12use crate::db::{DbAdapter, DbSchema};
13use crate::env::logger::Logger;
14use crate::error::OpenAuthError;
15use crate::options::{
16    DynamicRateLimitPathRule, OpenAuthOptions, RateLimitPathRule, RateLimitStorage,
17    RateLimitStorageOption,
18};
19use crate::plugin::{AuthPlugin, PluginErrorCode};
20use crate::rate_limit::MemoryRateLimitStorage;
21use http::Request;
22use openauth_oauth::oauth2::SocialOAuthProvider;
23use std::collections::BTreeMap;
24use std::fmt;
25use std::sync::Arc;
26
27pub use builder::{
28    create_auth_context, create_auth_context_with_adapter, create_auth_context_with_environment,
29    create_auth_context_with_environment_and_adapter,
30};
31pub use secrets::SecretMaterial;
32
33use origins::push_trusted_origin;
34
35#[derive(Clone)]
36pub struct AuthContext {
37    pub app_name: String,
38    pub base_url: String,
39    pub base_path: String,
40    pub options: OpenAuthOptions,
41    pub auth_cookies: AuthCookies,
42    pub session_config: SessionConfig,
43    pub secret: String,
44    pub secret_config: SecretMaterial,
45    pub password: PasswordContext,
46    pub rate_limit: RateLimitContext,
47    pub trusted_origins: Vec<String>,
48    pub disabled_paths: Vec<String>,
49    pub plugins: Vec<AuthPlugin>,
50    pub adapter: Option<Arc<dyn DbAdapter>>,
51    pub social_providers: BTreeMap<String, Arc<dyn SocialOAuthProvider>>,
52    pub db_schema: DbSchema,
53    pub plugin_error_codes: BTreeMap<String, PluginErrorCode>,
54    pub plugin_database_hooks: Vec<crate::plugin::PluginDatabaseHook>,
55    pub plugin_migrations: Vec<crate::plugin::PluginMigration>,
56    pub logger: Logger,
57}
58
59/// Environment values used by context initialization.
60#[derive(Clone, Default, PartialEq, Eq)]
61pub struct AuthEnvironment {
62    pub better_auth_secret: Option<String>,
63    pub auth_secret: Option<String>,
64    pub better_auth_secrets: Option<String>,
65}
66
67impl fmt::Debug for AuthEnvironment {
68    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
69        formatter
70            .debug_struct("AuthEnvironment")
71            .field(
72                "better_auth_secret",
73                &self.better_auth_secret.as_ref().map(|_| "<redacted>"),
74            )
75            .field(
76                "auth_secret",
77                &self.auth_secret.as_ref().map(|_| "<redacted>"),
78            )
79            .field(
80                "better_auth_secrets",
81                &self.better_auth_secrets.as_ref().map(|_| "<redacted>"),
82            )
83            .finish()
84    }
85}
86
87impl AuthEnvironment {
88    pub fn from_process() -> Self {
89        Self {
90            better_auth_secret: std::env::var("BETTER_AUTH_SECRET").ok(),
91            auth_secret: std::env::var("AUTH_SECRET").ok(),
92            better_auth_secrets: std::env::var("BETTER_AUTH_SECRETS").ok(),
93        }
94    }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct SessionConfig {
99    pub update_age: u64,
100    pub expires_in: u64,
101    pub fresh_age: u64,
102    pub cookie_refresh_cache: bool,
103}
104
105#[derive(Clone)]
106pub struct PasswordContext {
107    pub config: PasswordPolicy,
108    pub hash: fn(&str) -> Result<String, OpenAuthError>,
109    pub verify: fn(&str, &str) -> Result<bool, OpenAuthError>,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct PasswordPolicy {
114    pub min_password_length: usize,
115    pub max_password_length: usize,
116}
117
118#[derive(Clone)]
119pub struct RateLimitContext {
120    pub enabled: bool,
121    pub window: u64,
122    pub max: u64,
123    pub storage: RateLimitStorageOption,
124    pub custom_rules: Vec<RateLimitPathRule>,
125    pub dynamic_rules: Vec<DynamicRateLimitPathRule>,
126    pub plugin_rules: Vec<crate::plugin::PluginRateLimitRule>,
127    pub custom_storage: Option<Arc<dyn RateLimitStorage>>,
128    pub memory_storage: Arc<MemoryRateLimitStorage>,
129}
130
131impl AuthContext {
132    pub fn adapter(&self) -> Option<Arc<dyn DbAdapter>> {
133        self.adapter.clone()
134    }
135
136    pub fn social_provider(&self, id: &str) -> Option<Arc<dyn SocialOAuthProvider>> {
137        self.social_providers.get(id).cloned()
138    }
139
140    pub fn has_plugin(&self, id: &str) -> bool {
141        self.plugins.iter().any(|plugin| plugin.id == id)
142    }
143
144    pub fn is_trusted_origin(&self, url: &str, settings: Option<OriginMatchSettings>) -> bool {
145        self.trusted_origins
146            .iter()
147            .any(|origin| matches_origin_pattern(url, origin, settings))
148    }
149
150    pub fn trusted_origins_for_request(
151        &self,
152        request: Option<&Request<Vec<u8>>>,
153    ) -> Result<Vec<String>, OpenAuthError> {
154        let mut origins = self.trusted_origins.clone();
155        if let Some(provider) = self.options.trusted_origins.provider() {
156            for origin in provider.trusted_origins(request)? {
157                push_trusted_origin(&mut origins, origin);
158            }
159        }
160        Ok(origins)
161    }
162
163    pub fn is_trusted_origin_for_request(
164        &self,
165        url: &str,
166        settings: Option<OriginMatchSettings>,
167        request: Option<&Request<Vec<u8>>>,
168    ) -> Result<bool, OpenAuthError> {
169        Ok(self
170            .trusted_origins_for_request(request)?
171            .iter()
172            .any(|origin| matches_origin_pattern(url, origin, settings)))
173    }
174}