auth_framework/server/token_exchange/
token_exchange_common.rs

1//! # Common Token Exchange Components
2//!
3//! This module provides shared interfaces, utilities, and types used by both
4//! the basic `TokenExchangeManager` and the advanced `AdvancedTokenExchangeManager`.
5//!
6//! ## Architecture
7//!
8//! - **TokenExchangeService**: Common trait implemented by both managers
9//! - **ValidationUtils**: Shared validation logic to reduce code duplication
10//! - **CommonTypes**: Shared data structures and enums
11//! - **TokenExchangeFactory**: Factory for creating appropriate manager instances
12
13use crate::errors::{AuthError, Result};
14use async_trait::async_trait;
15use chrono::{DateTime, Utc};
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18
19/// Common token exchange service trait implemented by both managers
20#[async_trait]
21pub trait TokenExchangeService: Send + Sync {
22    /// Exchange token request type
23    type Request: Send + Sync;
24
25    /// Exchange token response type
26    type Response: Send + Sync;
27
28    /// Configuration type
29    type Config: Send + Sync;
30
31    /// Exchange a token according to RFC 8693
32    async fn exchange_token(&self, request: Self::Request) -> Result<Self::Response>;
33
34    /// Validate a token of the specified type
35    async fn validate_token(&self, token: &str, token_type: &str) -> Result<TokenValidationResult>;
36
37    /// Get supported subject token types
38    fn supported_subject_token_types(&self) -> Vec<String>;
39
40    /// Get supported requested token types
41    fn supported_requested_token_types(&self) -> Vec<String>;
42    /// Get service capabilities
43    fn capabilities(&self) -> TokenExchangeCapabilities;
44}
45
46/// Result of token validation
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct TokenValidationResult {
49    /// Whether the token is valid
50    pub is_valid: bool,
51
52    /// Token subject/principal
53    pub subject: Option<String>,
54
55    /// Token issuer
56    pub issuer: Option<String>,
57
58    /// Token audience
59    pub audience: Vec<String>,
60
61    /// Token scopes
62    pub scopes: Vec<String>,
63
64    /// Token expiration time
65    pub expires_at: Option<DateTime<Utc>>,
66
67    /// Additional token metadata
68    pub metadata: HashMap<String, serde_json::Value>,
69
70    /// Validation errors/warnings
71    pub validation_messages: Vec<String>,
72}
73
74/// Capabilities of a token exchange service
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct TokenExchangeCapabilities {
77    /// Supports basic RFC 8693 token exchange
78    pub basic_exchange: bool,
79
80    /// Supports multi-party delegation chains
81    pub multi_party_chains: bool,
82
83    /// Supports context preservation
84    pub context_preservation: bool,
85
86    /// Supports audit trail generation
87    pub audit_trail: bool,
88
89    /// Supports session integration
90    pub session_integration: bool,
91
92    /// Supports JWT cryptographic operations
93    pub jwt_operations: bool,
94
95    /// Supports policy-driven exchange control
96    pub policy_control: bool,
97
98    /// Supports cross-domain exchanges
99    pub cross_domain_exchange: bool,
100
101    /// Maximum delegation depth supported (0 = no limit for advanced)
102    pub max_delegation_depth: usize,
103
104    /// Service complexity level
105    pub complexity_level: ServiceComplexityLevel,
106}
107
108/// Service complexity level for choosing appropriate manager
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
110pub enum ServiceComplexityLevel {
111    /// Simple, lightweight operations
112    Basic,
113    /// Enterprise-grade with advanced features
114    Advanced,
115}
116
117/// Common token exchange validation utilities
118pub struct ValidationUtils;
119
120impl ValidationUtils {
121    /// Validate RFC 8693 grant type
122    pub fn validate_grant_type(grant_type: &str) -> Result<()> {
123        if grant_type != "urn:ietf:params:oauth:grant-type:token-exchange" {
124            return Err(AuthError::InvalidRequest(
125                "Invalid grant type for token exchange".to_string(),
126            ));
127        }
128        Ok(())
129    }
130
131    /// Validate token type against supported types
132    pub fn validate_token_type(token_type: &str, supported_types: &[String]) -> Result<()> {
133        if !supported_types.contains(&token_type.to_string()) {
134            return Err(AuthError::InvalidRequest(format!(
135                "Unsupported token type: {}",
136                token_type
137            )));
138        }
139        Ok(())
140    }
141
142    /// Extract subject from token metadata
143    pub fn extract_subject(metadata: &HashMap<String, serde_json::Value>) -> Option<String> {
144        metadata
145            .get("sub")
146            .and_then(|v| v.as_str())
147            .map(|s| s.to_string())
148    }
149
150    /// Extract scopes from token metadata or scope string
151    pub fn extract_scopes(
152        metadata: &HashMap<String, serde_json::Value>,
153        scope_string: Option<&str>,
154    ) -> Vec<String> {
155        // Try to get scopes from metadata first
156        if let Some(scopes) = metadata.get("scope").or_else(|| metadata.get("scopes")) {
157            if let Some(scope_str) = scopes.as_str() {
158                return scope_str
159                    .split_whitespace()
160                    .map(|s| s.to_string())
161                    .collect();
162            } else if let Some(scope_array) = scopes.as_array() {
163                return scope_array
164                    .iter()
165                    .filter_map(|v| v.as_str())
166                    .map(|s| s.to_string())
167                    .collect();
168            }
169        }
170
171        // Fall back to scope string parameter
172        scope_string
173            .map(|s| {
174                s.split_whitespace()
175                    .map(|scope| scope.to_string())
176                    .collect()
177            })
178            .unwrap_or_default()
179    }
180
181    /// Validate delegation chain depth
182    pub fn validate_delegation_depth(current_depth: usize, max_depth: usize) -> Result<()> {
183        if current_depth > max_depth {
184            return Err(AuthError::InvalidRequest(
185                "Maximum delegation depth exceeded".to_string(),
186            ));
187        }
188        Ok(())
189    }
190
191    /// Normalize token type URN
192    pub fn normalize_token_type(token_type: &str) -> String {
193        match token_type {
194            "jwt" => "urn:ietf:params:oauth:token-type:jwt".to_string(),
195            "access_token" => "urn:ietf:params:oauth:token-type:access_token".to_string(),
196            "refresh_token" => "urn:ietf:params:oauth:token-type:refresh_token".to_string(),
197            "id_token" => "urn:ietf:params:oauth:token-type:id_token".to_string(),
198            "saml2" => "urn:ietf:params:oauth:token-type:saml2".to_string(),
199            _ => token_type.to_string(),
200        }
201    }
202
203    /// Check if token type is JWT-based
204    pub fn is_jwt_token_type(token_type: &str) -> bool {
205        matches!(
206            token_type,
207            "urn:ietf:params:oauth:token-type:jwt"
208                | "urn:ietf:params:oauth:token-type:access_token"
209                | "urn:ietf:params:oauth:token-type:id_token"
210        )
211    }
212
213    /// Validate scope requirements
214    pub fn validate_scope_requirements(
215        requested_scopes: &[String],
216        available_scopes: &[String],
217        require_all: bool,
218    ) -> Result<()> {
219        if require_all {
220            for scope in requested_scopes {
221                if !available_scopes.contains(scope) {
222                    return Err(AuthError::InvalidRequest(format!(
223                        "Required scope not available: {}",
224                        scope
225                    )));
226                }
227            }
228        } else {
229            let has_any = requested_scopes
230                .iter()
231                .any(|scope| available_scopes.contains(scope));
232            if !has_any && !requested_scopes.is_empty() {
233                return Err(AuthError::InvalidRequest(
234                    "None of the requested scopes are available".to_string(),
235                ));
236            }
237        }
238        Ok(())
239    }
240}
241
242/// Factory for creating appropriate token exchange managers
243pub struct TokenExchangeFactory;
244
245impl TokenExchangeFactory {
246    /// Create the appropriate token exchange manager based on requirements
247    pub async fn create_manager(
248        requirements: &ExchangeRequirements,
249    ) -> Result<Box<dyn TokenExchangeService<Request = (), Response = (), Config = ()>>> {
250        // Determine complexity level based on requirements
251        let complexity = Self::determine_manager_type(requirements);
252
253        // Return error indicating users should use the specific factory methods
254        // The generic create_manager cannot work due to different type parameters
255        match complexity {
256            ServiceComplexityLevel::Advanced => Err(AuthError::InvalidRequest(
257                "Use TokenExchangeFactory::create_advanced_manager() for advanced requirements"
258                    .to_string(),
259            )),
260            ServiceComplexityLevel::Basic => Err(AuthError::InvalidRequest(
261                "Use TokenExchangeFactory::create_basic_manager() for basic requirements"
262                    .to_string(),
263            )),
264        }
265    }
266
267    /// Determine which manager type to use based on requirements
268    pub fn determine_manager_type(requirements: &ExchangeRequirements) -> ServiceComplexityLevel {
269        // Use advanced manager if any advanced features are needed
270        if requirements.needs_audit_trail
271            || requirements.needs_session_integration
272            || requirements.needs_context_preservation
273            || requirements.needs_multi_party_chains
274            || requirements.needs_jwt_operations
275            || requirements.needs_policy_control
276            || requirements.needs_cross_domain
277            || requirements.max_delegation_depth > 3
278        {
279            ServiceComplexityLevel::Advanced
280        } else {
281            ServiceComplexityLevel::Basic
282        }
283    }
284
285    /// Get recommended configuration based on use case
286    pub fn get_recommended_config(use_case: &TokenExchangeUseCase) -> ExchangeRequirements {
287        match use_case {
288            TokenExchangeUseCase::SimpleServiceToService => ExchangeRequirements {
289                needs_audit_trail: false,
290                needs_session_integration: false,
291                needs_context_preservation: false,
292                needs_multi_party_chains: false,
293                needs_jwt_operations: false,
294                needs_policy_control: false,
295                needs_cross_domain: false,
296                max_delegation_depth: 1,
297            },
298            TokenExchangeUseCase::MicroserviceChain => ExchangeRequirements {
299                needs_audit_trail: true,
300                needs_session_integration: false,
301                needs_context_preservation: true,
302                needs_multi_party_chains: true,
303                needs_jwt_operations: false,
304                needs_policy_control: true,
305                needs_cross_domain: false,
306                max_delegation_depth: 5,
307            },
308            TokenExchangeUseCase::EnterpriseIntegration => ExchangeRequirements {
309                needs_audit_trail: true,
310                needs_session_integration: true,
311                needs_context_preservation: true,
312                needs_multi_party_chains: true,
313                needs_jwt_operations: true,
314                needs_policy_control: true,
315                needs_cross_domain: true,
316                max_delegation_depth: 10,
317            },
318            TokenExchangeUseCase::CrossDomainFederation => ExchangeRequirements {
319                needs_audit_trail: true,
320                needs_session_integration: false,
321                needs_context_preservation: true,
322                needs_multi_party_chains: false,
323                needs_jwt_operations: true,
324                needs_policy_control: true,
325                needs_cross_domain: true,
326                max_delegation_depth: 3,
327            },
328        }
329    }
330}
331
332/// Requirements for token exchange functionality
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct ExchangeRequirements {
335    /// Requires complete audit trail
336    pub needs_audit_trail: bool,
337
338    /// Requires session integration
339    pub needs_session_integration: bool,
340
341    /// Requires context preservation
342    pub needs_context_preservation: bool,
343
344    /// Requires multi-party delegation chains
345    pub needs_multi_party_chains: bool,
346
347    /// Requires JWT cryptographic operations
348    pub needs_jwt_operations: bool,
349
350    /// Requires policy-driven control
351    pub needs_policy_control: bool,
352
353    /// Requires cross-domain exchanges
354    pub needs_cross_domain: bool,
355
356    /// Maximum delegation depth needed
357    pub max_delegation_depth: usize,
358}
359
360/// Common use cases for token exchange
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
362pub enum TokenExchangeUseCase {
363    /// Simple service-to-service authentication
364    SimpleServiceToService,
365
366    /// Microservice chain with delegation
367    MicroserviceChain,
368
369    /// Full enterprise integration
370    EnterpriseIntegration,
371
372    /// Cross-domain identity federation
373    CrossDomainFederation,
374}
375
376#[cfg(test)]
377mod tests {
378    use super::*;
379
380    #[test]
381    fn test_validation_utils_grant_type() {
382        // Valid grant type
383        assert!(
384            ValidationUtils::validate_grant_type("urn:ietf:params:oauth:grant-type:token-exchange")
385                .is_ok()
386        );
387
388        // Invalid grant type
389        assert!(ValidationUtils::validate_grant_type("authorization_code").is_err());
390    }
391
392    #[test]
393    fn test_validation_utils_token_type() {
394        let supported = vec!["urn:ietf:params:oauth:token-type:jwt".to_string()];
395
396        assert!(
397            ValidationUtils::validate_token_type(
398                "urn:ietf:params:oauth:token-type:jwt",
399                &supported
400            )
401            .is_ok()
402        );
403
404        assert!(ValidationUtils::validate_token_type("unsupported", &supported).is_err());
405    }
406
407    #[test]
408    fn test_extract_scopes() {
409        let mut metadata = HashMap::new();
410        metadata.insert(
411            "scope".to_string(),
412            serde_json::Value::String("read write".to_string()),
413        );
414
415        let scopes = ValidationUtils::extract_scopes(&metadata, None);
416        assert_eq!(scopes, vec!["read", "write"]);
417
418        // Test with scope parameter
419        let scopes = ValidationUtils::extract_scopes(&HashMap::new(), Some("admin user"));
420        assert_eq!(scopes, vec!["admin", "user"]);
421    }
422
423    #[test]
424    fn test_normalize_token_type() {
425        assert_eq!(
426            ValidationUtils::normalize_token_type("jwt"),
427            "urn:ietf:params:oauth:token-type:jwt"
428        );
429
430        assert_eq!(
431            ValidationUtils::normalize_token_type("urn:ietf:params:oauth:token-type:jwt"),
432            "urn:ietf:params:oauth:token-type:jwt"
433        );
434    }
435
436    #[test]
437    fn test_factory_manager_type_determination() {
438        // Simple requirements should use basic
439        let simple_req = ExchangeRequirements {
440            needs_audit_trail: false,
441            needs_session_integration: false,
442            needs_context_preservation: false,
443            needs_multi_party_chains: false,
444            needs_jwt_operations: false,
445            needs_policy_control: false,
446            needs_cross_domain: false,
447            max_delegation_depth: 1,
448        };
449
450        assert_eq!(
451            TokenExchangeFactory::determine_manager_type(&simple_req),
452            ServiceComplexityLevel::Basic
453        );
454
455        // Complex requirements should use advanced
456        let complex_req = ExchangeRequirements {
457            needs_audit_trail: true,
458            needs_session_integration: true,
459            needs_context_preservation: true,
460            needs_multi_party_chains: true,
461            needs_jwt_operations: true,
462            needs_policy_control: true,
463            needs_cross_domain: true,
464            max_delegation_depth: 10,
465        };
466
467        assert_eq!(
468            TokenExchangeFactory::determine_manager_type(&complex_req),
469            ServiceComplexityLevel::Advanced
470        );
471    }
472
473    #[test]
474    fn test_use_case_recommendations() {
475        let simple_config = TokenExchangeFactory::get_recommended_config(
476            &TokenExchangeUseCase::SimpleServiceToService,
477        );
478        assert!(!simple_config.needs_audit_trail);
479        assert_eq!(simple_config.max_delegation_depth, 1);
480
481        let enterprise_config = TokenExchangeFactory::get_recommended_config(
482            &TokenExchangeUseCase::EnterpriseIntegration,
483        );
484        assert!(enterprise_config.needs_audit_trail);
485        assert!(enterprise_config.needs_session_integration);
486        assert_eq!(enterprise_config.max_delegation_depth, 10);
487    }
488}
489
490