Skip to main content

ri/auth/
mod.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Authentication Module
19//! 
20//! This module provides comprehensive authentication and authorization functionality for Ri,
21//! offering multiple authentication methods and a robust permission system.
22//! 
23//! ## Key Components
24//! 
25//! - **RiAuthModule**: Main auth module implementing service module traits
26//! - **RiAuthConfig**: Configuration for authentication behavior
27//! - **RiJWTManager**: JWT token management for stateless authentication
28//! - **RiSessionManager**: Session management for stateful authentication
29//! - **RiPermissionManager**: Permission and role management
30//! - **RiOAuthManager**: OAuth provider integration
31//! - **RiJWTClaims**: JWT token claims structure
32//! - **RiJWTValidationOptions**: JWT validation options
33//! - **RiOAuthProvider**: OAuth provider interface
34//! - **RiOAuthToken**: OAuth token structure
35//! - **RiOAuthUserInfo**: OAuth user information
36//! - **RiPermission**: Permission structure
37//! - **RiRole**: Role structure with permissions
38//! - **RiSession**: Session structure
39//! 
40//! ## Design Principles
41//! 
42//! 1. **Multiple Authentication Methods**: Supports JWT, sessions, OAuth, and API keys
43//! 2. **Configurable**: Highly configurable authentication behavior
44//! 3. **Async Support**: Full async/await compatibility for session and OAuth operations
45//! 4. **Role-Based Access Control**: Comprehensive permission system with roles
46//! 5. **Stateless and Stateful Options**: Supports both stateless (JWT) and stateful (session) authentication
47//! 6. **Service Module Integration**: Implements service module traits for seamless integration
48//! 7. **Thread-safe**: Uses Arc and RwLock for safe concurrent access
49//! 8. **Non-critical**: Auth failures should not break the entire application
50//! 9. **Extensible**: Easy to add new authentication methods and OAuth providers
51//! 10. **Secure by Default**: Sensible default configurations for security
52//! 
53//! ## Usage
54//! 
55//! ```rust,ignore
56//! use ri::prelude::*;
57//! use ri::auth::{RiAuthConfig, RiJWTManager, RiJWTClaims};
58//! use serde_json::json;
59//! 
60//! async fn example() -> RiResult<()> {
61//!     // Create auth configuration
62//!     let auth_config = RiAuthConfig {
63//!         enabled: true,
64//!         jwt_secret: "secure-secret-key".to_string(),
65//!         jwt_expiry_secs: 3600,
66//!         session_timeout_secs: 86400,
67//!         oauth_providers: vec![],
68//!         enable_api_keys: true,
69//!         enable_session_auth: true,
70//!     };
71//!     
72//!     // Create auth module
73//!     let auth_module = RiAuthModule::new(auth_config);
74//!     
75//!     // Get JWT manager
76//!     let jwt_manager = auth_module.jwt_manager();
77//!     
78//!     // Create JWT claims
79//!     let claims = RiJWTClaims {
80//!         sub: "user-123".to_string(),
81//!         email: "user@example.com".to_string(),
82//!         roles: vec!["user".to_string()],
83//!         permissions: vec!["read:data".to_string()],
84//!         extra: json!({ "custom": "value" }),
85//!     };
86//!     
87//!     // Generate JWT token
88//!     let token = jwt_manager.generate_token(claims)?;
89//!     println!("Generated JWT token: {}", token);
90//!     
91//!     // Validate JWT token
92//!     let validated_claims = jwt_manager.validate_token(&token)?;
93//!     println!("Validated claims: {:?}", validated_claims);
94//!     
95//!     // Get session manager
96//!     let session_manager = auth_module.session_manager();
97//!     
98//!     // Create a session
99//!     let session = session_manager.write().await.create_session("user-123").await?;
100//!     println!("Created session: {}", session.id);
101//!     
102//!     Ok(())
103//! }
104//! ```
105
106mod jwt;
107mod oauth;
108mod permissions;
109mod session;
110mod security;
111mod revocation;
112
113pub use jwt::{RiJWTManager, RiJWTClaims, RiJWTValidationOptions};
114pub use oauth::{RiOAuthManager, RiOAuthToken, RiOAuthUserInfo, RiOAuthProvider};
115pub use permissions::{RiPermissionManager, RiPermission, RiRole};
116pub use session::{RiSessionManager, RiSession};
117pub use security::RiSecurityManager;
118pub use revocation::{RiJWTRevocationList, RiRevokedTokenInfo};
119
120use crate::core::{RiResult, RiError, RiServiceContext};
121use rand::RngCore;
122use serde::Deserialize;
123use std::env;
124use std::sync::Arc;
125use tokio::sync::RwLock;
126#[cfg(feature = "pyo3")]
127use tokio::runtime::Handle;
128
129const DEFAULT_JWT_SECRET_ENV: &str = "Ri_JWT_SECRET";
130const FALLBACK_SECRET_LENGTH: usize = 64;
131
132fn load_jwt_secret_from_env() -> String {
133    env::var(DEFAULT_JWT_SECRET_ENV).unwrap_or_else(|_| {
134        let mut secret = vec![0u8; FALLBACK_SECRET_LENGTH];
135        rand::thread_rng().fill_bytes(&mut secret);
136        hex::encode(secret)
137    })
138}
139
140fn load_oauth_env_var(provider_name: &str, suffix: &str) -> Result<String, RiError> {
141    let env_var = format!("Ri_OAUTH_{}_{}", provider_name.to_uppercase(), suffix);
142    env::var(&env_var).map_err(|_| {
143        RiError::Config(format!(
144            "OAuth {} is not set for provider '{}'. Please set the environment variable {}",
145            suffix.to_lowercase(),
146            provider_name,
147            env_var
148        ))
149    })
150}
151
152fn get_oauth_url(provider_name: &str, endpoint: &str) -> String {
153    match load_oauth_env_var(provider_name, endpoint) {
154        Ok(url) if !url.is_empty() => url,
155        _ => format!("https://{}.com/oauth/{}", provider_name, endpoint)
156    }
157}
158
159#[cfg(feature = "pyo3")]
160use pyo3::PyResult;
161
162/// Configuration for the authentication module.
163/// 
164/// This struct defines the configuration options for authentication behavior, including
165/// JWT settings, session settings, OAuth providers, and enabled authentication methods.
166/// 
167/// ## Security
168/// 
169/// The JWT secret is loaded from the `Ri_JWT_SECRET` environment variable. If not set,
170/// a cryptographically secure random secret is generated automatically.
171/// 
172/// **Important**: For production environments, always set the `Ri_JWT_SECRET` environment
173/// variable to a strong, unique value. Do not rely on auto-generated secrets in production.
174#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
175#[derive(Debug, Clone)]
176#[derive(Deserialize)]
177pub struct RiAuthConfig {
178    /// Whether authentication is enabled
179    pub enabled: bool,
180    /// Secret key for JWT token generation and validation
181    pub jwt_secret: String,
182    /// JWT token expiry time in seconds
183    pub jwt_expiry_secs: u64,
184    /// Session timeout in seconds
185    pub session_timeout_secs: u64,
186    /// List of OAuth providers to enable
187    pub oauth_providers: Vec<String>,
188    /// Whether API key authentication is enabled
189    pub enable_api_keys: bool,
190    /// Whether session authentication is enabled
191    pub enable_session_auth: bool,
192    /// OAuth token cache backend type (Memory or Redis)
193    #[cfg(feature = "cache")]
194    pub oauth_cache_backend_type: crate::cache::RiCacheBackendType,
195    /// Redis URL for OAuth token cache (used when backend is Redis)
196    #[cfg(feature = "cache")]
197    pub oauth_cache_redis_url: String,
198}
199
200impl Default for RiAuthConfig {
201    fn default() -> Self {
202        Self {
203            enabled: true,
204            jwt_secret: load_jwt_secret_from_env(),
205            jwt_expiry_secs: 3600,
206            session_timeout_secs: 86400,
207            oauth_providers: vec![],
208            enable_api_keys: true,
209            enable_session_auth: true,
210            #[cfg(feature = "cache")]
211            oauth_cache_backend_type: crate::cache::RiCacheBackendType::Memory,
212            #[cfg(feature = "cache")]
213            oauth_cache_redis_url: "redis://127.0.0.1:6379".to_string(),
214        }
215    }
216}
217
218/// Main authentication module for Ri.
219/// 
220/// This module provides comprehensive authentication and authorization functionality,
221/// including JWT management, session management, permission management, and OAuth integration.
222#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
223pub struct RiAuthModule {
224    /// Authentication configuration
225    config: RiAuthConfig,
226    /// JWT manager for stateless authentication
227    jwt_manager: Arc<RiJWTManager>,
228    /// Session manager for stateful authentication, protected by a RwLock for thread-safe access
229    session_manager: Arc<RwLock<RiSessionManager>>,
230    /// Permission manager for role-based access control, protected by a RwLock for thread-safe access
231    permission_manager: Arc<RwLock<RiPermissionManager>>,
232    /// OAuth manager for OAuth provider integration, protected by a RwLock for thread-safe access
233    oauth_manager: Arc<RwLock<RiOAuthManager>>,
234    /// JWT token revocation list for token invalidation
235    revocation_list: Arc<RiJWTRevocationList>,
236}
237
238impl RiAuthModule {
239    /// Creates a new authentication module with the given configuration.
240    /// 
241    /// **Performance Note**: This method creates a permission manager using the synchronous
242    /// `new()` method which uses `blocking_write` during initialization. For async contexts,
243    /// consider using `new_async()` to avoid blocking the runtime.
244    /// 
245    /// # Parameters
246    /// 
247    /// - `config`: The authentication configuration to use
248    /// 
249    /// # Returns
250    /// 
251    /// A `RiResult` containing the new `RiAuthModule` instance
252    /// 
253    /// # Errors
254    /// 
255    /// Returns an error if Redis cache creation fails when Redis backend is configured
256    pub async fn new(config: RiAuthConfig) -> crate::core::error::RiResult<Self> {
257        let jwt_manager = Arc::new(RiJWTManager::create(config.jwt_secret.clone(), config.jwt_expiry_secs));
258        let session_manager = Arc::new(RwLock::new(RiSessionManager::new(config.session_timeout_secs)));
259        let permission_manager = Arc::new(RwLock::new(RiPermissionManager::new()));
260        
261        #[cfg(feature = "cache")]
262        let cache: Arc<dyn crate::cache::RiCache> = match config.oauth_cache_backend_type {
263            crate::cache::RiCacheBackendType::Memory => {
264                Arc::new(crate::cache::RiMemoryCache::new())
265            }
266            crate::cache::RiCacheBackendType::Redis => {
267                let cache = crate::cache::RiRedisCache::new(&config.oauth_cache_redis_url).await
268                    .map_err(|e| crate::core::error::RiError::RedisError(format!("Failed to create Redis cache for OAuth: {}", e)))?;
269                Arc::new(cache)
270            }
271            _ => Arc::new(crate::cache::RiMemoryCache::new()),
272        };
273        
274        #[cfg(not(feature = "cache"))]
275        let cache = Arc::new(crate::cache::RiMemoryCache::new());
276        
277        let oauth_manager = Arc::new(RwLock::new(RiOAuthManager::new(cache)));
278        let revocation_list = Arc::new(RiJWTRevocationList::new());
279
280        Ok(Self {
281            config,
282            jwt_manager,
283            session_manager,
284            permission_manager,
285            oauth_manager,
286            revocation_list,
287        })
288    }
289
290    /// Creates a new authentication module with the given configuration (synchronous version).
291    /// 
292    /// This is a synchronous wrapper for use in the builder pattern.
293    /// 
294    /// # Parameters
295    /// 
296    /// - `config`: The authentication configuration to use
297    /// 
298    /// # Returns
299    /// 
300    /// A new `RiAuthModule` instance
301    pub fn with_config(config: RiAuthConfig) -> Self {
302        let jwt_manager = Arc::new(RiJWTManager::create(config.jwt_secret.clone(), config.jwt_expiry_secs));
303        let session_manager = Arc::new(RwLock::new(RiSessionManager::new(config.session_timeout_secs)));
304        let permission_manager = Arc::new(RwLock::new(RiPermissionManager::new()));
305        let cache = Arc::new(crate::cache::RiMemoryCache::new());
306        let oauth_manager = Arc::new(RwLock::new(RiOAuthManager::new(cache)));
307        let revocation_list = Arc::new(RiJWTRevocationList::new());
308
309        Self {
310            config,
311            jwt_manager,
312            session_manager,
313            permission_manager,
314            oauth_manager,
315            revocation_list,
316        }
317    }
318
319    /// Creates a new authentication module with the given configuration asynchronously.
320    /// 
321    /// This method is preferred for async contexts as it avoids blocking the runtime
322    /// during permission manager initialization by using the async `new_async()` method.
323    /// 
324    /// # Parameters
325    /// 
326    /// - `config`: The authentication configuration to use
327    /// 
328    /// # Returns
329    /// 
330    /// A `RiResult` containing the new `RiAuthModule` instance
331    /// 
332    /// # Errors
333    /// 
334    /// Returns an error if Redis cache creation fails when Redis backend is configured
335    pub async fn new_async(config: RiAuthConfig) -> crate::core::error::RiResult<Self> {
336        let jwt_manager = Arc::new(RiJWTManager::create(config.jwt_secret.clone(), config.jwt_expiry_secs));
337        let session_manager = Arc::new(RwLock::new(RiSessionManager::new(config.session_timeout_secs)));
338        let permission_manager = Arc::new(RwLock::new(RiPermissionManager::new_async().await));
339        
340        #[cfg(feature = "cache")]
341        let cache: Arc<dyn crate::cache::RiCache> = match config.oauth_cache_backend_type {
342            crate::cache::RiCacheBackendType::Memory => {
343                Arc::new(crate::cache::RiMemoryCache::new())
344            }
345            crate::cache::RiCacheBackendType::Redis => {
346                let cache = crate::cache::RiRedisCache::new(&config.oauth_cache_redis_url).await
347                    .map_err(|e| crate::core::error::RiError::RedisError(format!("Failed to create Redis cache: {}", e)))?;
348                Arc::new(cache)
349            }
350            _ => Arc::new(crate::cache::RiMemoryCache::new()),
351        };
352        
353        #[cfg(not(feature = "cache"))]
354        let cache = Arc::new(crate::cache::RiMemoryCache::new());
355        
356        let oauth_manager = Arc::new(RwLock::new(RiOAuthManager::new(cache)));
357        let revocation_list = Arc::new(RiJWTRevocationList::new());
358
359        Ok(Self {
360            config,
361            jwt_manager,
362            session_manager,
363            permission_manager,
364            oauth_manager,
365            revocation_list,
366        })
367    }
368
369    /// Returns a reference to the JWT revocation list.
370    /// 
371    /// # Returns
372    /// 
373    /// An Arc<RiJWTRevocationList> providing thread-safe access to the token revocation list
374    pub fn revocation_list(&self) -> Arc<RiJWTRevocationList> {
375        self.revocation_list.clone()
376    }
377
378    /// Returns a reference to the JWT manager.
379    /// 
380    /// # Returns
381    /// 
382    /// An Arc<RiJWTManager> providing thread-safe access to the JWT manager
383    pub fn jwt_manager(&self) -> Arc<RiJWTManager> {
384        self.jwt_manager.clone()
385    }
386
387    /// Returns a reference to the session manager.
388    /// 
389    /// # Returns
390    /// 
391    /// An Arc<RwLock<RiSessionManager>> providing thread-safe access to the session manager
392    pub fn session_manager(&self) -> Arc<RwLock<RiSessionManager>> {
393        self.session_manager.clone()
394    }
395
396    /// Returns a reference to the permission manager.
397    /// 
398    /// # Returns
399    /// 
400    /// An Arc<RwLock<RiPermissionManager>> providing thread-safe access to the permission manager
401    pub fn permission_manager(&self) -> Arc<RwLock<RiPermissionManager>> {
402        self.permission_manager.clone()
403    }
404
405    /// Returns a reference to the OAuth manager.
406    /// 
407    /// # Returns
408    /// 
409    /// An Arc<RwLock<RiOAuthManager>> providing thread-safe access to the OAuth manager
410    pub fn oauth_manager(&self) -> Arc<RwLock<RiOAuthManager>> {
411        self.oauth_manager.clone()
412    }
413}
414
415#[cfg(feature = "pyo3")]
416#[pyo3::prelude::pymethods]
417impl RiAuthConfig {
418    /// Creates a new authentication configuration with the specified parameters.
419    ///
420    /// All parameters have sensible defaults, making it easy to create a basic configuration.
421    /// The JWT secret is automatically loaded from the `Ri_JWT_SECRET` environment variable
422    /// if not provided.
423    ///
424    /// # Parameters
425    ///
426    /// - `enabled`: Whether authentication is enabled (default: true)
427    /// - `jwt_secret`: Secret key for JWT tokens (default: loaded from Ri_JWT_SECRET env var)
428    /// - `jwt_expiry_secs`: JWT token expiry time in seconds (default: 3600)
429    /// - `session_timeout_secs`: Session timeout in seconds (default: 86400)
430    /// - `oauth_providers`: List of OAuth providers to enable (default: empty)
431    /// - `enable_api_keys`: Whether API key authentication is enabled (default: true)
432    /// - `enable_session_auth`: Whether session authentication is enabled (default: true)
433    /// - `oauth_cache_backend_type`: Cache backend type - "Memory" or "Redis" (default: "Memory")
434    /// - `oauth_cache_redis_url`: Redis URL for OAuth cache (default: "redis://127.0.0.1:6379")
435    ///
436    /// # Returns
437    ///
438    /// A new `RiAuthConfig` instance
439    ///
440    /// # Example
441    ///
442    /// ```python
443    /// from ri import RiAuthConfig
444    ///
445    /// # Create with defaults
446    /// config = RiAuthConfig()
447    ///
448    /// # Create with custom settings
449    /// config = RiAuthConfig(
450    ///     enabled=True,
451    ///     jwt_secret="my-secret-key",
452    ///     jwt_expiry_secs=7200,
453    ///     oauth_providers=["google", "github"]
454    /// )
455    /// ```
456    #[new]
457    #[pyo3(signature = (
458        enabled = true,
459        jwt_secret = "",
460        jwt_expiry_secs = 3600,
461        session_timeout_secs = 86400,
462        oauth_providers = vec![],
463        enable_api_keys = true,
464        enable_session_auth = true,
465        oauth_cache_backend_type = None,
466        oauth_cache_redis_url = "redis://127.0.0.1:6379"
467    ))]
468    fn py_new(
469        enabled: bool,
470        jwt_secret: &str,
471        jwt_expiry_secs: u64,
472        session_timeout_secs: u64,
473        oauth_providers: Vec<String>,
474        enable_api_keys: bool,
475        enable_session_auth: bool,
476        oauth_cache_backend_type: Option<String>,
477        oauth_cache_redis_url: &str,
478    ) -> Self {
479        let secret = if jwt_secret.is_empty() {
480            load_jwt_secret_from_env()
481        } else {
482            jwt_secret.to_string()
483        };
484        
485        #[cfg(feature = "cache")]
486        {
487            let backend_type = match oauth_cache_backend_type.as_deref() {
488                Some("Redis") => crate::cache::RiCacheBackendType::Redis,
489                _ => crate::cache::RiCacheBackendType::Memory,
490            };
491            
492            Self {
493                enabled,
494                jwt_secret: secret,
495                jwt_expiry_secs,
496                session_timeout_secs,
497                oauth_providers,
498                enable_api_keys,
499                enable_session_auth,
500                oauth_cache_backend_type: backend_type,
501                oauth_cache_redis_url: oauth_cache_redis_url.to_string(),
502            }
503        }
504        
505        #[cfg(not(feature = "cache"))]
506        {
507            let _ = oauth_cache_backend_type;
508            let _ = oauth_cache_redis_url;
509            
510            Self {
511                enabled,
512                jwt_secret: secret,
513                jwt_expiry_secs,
514                session_timeout_secs,
515                oauth_providers,
516                enable_api_keys,
517                enable_session_auth,
518            }
519        }
520    }
521
522    /// Creates a new authentication configuration with default values.
523    ///
524    /// This is a convenience method that creates a configuration with all default settings.
525    /// The JWT secret is loaded from the `Ri_JWT_SECRET` environment variable.
526    ///
527    /// # Returns
528    ///
529    /// A new `RiAuthConfig` instance with default values
530    ///
531    /// # Example
532    ///
533    /// ```python
534    /// from ri import RiAuthConfig
535    ///
536    /// config = RiAuthConfig.default()
537    /// ```
538    #[staticmethod]
539    fn default() -> Self {
540        <Self as Default>::default()
541    }
542
543    /// Creates a new authentication configuration from environment variables.
544    ///
545    /// This method loads the JWT secret from the `Ri_JWT_SECRET` environment variable
546    /// and uses default values for all other settings.
547    ///
548    /// # Returns
549    ///
550    /// A new `RiAuthConfig` instance with values from environment
551    ///
552    /// # Example
553    ///
554    /// ```python
555    /// import os
556    /// from ri import RiAuthConfig
557    ///
558    /// os.environ["Ri_JWT_SECRET"] = "my-secret-key"
559    /// config = RiAuthConfig.from_env()
560    /// ```
561    #[staticmethod]
562    fn from_env() -> Self {
563        Self {
564            jwt_secret: load_jwt_secret_from_env(),
565            ..Self::default()
566        }
567    }
568
569    /// Returns whether authentication is enabled.
570    #[getter]
571    fn get_enabled(&self) -> bool {
572        self.enabled
573    }
574
575    /// Returns the JWT secret key.
576    #[getter]
577    fn get_jwt_secret(&self) -> String {
578        self.jwt_secret.clone()
579    }
580
581    /// Returns the JWT token expiry time in seconds.
582    #[getter]
583    fn get_jwt_expiry_secs(&self) -> u64 {
584        self.jwt_expiry_secs
585    }
586
587    /// Returns the session timeout in seconds.
588    #[getter]
589    fn get_session_timeout_secs(&self) -> u64 {
590        self.session_timeout_secs
591    }
592
593    /// Returns the list of OAuth providers.
594    #[getter]
595    fn get_oauth_providers(&self) -> Vec<String> {
596        self.oauth_providers.clone()
597    }
598
599    /// Returns whether API key authentication is enabled.
600    #[getter]
601    fn get_enable_api_keys(&self) -> bool {
602        self.enable_api_keys
603    }
604
605    /// Returns whether session authentication is enabled.
606    #[getter]
607    fn get_enable_session_auth(&self) -> bool {
608        self.enable_session_auth
609    }
610}
611
612#[cfg(feature = "pyo3")]
613/// Python bindings for the Ri Authentication Module.
614///
615/// This module provides Python interface to Ri authentication functionality,
616/// enabling Python applications to leverage Ri's authentication capabilities.
617///
618/// ## Supported Operations
619///
620/// - JWT token generation and validation
621/// - Session management for stateful authentication
622/// - Permission and role management for RBAC
623/// - OAuth provider integration
624///
625/// ## Python Usage Example
626///
627/// ```python
628/// from ri import RiAuthConfig, RiJWTManager
629///
630/// # Create auth configuration
631/// config = RiAuthConfig(
632///     enabled=True,
633///     jwt_secret="secure-secret-key",
634///     jwt_expiry_secs=3600,
635///     session_timeout_secs=86400,
636///     oauth_providers=["google", "github"],
637///     enable_api_keys=True,
638///     enable_session_auth=True,
639/// )
640///
641/// # Create auth module
642/// auth_module = RiAuthModule(config)
643///
644/// # Get JWT manager and generate token
645/// jwt_manager = auth_module.jwt_manager()
646/// token = jwt_manager.generate_token("user123", ["user"], ["read:data"])
647/// ```
648#[pyo3::prelude::pymethods]
649impl RiAuthModule {
650    #[new]
651    fn py_new(config: RiAuthConfig) -> PyResult<Self> {
652        let rt = Handle::current();
653        rt.block_on(async {
654            Self::new(config).await
655                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
656        })
657    }
658
659    #[getter]
660    fn get_config(&self) -> RiAuthConfig {
661        self.config.clone()
662    }
663
664    #[getter]
665    fn get_jwt_expiry_secs(&self) -> u64 {
666        self.jwt_manager.get_token_expiry()
667    }
668
669    #[getter]
670    fn get_session_timeout_secs(&self) -> u64 {
671        self.config.session_timeout_secs
672    }
673
674    #[getter]
675    fn is_enabled(&self) -> bool {
676        self.config.enabled
677    }
678
679    #[getter]
680    fn is_api_keys_enabled(&self) -> bool {
681        self.config.enable_api_keys
682    }
683
684    #[getter]
685    fn is_session_auth_enabled(&self) -> bool {
686        self.config.enable_session_auth
687    }
688
689    #[getter]
690    fn get_oauth_providers(&self) -> Vec<String> {
691        self.config.oauth_providers.clone()
692    }
693
694    fn validate_jwt_token(&self, token: &str) -> bool {
695        self.jwt_manager.validate_token(token).is_ok()
696    }
697
698    fn generate_test_token(&self, subject: &str, roles: Vec<String>, permissions: Vec<String>) -> PyResult<String> {
699        self.jwt_manager.generate_token(subject, roles, permissions)
700            .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
701    }
702}
703
704impl crate::core::ServiceModule for RiAuthModule {
705    fn name(&self) -> &str {
706        "Ri.Auth"
707    }
708
709    fn is_critical(&self) -> bool {
710        false
711    }
712
713    fn priority(&self) -> i32 {
714        20
715    }
716
717    fn dependencies(&self) -> Vec<&str> {
718        vec![]
719    }
720
721    fn init(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
722        Ok(())
723    }
724
725    fn start(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
726        Ok(())
727    }
728
729    fn shutdown(&mut self, _ctx: &mut crate::core::RiServiceContext) -> crate::core::RiResult<()> {
730        Ok(())
731    }
732}
733
734#[async_trait::async_trait]
735impl crate::core::RiModule for RiAuthModule {
736    /// Returns the name of the authentication module.
737    /// 
738    /// # Returns
739    /// 
740    /// The module name as a string
741    fn name(&self) -> &str {
742        "Ri.Auth"
743    }
744
745    /// Indicates whether the authentication module is critical.
746    /// 
747    /// The authentication module is non-critical, meaning that if it fails to initialize or operate,
748    /// it should not break the entire application. This allows the core functionality to continue
749    /// even if authentication features are unavailable.
750    /// 
751    /// # Returns
752    /// 
753    /// `false` since authentication is non-critical
754    fn is_critical(&self) -> bool {
755        false // Auth failures should not break the application
756    }
757
758    /// Initializes the authentication module asynchronously.
759    /// 
760    /// This method performs the following steps:
761    /// 1. Loads configuration from the service context
762    /// 2. Updates the module configuration if provided
763    /// 3. Reinitializes the JWT manager with the new configuration
764    /// 4. Initializes OAuth providers if configured
765    /// 
766    /// # Parameters
767    /// 
768    /// - `ctx`: The service context containing configuration
769    /// 
770    /// # Returns
771    /// 
772    /// A `RiResult<()>` indicating success or failure
773    async fn init(&mut self, ctx: &mut RiServiceContext) -> RiResult<()> {
774        log::info!("Initializing Ri Auth Module");
775
776        // Load configuration
777        let binding = ctx.config();
778        let cfg = binding.config();
779        
780        // Update configuration if provided
781        if let Some(auth_config) = cfg.get("auth") {
782            self.config = serde_yaml::from_str(auth_config)
783                .unwrap_or_else(|_| RiAuthConfig::default());
784        }
785
786        // Initialize JWT manager with new config
787        self.jwt_manager = Arc::new(RiJWTManager::create(self.config.jwt_secret.clone(), self.config.jwt_expiry_secs));
788
789        // Initialize OAuth providers if configured
790        if !self.config.oauth_providers.is_empty() {
791            for provider_name in &self.config.oauth_providers {
792                let client_id = load_oauth_env_var(provider_name, "CLIENT_ID")?;
793                let client_secret = load_oauth_env_var(provider_name, "CLIENT_SECRET")?;
794
795                let provider_config = crate::auth::oauth::RiOAuthProvider {
796                    id: provider_name.clone(),
797                    name: provider_name.clone(),
798                    client_id,
799                    client_secret,
800                    auth_url: get_oauth_url(provider_name, "authorize"),
801                    token_url: get_oauth_url(provider_name, "token"),
802                    user_info_url: get_oauth_url(provider_name, "userinfo"),
803                    scopes: vec!["openid".to_string(), "profile".to_string(), "email".to_string()],
804                    enabled: true,
805                    redirect_uri: None,
806                };
807                
808                let oauth_mgr = self.oauth_manager.write().await;
809                oauth_mgr.register_provider(provider_config).await?;
810                log::info!("OAuth provider registered: {provider_name}");
811            }
812        }
813
814        log::info!("Ri Auth Module initialized successfully");
815        Ok(())
816    }
817
818    /// Performs asynchronous cleanup after the application has shut down.
819    /// 
820    /// This method cleans up all sessions managed by the session manager.
821    /// 
822    /// # Parameters
823    /// 
824    /// - `_ctx`: The service context (not used in this implementation)
825    /// 
826    /// # Returns
827    /// 
828    /// A `RiResult<()>` indicating success or failure
829    async fn after_shutdown(&mut self, _ctx: &mut RiServiceContext) -> RiResult<()> {
830        log::info!("Cleaning up Ri Auth Module");
831        
832        // Cleanup sessions
833        let session_mgr = self.session_manager.write().await;
834        session_mgr.cleanup_all().await?;
835        
836        log::info!("Ri Auth Module cleanup completed");
837        Ok(())
838    }
839}