Skip to main content

auth_framework/server/core/
additional_modules.rs

1//! Additional server capability modules
2//!
3//! Note: WebAuthn/FIDO2 support is provided via the `PasskeyAuthMethod`
4//! in `src/methods/passkey/mod.rs` using the production-grade `passkey` crate.
5//! No separate WebAuthn server module is needed.
6
7/// JWT token server for issuing and validating JWT tokens
8pub mod jwt_server {
9    use crate::errors::Result;
10    use crate::storage::AuthStorage;
11    use serde::{Deserialize, Serialize};
12    use std::sync::Arc;
13
14    /// Configuration for the JWT token server.
15    ///
16    /// # Example
17    /// ```rust,ignore
18    /// let config = JwtServerConfig { issuer: "https://auth.example.com".into(), key_id: "k1".into() };
19    /// ```
20    #[derive(Debug, Clone)]
21    pub struct JwtServerConfig {
22        pub issuer: String,
23        pub key_id: String,
24    }
25
26    impl Default for JwtServerConfig {
27        fn default() -> Self {
28            Self {
29                issuer: "https://auth.example.com".to_string(),
30                key_id: "default".to_string(),
31            }
32        }
33    }
34
35    pub struct JwtServer {
36        config: JwtServerConfig,
37        storage: Arc<dyn AuthStorage>,
38    }
39
40    impl JwtServer {
41        /// Create a new JWT server.
42        ///
43        /// # Example
44        /// ```rust,ignore
45        /// let server = JwtServer::new(JwtServerConfig::default(), storage).await?;
46        /// ```
47        pub async fn new(config: JwtServerConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
48            Ok(Self { config, storage })
49        }
50
51        /// Perform any async initialization after construction.
52        ///
53        /// `JwtServer` requires no async startup work — all state is set up in [`Self::new`].
54        /// This method is provided for API symmetry with other server modules that
55        /// do require an initialization step (e.g., connecting to external services).
56        ///
57        /// # Example
58        /// ```rust,ignore
59        /// server.initialize().await?;
60        /// ```
61        pub async fn initialize(&self) -> Result<()> {
62            Ok(())
63        }
64
65        /// Get the well-known JWT configuration for discovery.
66        ///
67        /// # Example
68        /// ```rust,ignore
69        /// let config = server.get_well_known_jwt_configuration().await?;
70        /// println!("issuer: {}", config.issuer);
71        /// ```
72        pub async fn get_well_known_jwt_configuration(&self) -> Result<JwtWellKnownConfiguration> {
73            Ok(JwtWellKnownConfiguration {
74                issuer: self.config.issuer.clone(),
75                jwks_uri: format!("{}/jwks", self.config.issuer),
76            })
77        }
78
79        /// Store JWT metadata in storage.
80        ///
81        /// # Example
82        /// ```rust,ignore
83        /// let meta = server.get_well_known_jwt_configuration().await?;
84        /// server.store_jwt_metadata(&meta).await?;
85        /// ```
86        pub async fn store_jwt_metadata(&self, metadata: &JwtWellKnownConfiguration) -> Result<()> {
87            let key = format!("jwt_metadata:{}", self.config.issuer);
88            let value = serde_json::to_string(metadata).map_err(|e| {
89                crate::errors::AuthError::internal(format!("Serialization error: {}", e))
90            })?;
91
92            self.storage.store_kv(&key, value.as_bytes(), None).await?;
93            tracing::info!("Stored JWT metadata for issuer: {}", self.config.issuer);
94            Ok(())
95        }
96
97        /// Retrieve JWT metadata from storage.
98        ///
99        /// # Example
100        /// ```rust,ignore
101        /// if let Some(meta) = server.get_stored_metadata().await? {
102        ///     println!("stored issuer: {}", meta.issuer);
103        /// }
104        /// ```
105        pub async fn get_stored_metadata(&self) -> Result<Option<JwtWellKnownConfiguration>> {
106            let key = format!("jwt_metadata:{}", self.config.issuer);
107
108            if let Some(value_bytes) = self.storage.get_kv(&key).await? {
109                let value = String::from_utf8(value_bytes).map_err(|e| {
110                    crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
111                })?;
112                let metadata: JwtWellKnownConfiguration =
113                    serde_json::from_str(&value).map_err(|e| {
114                        crate::errors::AuthError::internal(format!("Deserialization error: {}", e))
115                    })?;
116                Ok(Some(metadata))
117            } else {
118                Ok(None)
119            }
120        }
121
122        /// Store JWT signing key information.
123        ///
124        /// # Example
125        /// ```rust,ignore
126        /// server.store_signing_key("pem-encoded-key-data").await?;
127        /// ```
128        pub async fn store_signing_key(&self, key_data: &str) -> Result<()> {
129            let key = format!("jwt_key:{}", self.config.key_id);
130            self.storage
131                .store_kv(&key, key_data.as_bytes(), None)
132                .await?;
133            tracing::info!("Stored JWT signing key: {}", self.config.key_id);
134            Ok(())
135        }
136    }
137
138    /// Well-known JWT configuration for OIDC discovery.
139    ///
140    /// # Example
141    /// ```rust,ignore
142    /// let wk = JwtWellKnownConfiguration { issuer: "https://auth.example.com".into(), jwks_uri: "https://auth.example.com/jwks".into() };
143    /// ```
144    #[derive(Debug, Clone, Serialize, Deserialize)]
145    pub struct JwtWellKnownConfiguration {
146        pub issuer: String,
147        pub jwks_uri: String,
148    }
149}
150
151/// API Gateway authentication and authorization
152pub mod api_gateway {
153    use crate::errors::Result;
154    use crate::storage::AuthStorage;
155    use std::sync::Arc;
156
157    /// Configuration for the API gateway module.
158    ///
159    /// # Example
160    /// ```rust,ignore
161    /// let config = ApiGatewayConfig { name: "my-gateway".into() };
162    /// ```
163    #[derive(Debug, Clone)]
164    pub struct ApiGatewayConfig {
165        pub name: String,
166    }
167
168    impl Default for ApiGatewayConfig {
169        fn default() -> Self {
170            Self {
171                name: "API Gateway".to_string(),
172            }
173        }
174    }
175
176    pub struct ApiGateway {
177        config: ApiGatewayConfig,
178        storage: Arc<dyn AuthStorage>,
179    }
180
181    impl ApiGateway {
182        /// Create a new API gateway.
183        ///
184        /// # Example
185        /// ```rust,ignore
186        /// let gw = ApiGateway::new(ApiGatewayConfig::default(), storage).await?;
187        /// ```
188        pub async fn new(config: ApiGatewayConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
189            Ok(Self { config, storage })
190        }
191
192        /// Perform any async initialization after construction.
193        ///
194        /// `ApiGateway` requires no async startup work — all state is set up in [`Self::new`].
195        /// This method is provided for API symmetry with other server modules that
196        /// do require an initialization step (e.g., connecting to external services).
197        ///
198        /// # Example
199        /// ```rust,ignore
200        /// gw.initialize().await?;
201        /// ```
202        pub async fn initialize(&self) -> Result<()> {
203            Ok(())
204        }
205
206        /// Store API gateway configuration metadata.
207        ///
208        /// # Example
209        /// ```rust,ignore
210        /// gw.store_gateway_metadata().await?;
211        /// ```
212        pub async fn store_gateway_metadata(&self) -> Result<()> {
213            let key = format!("api_gateway_config:{}", self.config.name);
214            let metadata = serde_json::json!({
215                "name": self.config.name,
216                "initialized_at": chrono::Utc::now().to_rfc3339()
217            });
218            let value = serde_json::to_string(&metadata).map_err(|e| {
219                crate::errors::AuthError::internal(format!("Serialization error: {}", e))
220            })?;
221
222            self.storage.store_kv(&key, value.as_bytes(), None).await?;
223            tracing::info!("Stored API Gateway metadata for: {}", self.config.name);
224            Ok(())
225        }
226
227        /// Store API route configuration.
228        ///
229        /// # Example
230        /// ```rust,ignore
231        /// gw.store_route_config("/api/users", "{\"auth\": true}").await?;
232        /// ```
233        pub async fn store_route_config(&self, route_path: &str, config_data: &str) -> Result<()> {
234            let key = format!("api_gateway_route:{}:{}", self.config.name, route_path);
235            self.storage
236                .store_kv(&key, config_data.as_bytes(), None)
237                .await?;
238            tracing::info!(
239                "Stored route config for {} on gateway: {}",
240                route_path,
241                self.config.name
242            );
243            Ok(())
244        }
245
246        /// Get API route configuration.
247        ///
248        /// # Example
249        /// ```rust,ignore
250        /// if let Some(cfg) = gw.get_route_config("/api/users").await? {
251        ///     println!("route config: {}", cfg);
252        /// }
253        /// ```
254        pub async fn get_route_config(&self, route_path: &str) -> Result<Option<String>> {
255            let key = format!("api_gateway_route:{}:{}", self.config.name, route_path);
256
257            if let Some(config_bytes) = self.storage.get_kv(&key).await? {
258                let config = String::from_utf8(config_bytes).map_err(|e| {
259                    crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
260                })?;
261                Ok(Some(config))
262            } else {
263                Ok(None)
264            }
265        }
266
267        /// Get gateway name from config.
268        ///
269        /// # Example
270        /// ```rust,ignore
271        /// let name = gw.get_gateway_name();
272        /// ```
273        pub fn get_gateway_name(&self) -> &str {
274            &self.config.name
275        }
276    }
277}
278
279/// SAML Identity Provider
280pub mod saml_idp {
281    use crate::errors::Result;
282    use crate::storage::AuthStorage;
283    use serde::{Deserialize, Serialize};
284    use std::sync::Arc;
285
286    /// Configuration for the SAML Identity Provider.
287    ///
288    /// # Example
289    /// ```rust,ignore
290    /// let config = SamlIdpConfig { entity_id: "https://idp.example.com".into() };
291    /// ```
292    #[derive(Debug, Clone)]
293    pub struct SamlIdpConfig {
294        pub entity_id: String,
295    }
296
297    impl Default for SamlIdpConfig {
298        fn default() -> Self {
299            Self {
300                entity_id: "https://auth.example.com".to_string(),
301            }
302        }
303    }
304
305    pub struct SamlIdentityProvider {
306        config: SamlIdpConfig,
307        storage: Arc<dyn AuthStorage>,
308    }
309
310    impl SamlIdentityProvider {
311        /// Create a new SAML Identity Provider.
312        ///
313        /// # Example
314        /// ```rust,ignore
315        /// let idp = SamlIdentityProvider::new(SamlIdpConfig::default(), storage).await?;
316        /// ```
317        pub async fn new(config: SamlIdpConfig, storage: Arc<dyn AuthStorage>) -> Result<Self> {
318            Ok(Self { config, storage })
319        }
320
321        /// Perform any async initialization after construction.
322        ///
323        /// `SamlIdentityProvider` requires no async startup work — all state is set up in [`Self::new`].
324        /// This method is provided for API symmetry with other server modules that
325        /// do require an initialization step (e.g., connecting to external services).
326        ///
327        /// # Example
328        /// ```rust,ignore
329        /// idp.initialize().await?;
330        /// ```
331        pub async fn initialize(&self) -> Result<()> {
332            Ok(())
333        }
334
335        /// Get the SAML metadata for this identity provider.
336        ///
337        /// # Example
338        /// ```rust,ignore
339        /// let meta = idp.get_metadata().await?;
340        /// println!("entity: {}", meta.entity_id);
341        /// ```
342        pub async fn get_metadata(&self) -> Result<SamlMetadata> {
343            Ok(SamlMetadata {
344                entity_id: self.config.entity_id.clone(),
345            })
346        }
347
348        /// Store SAML metadata in storage.
349        ///
350        /// # Example
351        /// ```rust,ignore
352        /// idp.store_saml_metadata(&meta).await?;
353        /// ```
354        pub async fn store_saml_metadata(&self, metadata: &SamlMetadata) -> Result<()> {
355            let key = format!("saml_metadata:{}", self.config.entity_id);
356            let value = serde_json::to_string(metadata).map_err(|e| {
357                crate::errors::AuthError::internal(format!("Serialization error: {}", e))
358            })?;
359
360            self.storage.store_kv(&key, value.as_bytes(), None).await?;
361            tracing::info!("Stored SAML metadata for entity: {}", self.config.entity_id);
362            Ok(())
363        }
364
365        /// Store SAML assertion.
366        ///
367        /// # Example
368        /// ```rust,ignore
369        /// idp.store_assertion("assertion-1", "<xml>...</xml>").await?;
370        /// ```
371        pub async fn store_assertion(
372            &self,
373            assertion_id: &str,
374            assertion_data: &str,
375        ) -> Result<()> {
376            let key = format!("saml_assertion:{}:{}", self.config.entity_id, assertion_id);
377            self.storage
378                .store_kv(
379                    &key,
380                    assertion_data.as_bytes(),
381                    Some(std::time::Duration::from_secs(3600)),
382                )
383                .await?;
384            tracing::info!(
385                "Stored SAML assertion {} for entity: {}",
386                assertion_id,
387                self.config.entity_id
388            );
389            Ok(())
390        }
391
392        /// Retrieve SAML assertion.
393        ///
394        /// # Example
395        /// ```rust,ignore
396        /// if let Some(xml) = idp.get_assertion("assertion-1").await? {
397        ///     println!("assertion: {}", xml);
398        /// }
399        /// ```
400        pub async fn get_assertion(&self, assertion_id: &str) -> Result<Option<String>> {
401            let key = format!("saml_assertion:{}:{}", self.config.entity_id, assertion_id);
402
403            if let Some(assertion_bytes) = self.storage.get_kv(&key).await? {
404                let assertion = String::from_utf8(assertion_bytes).map_err(|e| {
405                    crate::errors::AuthError::internal(format!("UTF-8 conversion error: {}", e))
406                })?;
407                Ok(Some(assertion))
408            } else {
409                Ok(None)
410            }
411        }
412    }
413
414    /// SAML metadata document.
415    ///
416    /// # Example
417    /// ```rust,ignore
418    /// let meta = SamlMetadata { entity_id: "https://idp.example.com".into() };
419    /// ```
420    #[derive(Debug, Clone, Serialize, Deserialize)]
421    pub struct SamlMetadata {
422        pub entity_id: String,
423    }
424}
425
426// Additional server-side modules
427
428pub mod consent {
429    //! User consent management (OAuth 2.0 / OIDC consent screen logic)
430
431    use crate::errors::Result;
432    use crate::storage::AuthStorage;
433    use serde::{Deserialize, Serialize};
434    use std::collections::HashMap;
435    use std::sync::Arc;
436
437    /// Configuration for the consent module.
438    ///
439    /// # Example
440    /// ```rust,ignore
441    /// let config = ConsentConfig { require_explicit_consent: true, remember_consent_ttl_secs: 3600 };
442    /// ```
443    #[derive(Debug, Clone, Serialize, Deserialize)]
444    pub struct ConsentConfig {
445        /// Require explicit consent for every OAuth 2.0 authorization request.
446        pub require_explicit_consent: bool,
447        /// Remember consent decisions for this many seconds (0 = never remember).
448        pub remember_consent_ttl_secs: u64,
449    }
450
451    impl Default for ConsentConfig {
452        fn default() -> Self {
453            Self {
454                require_explicit_consent: true,
455                remember_consent_ttl_secs: 86_400, // 24 hours
456            }
457        }
458    }
459
460    /// A stored consent decision.
461    ///
462    /// # Example
463    /// ```rust,ignore
464    /// let record = ConsentRecord {
465    ///     user_id: "u1".into(), client_id: "c1".into(),
466    ///     scopes: vec!["openid".into()], granted_at: 0, expires_at: None,
467    /// };
468    /// ```
469    #[derive(Debug, Clone, Serialize, Deserialize)]
470    pub struct ConsentRecord {
471        pub user_id: String,
472        pub client_id: String,
473        pub scopes: Vec<String>,
474        pub granted_at: u64,
475        pub expires_at: Option<u64>,
476    }
477
478    /// Manages user consent decisions with optional storage backend for persistence.
479    ///
480    /// When constructed with [`ConsentManager::new_with_storage`] consent decisions
481    /// are written through to `AuthStorage` so they survive process restarts.
482    /// When constructed with [`ConsentManager::new`] decisions are kept in-process
483    /// only (suitable for testing or single-node scenarios with short TTLs).
484    pub struct ConsentManager {
485        config: ConsentConfig,
486        /// Write-through in-process cache.  Always populated on reads.
487        records: HashMap<String, ConsentRecord>,
488        storage: Option<Arc<dyn AuthStorage>>,
489    }
490
491    impl std::fmt::Debug for ConsentManager {
492        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
493            f.debug_struct("ConsentManager")
494                .field("config", &self.config)
495                .field("records", &self.records)
496                .field("storage", &self.storage.is_some())
497                .finish()
498        }
499    }
500
501    impl Default for ConsentManager {
502        fn default() -> Self {
503            Self::new(ConsentConfig::default())
504        }
505    }
506
507    impl ConsentManager {
508        /// Create an in-memory-only `ConsentManager`.
509        ///
510        /// # Example
511        /// ```rust,ignore
512        /// let mgr = ConsentManager::new(ConsentConfig::default());
513        /// ```
514        pub fn new(config: ConsentConfig) -> Self {
515            Self {
516                config,
517                records: HashMap::new(),
518                storage: None,
519            }
520        }
521
522        /// Create a storage-backed `ConsentManager` that persists decisions across
523        /// restarts using the provided `AuthStorage` implementation.
524        ///
525        /// # Example
526        /// ```rust,ignore
527        /// let mgr = ConsentManager::new_with_storage(ConsentConfig::default(), storage);
528        /// ```
529        pub fn new_with_storage(config: ConsentConfig, storage: Arc<dyn AuthStorage>) -> Self {
530            Self {
531                config,
532                records: HashMap::new(),
533                storage: Some(storage),
534            }
535        }
536
537        /// Storage key for a consent record.
538        fn storage_key(user_id: &str, client_id: &str) -> String {
539            format!("consent:{}:{}", user_id, client_id)
540        }
541
542        /// Record a consent decision, persisting to storage if configured.
543        ///
544        /// # Example
545        /// ```rust,ignore
546        /// mgr.grant(record).await?;
547        /// ```
548        pub async fn grant(&mut self, record: ConsentRecord) -> Result<()> {
549            let key = format!("{}:{}", record.user_id, record.client_id);
550            if let Some(storage) = &self.storage {
551                let storage_key = Self::storage_key(&record.user_id, &record.client_id);
552                let ttl = if record.expires_at.is_some() {
553                    Some(std::time::Duration::from_secs(
554                        self.config.remember_consent_ttl_secs,
555                    ))
556                } else {
557                    None
558                };
559                let bytes = serde_json::to_vec(&record).map_err(|e| {
560                    crate::errors::AuthError::internal(format!(
561                        "Consent serialization error: {}",
562                        e
563                    ))
564                })?;
565                storage.store_kv(&storage_key, &bytes, ttl).await?;
566            }
567            self.records.insert(key, record);
568            Ok(())
569        }
570
571        /// Revoke a previously granted consent, removing it from storage if configured.
572        ///
573        /// # Example
574        /// ```rust,ignore
575        /// let was_present = mgr.revoke("user-1", "client-1").await?;
576        /// ```
577        pub async fn revoke(&mut self, user_id: &str, client_id: &str) -> Result<bool> {
578            let key = format!("{}:{}", user_id, client_id);
579            if let Some(storage) = &self.storage {
580                let storage_key = Self::storage_key(user_id, client_id);
581                // Best-effort delete; ignore "not found" errors.
582                let _ = storage.delete_kv(&storage_key).await;
583            }
584            Ok(self.records.remove(&key).is_some())
585        }
586
587        /// Check whether consent for the given scopes has already been granted.
588        /// Checks the in-process cache first; falls back to storage on a cache miss.
589        ///
590        /// # Example
591        /// ```rust,ignore
592        /// let ok = mgr.has_consent("user-1", "client-1", &["openid".into()]).await?;
593        /// ```
594        pub async fn has_consent(
595            &mut self,
596            user_id: &str,
597            client_id: &str,
598            scopes: &[String],
599        ) -> Result<bool> {
600            if !self.config.require_explicit_consent {
601                return Ok(true);
602            }
603            let key = format!("{}:{}", user_id, client_id);
604
605            // Cache miss: try storage.
606            if !self.records.contains_key(&key)
607                && let Some(storage) = &self.storage
608            {
609                let storage_key = Self::storage_key(user_id, client_id);
610                if let Ok(Some(bytes)) = storage.get_kv(&storage_key).await
611                    && let Ok(record) = serde_json::from_slice::<ConsentRecord>(&bytes)
612                {
613                    self.records.insert(key.clone(), record);
614                }
615            }
616
617            Ok(self
618                .records
619                .get(&key)
620                .is_some_and(|record| scopes.iter().all(|s| record.scopes.contains(s))))
621        }
622
623        /// Return the configuration in use.
624        ///
625        /// # Example
626        /// ```rust,ignore
627        /// let cfg = mgr.config();
628        /// assert!(cfg.require_explicit_consent);
629        /// ```
630        pub fn config(&self) -> &ConsentConfig {
631            &self.config
632        }
633    }
634}
635
636pub mod introspection {
637    //! Token introspection endpoint (RFC 7662)
638
639    use serde::{Deserialize, Serialize};
640
641    /// Configuration for the token introspection module.
642    ///
643    /// # Example
644    /// ```rust,ignore
645    /// let config = IntrospectionConfig { restrict_to_registered_servers: true, include_claims: false };
646    /// ```
647    #[derive(Debug, Clone, Serialize, Deserialize)]
648    pub struct IntrospectionConfig {
649        /// Allow only registered resource servers to call the introspection endpoint.
650        pub restrict_to_registered_servers: bool,
651        /// Include full JWT claims in the response.
652        pub include_claims: bool,
653    }
654
655    impl Default for IntrospectionConfig {
656        fn default() -> Self {
657            Self {
658                restrict_to_registered_servers: false,
659                include_claims: true,
660            }
661        }
662    }
663
664    /// Response body for RFC 7662 token introspection.
665    ///
666    /// # Example
667    /// ```rust,ignore
668    /// let resp = IntrospectionResponse::inactive();
669    /// assert!(!resp.active);
670    /// ```
671    #[derive(Debug, Clone, Serialize, Deserialize)]
672    pub struct IntrospectionResponse {
673        pub active: bool,
674        #[serde(skip_serializing_if = "Option::is_none")]
675        pub scope: Option<String>,
676        #[serde(skip_serializing_if = "Option::is_none")]
677        pub client_id: Option<String>,
678        #[serde(skip_serializing_if = "Option::is_none")]
679        pub sub: Option<String>,
680        #[serde(skip_serializing_if = "Option::is_none")]
681        pub exp: Option<i64>,
682        #[serde(skip_serializing_if = "Option::is_none")]
683        pub iat: Option<i64>,
684        #[serde(skip_serializing_if = "Option::is_none")]
685        pub token_type: Option<String>,
686    }
687
688    impl IntrospectionResponse {
689        /// Return an `active: false` response (e.g., for invalid/expired tokens).
690        ///
691        /// # Example
692        /// ```rust,ignore
693        /// let resp = IntrospectionResponse::inactive();
694        /// ```
695        pub fn inactive() -> Self {
696            Self {
697                active: false,
698                scope: None,
699                client_id: None,
700                sub: None,
701                exp: None,
702                iat: None,
703                token_type: None,
704            }
705        }
706    }
707
708    /// Handles token introspection logic for the server.
709    #[derive(Debug, Default)]
710    pub struct IntrospectionManager {
711        config: IntrospectionConfig,
712    }
713
714    impl IntrospectionManager {
715        /// Create a new `IntrospectionManager`.
716        ///
717        /// # Example
718        /// ```rust,ignore
719        /// let mgr = IntrospectionManager::new(IntrospectionConfig::default());
720        /// ```
721        pub fn new(config: IntrospectionConfig) -> Self {
722            Self { config }
723        }
724
725        /// Return the configuration in use.
726        ///
727        /// # Example
728        /// ```rust,ignore
729        /// let cfg = mgr.config();
730        /// ```
731        pub fn config(&self) -> &IntrospectionConfig {
732            &self.config
733        }
734    }
735}
736
737pub mod device_flow_server {
738    //! Device Authorization Grant server-side implementation (RFC 8628)
739
740    use crate::errors::Result;
741    use crate::storage::AuthStorage;
742    use serde::{Deserialize, Serialize};
743    use std::collections::HashMap;
744    use std::sync::Arc;
745
746    /// Configuration for the device flow module.
747    ///
748    /// # Example
749    /// ```rust,ignore
750    /// let config = DeviceFlowConfig::default();
751    /// assert_eq!(config.user_code_length, 8);
752    /// ```
753    #[derive(Debug, Clone, Serialize, Deserialize)]
754    pub struct DeviceFlowConfig {
755        /// Length of the user code (e.g., 8 characters).
756        pub user_code_length: usize,
757        /// How long a device code is valid (seconds).
758        pub device_code_ttl_secs: u64,
759        /// Minimum interval between polling requests (seconds).
760        pub polling_interval_secs: u64,
761        /// Verification URI shown to the user.
762        pub verification_uri: String,
763    }
764
765    impl Default for DeviceFlowConfig {
766        fn default() -> Self {
767            Self {
768                user_code_length: 8,
769                device_code_ttl_secs: 1800, // 30 minutes
770                polling_interval_secs: 5,
771                verification_uri: "https://example.com/device".to_string(),
772            }
773        }
774    }
775
776    /// State of a pending device authorization request.
777    ///
778    /// # Example
779    /// ```rust,ignore
780    /// let state = DeviceAuthState::Pending;
781    /// ```
782    #[derive(Debug, Clone, Serialize, Deserialize)]
783    pub enum DeviceAuthState {
784        Pending,
785        Authorized { access_token: String },
786        Denied,
787        Expired,
788    }
789
790    /// A device authorization record.
791    ///
792    /// # Example
793    /// ```rust,ignore
794    /// let rec = DeviceAuthRecord {
795    ///     device_code: "dc".into(), user_code: "UC".into(),
796    ///     client_id: "c1".into(), scopes: vec![], state: DeviceAuthState::Pending, expires_at: 0,
797    /// };
798    /// ```
799    #[derive(Debug, Clone, Serialize, Deserialize)]
800    pub struct DeviceAuthRecord {
801        pub device_code: String,
802        pub user_code: String,
803        pub client_id: String,
804        pub scopes: Vec<String>,
805        pub state: DeviceAuthState,
806        pub expires_at: u64,
807    }
808
809    /// Manages device authorization flow state with optional storage backend.
810    ///
811    /// When constructed with [`DeviceFlowManager::new_with_storage`] device auth
812    /// records are written through to `AuthStorage` with TTL equal to
813    /// `device_code_ttl_secs`, surviving process restarts and enabling
814    /// multi-instance deployments.  When constructed with [`DeviceFlowManager::new`]
815    /// records are kept in-process only.
816    pub struct DeviceFlowManager {
817        config: DeviceFlowConfig,
818        /// Write-through in-process cache keyed by `device_code`.
819        records: HashMap<String, DeviceAuthRecord>,
820        storage: Option<Arc<dyn AuthStorage>>,
821    }
822
823    impl std::fmt::Debug for DeviceFlowManager {
824        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825            f.debug_struct("DeviceFlowManager")
826                .field("config", &self.config)
827                .field("records", &self.records)
828                .field("storage", &self.storage.is_some())
829                .finish()
830        }
831    }
832
833    impl Default for DeviceFlowManager {
834        fn default() -> Self {
835            Self::new(DeviceFlowConfig::default())
836        }
837    }
838
839    impl DeviceFlowManager {
840        /// Create an in-memory-only `DeviceFlowManager`.
841        ///
842        /// # Example
843        /// ```rust,ignore
844        /// let mgr = DeviceFlowManager::new(DeviceFlowConfig::default());
845        /// ```
846        pub fn new(config: DeviceFlowConfig) -> Self {
847            Self {
848                config,
849                records: HashMap::new(),
850                storage: None,
851            }
852        }
853
854        /// Create a storage-backed `DeviceFlowManager` that persists device
855        /// authorization records across restarts.
856        ///
857        /// # Example
858        /// ```rust,ignore
859        /// let mgr = DeviceFlowManager::new_with_storage(DeviceFlowConfig::default(), storage);
860        /// ```
861        pub fn new_with_storage(config: DeviceFlowConfig, storage: Arc<dyn AuthStorage>) -> Self {
862            Self {
863                config,
864                records: HashMap::new(),
865                storage: Some(storage),
866            }
867        }
868
869        /// Storage key for a device auth record.
870        fn storage_key(device_code: &str) -> String {
871            format!("device_flow:{}", device_code)
872        }
873
874        /// Persist a new device authorization record.
875        ///
876        /// # Example
877        /// ```rust,ignore
878        /// mgr.register(record).await?;
879        /// ```
880        pub async fn register(&mut self, record: DeviceAuthRecord) -> Result<()> {
881            if let Some(storage) = &self.storage {
882                let key = Self::storage_key(&record.device_code);
883                let ttl = Some(std::time::Duration::from_secs(
884                    self.config.device_code_ttl_secs,
885                ));
886                let bytes = serde_json::to_vec(&record).map_err(|e| {
887                    crate::errors::AuthError::internal(format!(
888                        "DeviceFlowRecord serialization error: {}",
889                        e
890                    ))
891                })?;
892                storage.store_kv(&key, &bytes, ttl).await?;
893            }
894            self.records.insert(record.device_code.clone(), record);
895            Ok(())
896        }
897
898        /// Look up a record by device code, checking storage on a cache miss.
899        ///
900        /// # Example
901        /// ```rust,ignore
902        /// if let Some(rec) = mgr.get("device-code-1").await? {
903        ///     println!("state: {:?}", rec.state);
904        /// }
905        /// ```
906        pub async fn get(&mut self, device_code: &str) -> Result<Option<DeviceAuthRecord>> {
907            if let Some(record) = self.records.get(device_code) {
908                return Ok(Some(record.clone()));
909            }
910            if let Some(storage) = &self.storage {
911                let key = Self::storage_key(device_code);
912                if let Some(bytes) = storage.get_kv(&key).await?
913                    && let Ok(record) = serde_json::from_slice::<DeviceAuthRecord>(&bytes)
914                {
915                    self.records.insert(device_code.to_string(), record.clone());
916                    return Ok(Some(record));
917                }
918            }
919            Ok(None)
920        }
921
922        /// Approve the authorization request for a given user code.
923        ///
924        /// # Example
925        /// ```rust,ignore
926        /// let approved = mgr.approve("USER-CODE", "access-token".into()).await?;
927        /// ```
928        pub async fn approve(&mut self, user_code: &str, access_token: String) -> Result<bool> {
929            // Find the matching record (by user_code, not device_code).
930            let device_code = self
931                .records
932                .values()
933                .find(|r| r.user_code == user_code)
934                .map(|r| r.device_code.clone());
935
936            if let Some(dc) = device_code {
937                if let Some(record) = self.records.get_mut(&dc) {
938                    record.state = DeviceAuthState::Authorized {
939                        access_token: access_token.clone(),
940                    };
941                    if let Some(storage) = &self.storage {
942                        let key = Self::storage_key(&dc);
943                        let bytes = serde_json::to_vec(&*record).map_err(|e| {
944                            crate::errors::AuthError::internal(format!(
945                                "DeviceFlowRecord serialization error: {}",
946                                e
947                            ))
948                        })?;
949                        // Preserve remaining TTL rather than resetting it.
950                        storage.store_kv(&key, &bytes, None).await?;
951                    }
952                }
953                return Ok(true);
954            }
955            Ok(false)
956        }
957
958        /// Return the configuration in use.
959        ///
960        /// # Example
961        /// ```rust,ignore
962        /// let cfg = mgr.config();
963        /// assert_eq!(cfg.user_code_length, 8);
964        /// ```
965        pub fn config(&self) -> &DeviceFlowConfig {
966            &self.config
967        }
968    }
969}
970
971#[cfg(test)]
972mod tests {
973    use super::*;
974    use crate::storage::MemoryStorage;
975    use std::sync::Arc;
976    use std::time::{SystemTime, UNIX_EPOCH};
977
978    fn now_secs() -> u64 {
979        SystemTime::now()
980            .duration_since(UNIX_EPOCH)
981            .unwrap()
982            .as_secs()
983    }
984
985    // ── JwtServer ───────────────────────────────────────────────────────
986
987    #[tokio::test]
988    async fn test_jwt_server_store_and_get_metadata() {
989        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
990        let config = jwt_server::JwtServerConfig {
991            issuer: "https://auth.example.com".into(),
992            key_id: "key_1".into(),
993        };
994        let server = jwt_server::JwtServer::new(config, storage).await.unwrap();
995        server.initialize().await.unwrap();
996
997        let wkc = server.get_well_known_jwt_configuration().await.unwrap();
998        assert_eq!(wkc.issuer, "https://auth.example.com");
999
1000        server.store_jwt_metadata(&wkc).await.unwrap();
1001        let retrieved = server.get_stored_metadata().await.unwrap();
1002        assert!(retrieved.is_some());
1003    }
1004
1005    #[tokio::test]
1006    async fn test_jwt_server_store_signing_key() {
1007        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1008        let config = jwt_server::JwtServerConfig {
1009            issuer: "https://auth.example.com".into(),
1010            key_id: "key_2".into(),
1011        };
1012        let server = jwt_server::JwtServer::new(config, storage).await.unwrap();
1013        server.store_signing_key("test-key-data").await.unwrap();
1014    }
1015
1016    // ── ApiGateway ──────────────────────────────────────────────────────
1017
1018    #[tokio::test]
1019    async fn test_api_gateway_store_and_get_route() {
1020        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1021        let config = api_gateway::ApiGatewayConfig {
1022            name: "test-gw".into(),
1023        };
1024        let gw = api_gateway::ApiGateway::new(config, storage).await.unwrap();
1025        gw.initialize().await.unwrap();
1026        assert_eq!(gw.get_gateway_name(), "test-gw");
1027
1028        gw.store_route_config("/api/v1/users", r#"{"auth":"required"}"#)
1029            .await
1030            .unwrap();
1031        let route = gw.get_route_config("/api/v1/users").await.unwrap();
1032        assert!(route.is_some());
1033    }
1034
1035    #[tokio::test]
1036    async fn test_api_gateway_get_route_not_found() {
1037        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1038        let config = api_gateway::ApiGatewayConfig { name: "gw2".into() };
1039        let gw = api_gateway::ApiGateway::new(config, storage).await.unwrap();
1040        assert!(gw.get_route_config("/nope").await.unwrap().is_none());
1041    }
1042
1043    // ── SAML IdP ────────────────────────────────────────────────────────
1044
1045    #[tokio::test]
1046    async fn test_saml_idp_store_and_get_assertion() {
1047        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1048        let config = saml_idp::SamlIdpConfig {
1049            entity_id: "urn:example:idp".into(),
1050        };
1051        let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1052            .await
1053            .unwrap();
1054        saml.initialize().await.unwrap();
1055        saml.store_assertion("assert_1", "<Assertion/>")
1056            .await
1057            .unwrap();
1058        let a = saml.get_assertion("assert_1").await.unwrap();
1059        assert_eq!(a.as_deref(), Some("<Assertion/>"));
1060    }
1061
1062    #[tokio::test]
1063    async fn test_saml_idp_get_assertion_not_found() {
1064        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1065        let config = saml_idp::SamlIdpConfig {
1066            entity_id: "urn:example:idp2".into(),
1067        };
1068        let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1069            .await
1070            .unwrap();
1071        assert!(saml.get_assertion("nope").await.unwrap().is_none());
1072    }
1073
1074    #[tokio::test]
1075    async fn test_saml_idp_metadata() {
1076        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1077        let config = saml_idp::SamlIdpConfig {
1078            entity_id: "urn:example:idp3".into(),
1079        };
1080        let saml = saml_idp::SamlIdentityProvider::new(config, storage)
1081            .await
1082            .unwrap();
1083        let meta = saml.get_metadata().await.unwrap();
1084        assert_eq!(meta.entity_id, "urn:example:idp3");
1085    }
1086
1087    // ── ConsentManager ──────────────────────────────────────────────────
1088
1089    #[tokio::test]
1090    async fn test_consent_grant_and_check() {
1091        let config = consent::ConsentConfig {
1092            require_explicit_consent: true,
1093            remember_consent_ttl_secs: 3600,
1094        };
1095        let mut cm = consent::ConsentManager::new(config);
1096        let record = consent::ConsentRecord {
1097            user_id: "user1".into(),
1098            client_id: "client1".into(),
1099            scopes: vec!["read".into(), "write".into()],
1100            granted_at: now_secs(),
1101            expires_at: Some(now_secs() + 3600),
1102        };
1103        cm.grant(record).await.unwrap();
1104        assert!(
1105            cm.has_consent("user1", "client1", &["read".into()])
1106                .await
1107                .unwrap()
1108        );
1109    }
1110
1111    #[tokio::test]
1112    async fn test_consent_no_consent_by_default() {
1113        let config = consent::ConsentConfig {
1114            require_explicit_consent: true,
1115            remember_consent_ttl_secs: 3600,
1116        };
1117        let mut cm = consent::ConsentManager::new(config);
1118        assert!(
1119            !cm.has_consent("user1", "client1", &["read".into()])
1120                .await
1121                .unwrap()
1122        );
1123    }
1124
1125    #[tokio::test]
1126    async fn test_consent_revoke() {
1127        let config = consent::ConsentConfig {
1128            require_explicit_consent: true,
1129            remember_consent_ttl_secs: 3600,
1130        };
1131        let mut cm = consent::ConsentManager::new(config);
1132        cm.grant(consent::ConsentRecord {
1133            user_id: "user2".into(),
1134            client_id: "client2".into(),
1135            scopes: vec!["read".into()],
1136            granted_at: now_secs(),
1137            expires_at: None,
1138        })
1139        .await
1140        .unwrap();
1141        let revoked = cm.revoke("user2", "client2").await.unwrap();
1142        assert!(revoked);
1143        assert!(
1144            !cm.has_consent("user2", "client2", &["read".into()])
1145                .await
1146                .unwrap()
1147        );
1148    }
1149
1150    #[tokio::test]
1151    async fn test_consent_revoke_nonexistent() {
1152        let config = consent::ConsentConfig {
1153            require_explicit_consent: true,
1154            remember_consent_ttl_secs: 3600,
1155        };
1156        let mut cm = consent::ConsentManager::new(config);
1157        let revoked = cm.revoke("ghost", "ghost_client").await.unwrap();
1158        assert!(!revoked);
1159    }
1160
1161    #[tokio::test]
1162    async fn test_consent_with_storage() {
1163        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1164        let config = consent::ConsentConfig {
1165            require_explicit_consent: true,
1166            remember_consent_ttl_secs: 3600,
1167        };
1168        let mut cm = consent::ConsentManager::new_with_storage(config, storage);
1169        cm.grant(consent::ConsentRecord {
1170            user_id: "user3".into(),
1171            client_id: "client3".into(),
1172            scopes: vec!["openid".into()],
1173            granted_at: now_secs(),
1174            expires_at: None,
1175        })
1176        .await
1177        .unwrap();
1178        assert!(
1179            cm.has_consent("user3", "client3", &["openid".into()])
1180                .await
1181                .unwrap()
1182        );
1183    }
1184
1185    // ── DeviceFlowManager ───────────────────────────────────────────────
1186
1187    #[tokio::test]
1188    async fn test_device_flow_register_and_get() {
1189        let config = device_flow_server::DeviceFlowConfig {
1190            user_code_length: 8,
1191            device_code_ttl_secs: 300,
1192            polling_interval_secs: 5,
1193            verification_uri: "https://auth.example.com/device".into(),
1194        };
1195        let mut df = device_flow_server::DeviceFlowManager::new(config);
1196        df.register(device_flow_server::DeviceAuthRecord {
1197            device_code: "dev_code_1".into(),
1198            user_code: "ABCD-EFGH".into(),
1199            client_id: "mobile_app".into(),
1200            scopes: vec!["read".into()],
1201            state: device_flow_server::DeviceAuthState::Pending,
1202            expires_at: now_secs() + 300,
1203        })
1204        .await
1205        .unwrap();
1206        let record = df.get("dev_code_1").await.unwrap();
1207        assert!(record.is_some());
1208        assert_eq!(record.unwrap().user_code, "ABCD-EFGH");
1209    }
1210
1211    #[tokio::test]
1212    async fn test_device_flow_get_not_found() {
1213        let config = device_flow_server::DeviceFlowConfig {
1214            user_code_length: 8,
1215            device_code_ttl_secs: 300,
1216            polling_interval_secs: 5,
1217            verification_uri: "https://auth.example.com/device".into(),
1218        };
1219        let mut df = device_flow_server::DeviceFlowManager::new(config);
1220        assert!(df.get("nonexistent").await.unwrap().is_none());
1221    }
1222
1223    #[tokio::test]
1224    async fn test_device_flow_approve() {
1225        let config = device_flow_server::DeviceFlowConfig {
1226            user_code_length: 8,
1227            device_code_ttl_secs: 300,
1228            polling_interval_secs: 5,
1229            verification_uri: "https://auth.example.com/device".into(),
1230        };
1231        let mut df = device_flow_server::DeviceFlowManager::new(config);
1232        df.register(device_flow_server::DeviceAuthRecord {
1233            device_code: "dev_code_2".into(),
1234            user_code: "WXYZ-1234".into(),
1235            client_id: "tv_app".into(),
1236            scopes: vec!["read".into()],
1237            state: device_flow_server::DeviceAuthState::Pending,
1238            expires_at: now_secs() + 300,
1239        })
1240        .await
1241        .unwrap();
1242        let approved = df
1243            .approve("WXYZ-1234", "access_token_here".into())
1244            .await
1245            .unwrap();
1246        assert!(approved);
1247
1248        let record = df.get("dev_code_2").await.unwrap().unwrap();
1249        matches!(
1250            record.state,
1251            device_flow_server::DeviceAuthState::Authorized { .. }
1252        );
1253    }
1254
1255    #[tokio::test]
1256    async fn test_device_flow_approve_nonexistent() {
1257        let config = device_flow_server::DeviceFlowConfig {
1258            user_code_length: 8,
1259            device_code_ttl_secs: 300,
1260            polling_interval_secs: 5,
1261            verification_uri: "https://auth.example.com/device".into(),
1262        };
1263        let mut df = device_flow_server::DeviceFlowManager::new(config);
1264        let approved = df.approve("NO_CODE", "token".into()).await.unwrap();
1265        assert!(!approved);
1266    }
1267
1268    #[tokio::test]
1269    async fn test_device_flow_with_storage() {
1270        let storage: Arc<dyn crate::storage::AuthStorage> = Arc::new(MemoryStorage::new());
1271        let config = device_flow_server::DeviceFlowConfig {
1272            user_code_length: 8,
1273            device_code_ttl_secs: 300,
1274            polling_interval_secs: 5,
1275            verification_uri: "https://auth.example.com/device".into(),
1276        };
1277        let mut df = device_flow_server::DeviceFlowManager::new_with_storage(config, storage);
1278        df.register(device_flow_server::DeviceAuthRecord {
1279            device_code: "stored_code".into(),
1280            user_code: "ST0R-ED00".into(),
1281            client_id: "app".into(),
1282            scopes: vec![],
1283            state: device_flow_server::DeviceAuthState::Pending,
1284            expires_at: now_secs() + 300,
1285        })
1286        .await
1287        .unwrap();
1288        let record = df.get("stored_code").await.unwrap();
1289        assert!(record.is_some());
1290    }
1291
1292    // ── IntrospectionManager ────────────────────────────────────────────
1293
1294    #[test]
1295    fn test_introspection_inactive_response() {
1296        let resp = introspection::IntrospectionResponse::inactive();
1297        assert!(!resp.active);
1298    }
1299}