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