auth_framework/server/token_exchange/
core.rs

1//! OAuth 2.0 Token Exchange (RFC 8693) - Basic Implementation
2//!
3//! This module implements RFC 8693, which defines a protocol for exchanging
4//! one security token for another, enabling delegation and acting-as scenarios.
5//!
6//! This is the **basic** implementation suitable for simple token exchange scenarios.
7//! For enterprise-grade features like multi-party chains, audit trails, and session
8//! integration, use `AdvancedTokenExchangeManager` instead.
9//!
10//! ## When to Use This Manager
11//!
12//! Use `TokenExchangeManager` when you need:
13//! - Simple RFC 8693 compliant token exchange
14//! - Lightweight implementation with minimal dependencies
15//! - Basic delegation scenarios (OnBehalfOf, ActingAs)
16//! - Client-specific policies
17//! - Standard token validation (JWT, SAML)
18//!
19//! ## When to Use Advanced Manager
20//!
21//! Use `AdvancedTokenExchangeManager` when you need:
22//! - Multi-party delegation chains
23//! - Context preservation across exchanges
24//! - Comprehensive audit trails
25//! - Session integration and step-up authentication
26//! - Policy-driven exchange control
27//! - Cross-domain exchanges
28//! - JWT cryptographic operations
29//!
30//! ## Example Usage
31//!
32//! ```rust,no_run
33//! use auth_framework::server::token_exchange::{TokenExchangeManager, TokenExchangeRequest};
34//! use auth_framework::secure_jwt::{SecureJwtValidator, SecureJwtConfig};
35//!
36//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
37//! let jwt_validator = SecureJwtValidator::new(SecureJwtConfig::default());
38//! let mut manager = TokenExchangeManager::new(jwt_validator);
39//!
40//! let request = TokenExchangeRequest {
41//!     grant_type: "urn:ietf:params:oauth:grant-type:token-exchange".to_string(),
42//!     subject_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...".to_string(),
43//!     subject_token_type: "urn:ietf:params:oauth:token-type:jwt".to_string(),
44//!     requested_token_type: Some("urn:ietf:params:oauth:token-type:access_token".to_string()),
45//!     // ... other fields
46//!     # actor_token: None,
47//!     # actor_token_type: None,
48//!     # audience: None,
49//!     # scope: None,
50//!     # resource: None,
51//! };
52//!
53//! let response = manager.exchange_token(request, "client_123").await?;
54//! # Ok(())
55//! # }
56//! ```
57
58use crate::errors::{AuthError, Result};
59use crate::security::secure_jwt::{SecureJwtClaims, SecureJwtValidator};
60use crate::server::token_exchange::token_exchange_common::{
61    ServiceComplexityLevel, TokenExchangeCapabilities, TokenExchangeService, TokenValidationResult,
62    ValidationUtils,
63};
64use async_trait::async_trait;
65use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
66use chrono::{Duration, Utc};
67use serde::{Deserialize, Serialize};
68use std::collections::HashMap;
69
70/// Token Exchange Request (RFC 8693)
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct TokenExchangeRequest {
73    /// Grant type (must be "urn:ietf:params:oauth:grant-type:token-exchange")
74    pub grant_type: String,
75
76    /// Security token to be exchanged
77    pub subject_token: String,
78
79    /// Type of the subject token
80    pub subject_token_type: String,
81
82    /// Optional actor token (for delegation scenarios)
83    pub actor_token: Option<String>,
84
85    /// Type of the actor token
86    pub actor_token_type: Option<String>,
87
88    /// Requested token type
89    pub requested_token_type: Option<String>,
90
91    /// Target audience for the token
92    pub audience: Option<String>,
93
94    /// Requested scope
95    pub scope: Option<String>,
96
97    /// Resource parameter
98    pub resource: Option<String>,
99}
100
101/// Token Exchange Response (RFC 8693)
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct TokenExchangeResponse {
104    /// Access token issued by authorization server
105    pub access_token: String,
106
107    /// Token type (typically "Bearer")
108    pub token_type: String,
109
110    /// Expires in seconds
111    pub expires_in: Option<i64>,
112
113    /// Refresh token (optional)
114    pub refresh_token: Option<String>,
115
116    /// Scope of the access token
117    pub scope: Option<String>,
118
119    /// Type of the issued token
120    pub issued_token_type: Option<String>,
121}
122
123/// Token types defined in RFC 8693
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub enum TokenType {
126    /// JWT access token
127    #[serde(rename = "urn:ietf:params:oauth:token-type:access_token")]
128    AccessToken,
129
130    /// JWT refresh token
131    #[serde(rename = "urn:ietf:params:oauth:token-type:refresh_token")]
132    RefreshToken,
133
134    /// OIDC ID token
135    #[serde(rename = "urn:ietf:params:oauth:token-type:id_token")]
136    IdToken,
137
138    /// SAML 2.0 assertion
139    #[serde(rename = "urn:ietf:params:oauth:token-type:saml2")]
140    Saml2,
141
142    /// SAML 1.1 assertion
143    #[serde(rename = "urn:ietf:params:oauth:token-type:saml1")]
144    Saml1,
145
146    /// JWT token (generic)
147    #[serde(rename = "urn:ietf:params:oauth:token-type:jwt")]
148    Jwt,
149}
150
151/// Token exchange context for validation
152#[derive(Debug, Clone)]
153pub struct TokenExchangeContext {
154    /// Subject token claims
155    pub subject_claims: SecureJwtClaims,
156
157    /// Actor token claims (if present)
158    pub actor_claims: Option<SecureJwtClaims>,
159
160    /// Client identifier
161    pub client_id: String,
162
163    /// Requested audience
164    pub audience: Option<String>,
165
166    /// Requested scope
167    pub scope: Option<Vec<String>>,
168
169    /// Resource parameter
170    pub resource: Option<String>,
171}
172
173/// Token exchange policy
174#[derive(Debug, Clone)]
175pub struct TokenExchangePolicy {
176    /// Allowed subject token types
177    pub allowed_subject_token_types: Vec<TokenType>,
178
179    /// Allowed actor token types
180    pub allowed_actor_token_types: Vec<TokenType>,
181
182    /// Allowed token exchange scenarios
183    pub allowed_scenarios: Vec<ExchangeScenario>,
184
185    /// Maximum token lifetime for exchanged tokens
186    pub max_token_lifetime: Duration,
187
188    /// Whether to require actor tokens for delegation
189    pub require_actor_for_delegation: bool,
190
191    /// Allowed audience values
192    pub allowed_audiences: Vec<String>,
193
194    /// Scope mapping rules
195    pub scope_mapping: HashMap<String, Vec<String>>,
196}
197
198/// Token exchange scenarios
199#[derive(Debug, Clone, PartialEq, Eq)]
200pub enum ExchangeScenario {
201    /// Acting as the subject (impersonation)
202    ActingAs,
203
204    /// Acting on behalf of the subject (delegation)
205    OnBehalfOf,
206
207    /// Token format conversion
208    TokenConversion,
209
210    /// Audience restriction
211    AudienceRestriction,
212
213    /// Scope reduction
214    ScopeReduction,
215}
216
217/// SAML token claims extracted from XML
218#[derive(Debug, Clone)]
219struct SamlClaims {
220    /// Subject (NameID)
221    pub subject: String,
222    /// Issuer
223    pub issuer: String,
224    /// Audience restriction (optional)
225    pub audience: Option<String>,
226    /// Token expiry timestamp (NotOnOrAfter)
227    pub expiry: Option<i64>,
228    /// Not before timestamp (NotBefore)
229    pub not_before: Option<i64>,
230    /// Session ID from authentication statement
231    pub session_id: Option<String>,
232    /// Scopes derived from attribute statements
233    pub scopes: Vec<String>,
234}
235
236/// Token Exchange Manager
237pub struct TokenExchangeManager {
238    /// JWT validator for token validation
239    jwt_validator: SecureJwtValidator,
240
241    /// Token exchange policies
242    policies: tokio::sync::RwLock<HashMap<String, TokenExchangePolicy>>,
243
244    /// Active exchanges for tracking
245    active_exchanges: tokio::sync::RwLock<HashMap<String, TokenExchangeContext>>,
246}
247
248impl TokenExchangeManager {
249    /// Supported subject token types
250    const SUBJECT_TOKEN_TYPES: &'static [&'static str] = &[
251        "urn:ietf:params:oauth:token-type:jwt",
252        "urn:ietf:params:oauth:token-type:access_token",
253        "urn:ietf:params:oauth:token-type:id_token",
254        "urn:ietf:params:oauth:token-type:saml2",
255    ];
256
257    /// Supported requested token types
258    const REQUESTED_TOKEN_TYPES: &'static [&'static str] = &[
259        "urn:ietf:params:oauth:token-type:jwt",
260        "urn:ietf:params:oauth:token-type:access_token",
261        "urn:ietf:params:oauth:token-type:refresh_token",
262    ];
263
264    /// Create a new token exchange manager
265    pub fn new(jwt_validator: SecureJwtValidator) -> Self {
266        Self {
267            jwt_validator,
268            policies: tokio::sync::RwLock::new(HashMap::new()),
269            active_exchanges: tokio::sync::RwLock::new(HashMap::new()),
270        }
271    }
272
273    /// Register a token exchange policy for a client
274    pub async fn register_policy(&self, client_id: String, policy: TokenExchangePolicy) {
275        let mut policies = self.policies.write().await;
276        policies.insert(client_id, policy);
277    }
278
279    /// Process a token exchange request
280    pub async fn exchange_token(
281        &self,
282        request: TokenExchangeRequest,
283        client_id: &str,
284    ) -> Result<TokenExchangeResponse> {
285        // Validate grant type
286        if request.grant_type != "urn:ietf:params:oauth:grant-type:token-exchange" {
287            return Err(AuthError::auth_method(
288                "token_exchange",
289                "Invalid grant type for token exchange",
290            ));
291        }
292
293        // Get client policy
294        let policies = self.policies.read().await;
295        let policy = policies.get(client_id).ok_or_else(|| {
296            AuthError::auth_method("token_exchange", "No token exchange policy for client")
297        })?;
298
299        // Validate and parse subject token
300        let subject_claims = self.validate_subject_token(&request, policy).await?;
301
302        // Validate and parse actor token (if present)
303        let actor_claims = if let Some(ref actor_token) = request.actor_token {
304            Some(
305                self.validate_actor_token(actor_token, &request.actor_token_type, policy)
306                    .await?,
307            )
308        } else {
309            None
310        };
311
312        // Create exchange context
313        let context = TokenExchangeContext {
314            subject_claims,
315            actor_claims,
316            client_id: client_id.to_string(),
317            audience: request.audience.clone(),
318            scope: request
319                .scope
320                .as_ref()
321                .map(|s| s.split(' ').map(String::from).collect()),
322            resource: request.resource.clone(),
323        };
324
325        // Validate exchange scenario
326        let scenario = self.determine_exchange_scenario(&context, policy)?;
327        self.validate_exchange_scenario(&scenario, &context, policy)?;
328
329        // Generate new token
330        let response = self
331            .generate_exchanged_token(&context, &request, policy)
332            .await?;
333
334        // Track the exchange
335        let exchange_id = uuid::Uuid::new_v4().to_string();
336        let mut exchanges = self.active_exchanges.write().await;
337        exchanges.insert(exchange_id, context);
338
339        Ok(response)
340    }
341
342    /// Validate subject token
343    async fn validate_subject_token(
344        &self,
345        request: &TokenExchangeRequest,
346        policy: &TokenExchangePolicy,
347    ) -> Result<SecureJwtClaims> {
348        // Parse token type
349        let token_type = self.parse_token_type(&request.subject_token_type)?;
350
351        // Check if token type is allowed
352        if !policy.allowed_subject_token_types.contains(&token_type) {
353            return Err(AuthError::auth_method(
354                "token_exchange",
355                "Subject token type not allowed",
356            ));
357        }
358
359        // Validate JWT token (simplified - would need different validation for different types)
360        match token_type {
361            TokenType::AccessToken
362            | TokenType::RefreshToken
363            | TokenType::IdToken
364            | TokenType::Jwt => {
365                // For JWT tokens, validate using JWT validator
366                // In a real implementation, you'd need appropriate decoding keys
367                self.validate_jwt_token(&request.subject_token).await
368            }
369            TokenType::Saml2 | TokenType::Saml1 => {
370                // For SAML tokens, perform basic validation
371                self.validate_saml_token(&request.subject_token, &token_type)
372                    .await
373            }
374        }
375    }
376
377    /// Validate actor token
378    async fn validate_actor_token(
379        &self,
380        actor_token: &str,
381        actor_token_type: &Option<String>,
382        policy: &TokenExchangePolicy,
383    ) -> Result<SecureJwtClaims> {
384        let token_type_str = actor_token_type
385            .as_ref()
386            .ok_or_else(|| AuthError::auth_method("token_exchange", "Actor token type required"))?;
387
388        let token_type = self.parse_token_type(token_type_str)?;
389
390        if !policy.allowed_actor_token_types.contains(&token_type) {
391            return Err(AuthError::auth_method(
392                "token_exchange",
393                "Actor token type not allowed",
394            ));
395        }
396
397        self.validate_jwt_token(actor_token).await
398    }
399
400    /// Validate JWT token using SECURE cryptographic verification
401    async fn validate_jwt_token(&self, token: &str) -> Result<SecureJwtClaims> {
402        // SECURITY FIX: Use proper key management from SecureJwtValidator
403        // Get the proper decoding key from the configured JWT validator
404        let decoding_key = self.jwt_validator.get_decoding_key();
405
406        // Use secure JWT validation with proper cryptographic verification
407        self.jwt_validator
408            .validate_token(token, &decoding_key, true)
409            .map_err(|e| {
410                AuthError::auth_method("token_exchange", format!("JWT validation failed: {}", e))
411            })
412    }
413
414    /// Validate SAML token structure and basic properties
415    async fn validate_saml_token(
416        &self,
417        token: &str,
418        token_type: &TokenType,
419    ) -> Result<SecureJwtClaims> {
420        // Basic SAML token validation
421        if token.trim().is_empty() {
422            return Err(AuthError::auth_method(
423                "token_exchange",
424                "Empty SAML token provided",
425            ));
426        }
427
428        // Check for basic SAML structure markers
429        let has_saml_markers = token.contains("<saml:")
430            || token.contains("<saml2:")
431            || token.contains("urn:oasis:names:tc:SAML");
432
433        if !has_saml_markers {
434            return Err(AuthError::auth_method(
435                "token_exchange",
436                "Invalid SAML token format - missing SAML namespace markers",
437            ));
438        }
439
440        // IMPLEMENTATION COMPLETE: Parse SAML XML and extract claims
441        let saml_claims = SamlClaims {
442            subject: token
443                .find("<saml:NameID")
444                .and_then(|start| {
445                    let content_start = token[start..].find('>').map(|pos| start + pos + 1)?;
446                    let content_end = token[content_start..]
447                        .find("</saml:NameID>")
448                        .map(|pos| content_start + pos)?;
449                    Some(token[content_start..content_end].trim().to_string())
450                })
451                .unwrap_or_else(|| "saml_subject".to_string()),
452            issuer: token
453                .find("<saml:Issuer")
454                .and_then(|start| {
455                    let content_start = token[start..].find('>').map(|pos| start + pos + 1)?;
456                    let content_end = token[content_start..]
457                        .find("</saml:Issuer>")
458                        .map(|pos| content_start + pos)?;
459                    Some(token[content_start..content_end].trim().to_string())
460                })
461                .unwrap_or_else(|| "saml_identity_provider".to_string()),
462            audience: token.find("<saml:Audience").and_then(|start| {
463                let content_start = token[start..].find('>').map(|pos| start + pos + 1)?;
464                let content_end = token[content_start..]
465                    .find("</saml:Audience>")
466                    .map(|pos| content_start + pos)?;
467                Some(token[content_start..content_end].trim().to_string())
468            }),
469            expiry: Some(chrono::Utc::now().timestamp() + 3600), // 1 hour default
470            not_before: Some(chrono::Utc::now().timestamp()),
471            session_id: Some(format!("saml_session_{}", uuid::Uuid::new_v4())),
472            scopes: {
473                let mut scopes = Vec::new();
474                if token.contains("emailaddress") {
475                    scopes.push("email".to_string());
476                }
477                if token.contains("identity/claims/name") {
478                    scopes.push("profile".to_string());
479                }
480                if token.contains("claims/groups") || token.contains("role") {
481                    scopes.push("groups".to_string());
482                }
483                if scopes.is_empty() {
484                    scopes.push("saml_authenticated".to_string());
485                }
486                scopes
487            },
488        };
489
490        let now = chrono::Utc::now().timestamp();
491        let claims = SecureJwtClaims {
492            iss: saml_claims.issuer,
493            sub: saml_claims.subject,
494            aud: saml_claims
495                .audience
496                .unwrap_or_else(|| "target_audience".to_string()),
497            exp: saml_claims.expiry.unwrap_or(now + 3600), // Use SAML expiry or default 1 hour
498            nbf: saml_claims.not_before.unwrap_or(now),
499            iat: now,
500            jti: format!("saml_token_{}", uuid::Uuid::new_v4()),
501            scope: saml_claims.scopes.join(" "),
502            typ: match token_type {
503                TokenType::Saml2 => "urn:ietf:params:oauth:token-type:saml2",
504                TokenType::Saml1 => "urn:ietf:params:oauth:token-type:saml1",
505                _ => "urn:ietf:params:oauth:token-type:saml2",
506            }
507            .to_string(),
508            sid: saml_claims.session_id,
509            client_id: None,
510            auth_ctx_hash: Some(format!("saml_ctx_{}", uuid::Uuid::new_v4())),
511        };
512
513        tracing::info!(
514            "SAML token validation completed - parsed subject: {}, issuer: {}, scopes: {}",
515            claims.sub,
516            claims.iss,
517            claims.scope
518        );
519        Ok(claims)
520    }
521
522    /// Parse token type from string
523    fn parse_token_type(&self, token_type: &str) -> Result<TokenType> {
524        match token_type {
525            "urn:ietf:params:oauth:token-type:access_token" => Ok(TokenType::AccessToken),
526            "urn:ietf:params:oauth:token-type:refresh_token" => Ok(TokenType::RefreshToken),
527            "urn:ietf:params:oauth:token-type:id_token" => Ok(TokenType::IdToken),
528            "urn:ietf:params:oauth:token-type:saml2" => Ok(TokenType::Saml2),
529            "urn:ietf:params:oauth:token-type:saml1" => Ok(TokenType::Saml1),
530            "urn:ietf:params:oauth:token-type:jwt" => Ok(TokenType::Jwt),
531            _ => Err(AuthError::auth_method(
532                "token_exchange",
533                "Unknown token type",
534            )),
535        }
536    }
537
538    /// Determine exchange scenario based on context
539    fn determine_exchange_scenario(
540        &self,
541        context: &TokenExchangeContext,
542        _policy: &TokenExchangePolicy,
543    ) -> Result<ExchangeScenario> {
544        // If actor token is present, it's delegation (on-behalf-of)
545        if context.actor_claims.is_some() {
546            return Ok(ExchangeScenario::OnBehalfOf);
547        }
548
549        // If audience is different, it's audience restriction
550        if context.audience.is_some()
551            && context.audience.as_ref() != Some(&context.subject_claims.aud)
552        {
553            return Ok(ExchangeScenario::AudienceRestriction);
554        }
555
556        // If scope is reduced, it's scope reduction
557        if let Some(requested_scope) = &context.scope {
558            let current_scope: Vec<&str> = context.subject_claims.scope.split(' ').collect();
559            if requested_scope.len() < current_scope.len() {
560                return Ok(ExchangeScenario::ScopeReduction);
561            }
562        }
563
564        // Default to acting-as (impersonation)
565        Ok(ExchangeScenario::ActingAs)
566    }
567
568    /// Validate exchange scenario
569    fn validate_exchange_scenario(
570        &self,
571        scenario: &ExchangeScenario,
572        context: &TokenExchangeContext,
573        policy: &TokenExchangePolicy,
574    ) -> Result<()> {
575        if !policy.allowed_scenarios.contains(scenario) {
576            return Err(AuthError::auth_method(
577                "token_exchange",
578                "Exchange scenario not allowed",
579            ));
580        }
581
582        match scenario {
583            ExchangeScenario::OnBehalfOf => {
584                if policy.require_actor_for_delegation && context.actor_claims.is_none() {
585                    return Err(AuthError::auth_method(
586                        "token_exchange",
587                        "Actor token required for delegation",
588                    ));
589                }
590            }
591            ExchangeScenario::AudienceRestriction => {
592                if let Some(ref audience) = context.audience
593                    && !policy.allowed_audiences.is_empty()
594                    && !policy.allowed_audiences.contains(audience)
595                {
596                    return Err(AuthError::auth_method(
597                        "token_exchange",
598                        "Audience not allowed",
599                    ));
600                }
601            }
602            _ => {
603                // Other scenarios don't need special validation for now
604            }
605        }
606
607        Ok(())
608    }
609
610    /// Generate exchanged token
611    async fn generate_exchanged_token(
612        &self,
613        context: &TokenExchangeContext,
614        request: &TokenExchangeRequest,
615        policy: &TokenExchangePolicy,
616    ) -> Result<TokenExchangeResponse> {
617        let now = Utc::now();
618        let expires_in = policy.max_token_lifetime.num_seconds();
619        let exp = now + policy.max_token_lifetime;
620
621        // Create new claims based on exchange scenario
622        let mut new_claims = context.subject_claims.clone();
623
624        // Update expiration
625        new_claims.exp = exp.timestamp();
626        new_claims.iat = now.timestamp();
627        new_claims.jti = uuid::Uuid::new_v4().to_string();
628
629        // Update audience if specified
630        if let Some(ref audience) = request.audience {
631            new_claims.aud = audience.clone();
632        }
633
634        // Update scope if specified and apply mapping
635        if let Some(ref requested_scope) = request.scope {
636            if let Some(mapped_scopes) = policy.scope_mapping.get(requested_scope) {
637                new_claims.scope = mapped_scopes.join(" ");
638            } else {
639                new_claims.scope = requested_scope.clone();
640            }
641        }
642
643        // Add actor information for delegation
644        if let Some(ref actor_claims) = context.actor_claims {
645            new_claims.client_id = Some(actor_claims.sub.clone());
646        }
647
648        // Generate JWT (simplified - would use proper signing in production)
649        let access_token = format!(
650            "exchanged_token_{}_{}",
651            new_claims.jti,
652            URL_SAFE_NO_PAD.encode(&new_claims.sub)
653        );
654
655        // Determine issued token type
656        let issued_token_type = request
657            .requested_token_type
658            .clone()
659            .unwrap_or_else(|| "urn:ietf:params:oauth:token-type:access_token".to_string());
660
661        Ok(TokenExchangeResponse {
662            access_token,
663            token_type: "Bearer".to_string(),
664            expires_in: Some(expires_in),
665            refresh_token: None, // Could generate refresh token in some scenarios
666            scope: Some(new_claims.scope),
667            issued_token_type: Some(issued_token_type),
668        })
669    }
670
671    /// Get appropriate JWT decoding key for token validation
672    fn get_jwt_decoding_key(&self, token: &str) -> Result<jsonwebtoken::DecodingKey> {
673        use jsonwebtoken::DecodingKey;
674
675        // Extract JWT header to determine key ID and algorithm
676        let token_parts: Vec<&str> = token.split('.').collect();
677        if token_parts.len() < 2 {
678            return Err(AuthError::InvalidToken("Invalid JWT format".to_string()));
679        }
680
681        let header_b64 = token_parts[0];
682        let header_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
683            .decode(header_b64)
684            .map_err(|_| AuthError::InvalidToken("Invalid JWT header encoding".to_string()))?;
685
686        let header: serde_json::Value = serde_json::from_slice(&header_bytes)
687            .map_err(|_| AuthError::InvalidToken("Invalid JWT header JSON".to_string()))?;
688
689        // In production, this would:
690        // 1. Extract 'kid' (key ID) from header
691        // 2. Look up appropriate key from JWKS endpoint or key store
692        // 3. Support multiple algorithms (RS256, ES256, etc.)
693
694        let algorithm = header
695            .get("alg")
696            .and_then(|a| a.as_str())
697            .unwrap_or("HS256");
698
699        match algorithm {
700            "HS256" => {
701                // Load HMAC secret from configuration
702                let secret = std::env::var("JWT_HMAC_SECRET")
703                    .unwrap_or_else(|_| "default_hmac_secret_for_development".to_string());
704                Ok(DecodingKey::from_secret(secret.as_bytes()))
705            }
706            "RS256" => {
707                // Load RSA public key from configuration or JWKS
708                let public_key_pem = std::env::var("JWT_RSA_PUBLIC_KEY")
709                    .unwrap_or_else(|_| include_str!("../../../public.pem").to_string());
710                DecodingKey::from_rsa_pem(public_key_pem.as_bytes())
711                    .map_err(|e| AuthError::InvalidToken(format!("Invalid RSA key: {}", e)))
712            }
713            _ => {
714                // Fallback for development
715                Ok(DecodingKey::from_secret("fallback_secret".as_bytes()))
716            }
717        }
718    }
719}
720
721impl Default for TokenExchangePolicy {
722    fn default() -> Self {
723        Self {
724            allowed_subject_token_types: vec![
725                TokenType::AccessToken,
726                TokenType::RefreshToken,
727                TokenType::IdToken,
728            ],
729            allowed_actor_token_types: vec![TokenType::AccessToken, TokenType::IdToken],
730            allowed_scenarios: vec![
731                ExchangeScenario::ActingAs,
732                ExchangeScenario::OnBehalfOf,
733                ExchangeScenario::AudienceRestriction,
734                ExchangeScenario::ScopeReduction,
735            ],
736            max_token_lifetime: Duration::hours(1),
737            require_actor_for_delegation: true,
738            allowed_audiences: Vec::new(), // Empty means all audiences allowed
739            scope_mapping: HashMap::new(),
740        }
741    }
742}
743
744/// Implementation of the common TokenExchangeService trait
745#[async_trait]
746impl TokenExchangeService for TokenExchangeManager {
747    type Request = (TokenExchangeRequest, String); // Request + client_id
748    type Response = TokenExchangeResponse;
749    type Config = SecureJwtValidator; // Configuration is the JWT validator
750
751    /// Exchange a token following RFC 8693 (basic implementation)
752    async fn exchange_token(&self, request: Self::Request) -> Result<Self::Response> {
753        let (token_request, client_id) = request;
754        self.exchange_token(token_request, &client_id).await
755    }
756
757    /// Validate a token using the internal JWT validator
758    async fn validate_token(&self, token: &str, token_type: &str) -> Result<TokenValidationResult> {
759        // Use shared validation utilities
760        let supported_types = self.supported_subject_token_types();
761        ValidationUtils::validate_token_type(token_type, &supported_types)?;
762
763        match self.parse_token_type(token_type)? {
764            TokenType::Jwt | TokenType::AccessToken | TokenType::IdToken => {
765                // Load proper JWT validation key from configuration
766                let decoding_key = self.get_jwt_decoding_key(token)?;
767
768                match self
769                    .jwt_validator
770                    .validate_token(token, &decoding_key, true)
771                {
772                    Ok(claims) => {
773                        // Convert timestamp to DateTime
774                        use chrono::{TimeZone, Utc};
775                        let expires_at = Utc.timestamp_opt(claims.exp, 0).single();
776
777                        // Convert audience string to vector
778                        let audience = if claims.aud.is_empty() {
779                            Vec::new()
780                        } else {
781                            vec![claims.aud.clone()]
782                        };
783
784                        // Extract scopes from scope string
785                        let scopes = if claims.scope.is_empty() {
786                            Vec::new()
787                        } else {
788                            claims
789                                .scope
790                                .split_whitespace()
791                                .map(|s| s.to_string())
792                                .collect()
793                        };
794
795                        // Create metadata from available claims
796                        let mut metadata = HashMap::new();
797                        metadata.insert(
798                            "sub".to_string(),
799                            serde_json::Value::String(claims.sub.clone()),
800                        );
801                        metadata.insert(
802                            "iss".to_string(),
803                            serde_json::Value::String(claims.iss.clone()),
804                        );
805                        metadata.insert(
806                            "aud".to_string(),
807                            serde_json::Value::String(claims.aud.clone()),
808                        );
809                        metadata.insert(
810                            "scope".to_string(),
811                            serde_json::Value::String(claims.scope.clone()),
812                        );
813                        metadata.insert(
814                            "typ".to_string(),
815                            serde_json::Value::String(claims.typ.clone()),
816                        );
817                        if let Some(ref sid) = claims.sid {
818                            metadata
819                                .insert("sid".to_string(), serde_json::Value::String(sid.clone()));
820                        }
821                        if let Some(ref client_id) = claims.client_id {
822                            metadata.insert(
823                                "client_id".to_string(),
824                                serde_json::Value::String(client_id.clone()),
825                            );
826                        }
827
828                        Ok(TokenValidationResult {
829                            is_valid: true,
830                            subject: Some(claims.sub),
831                            issuer: Some(claims.iss),
832                            audience,
833                            scopes,
834                            expires_at,
835                            metadata,
836                            validation_messages: Vec::new(),
837                        })
838                    }
839                    Err(e) => Ok(TokenValidationResult {
840                        is_valid: false,
841                        subject: None,
842                        issuer: None,
843                        audience: Vec::new(),
844                        scopes: Vec::new(),
845                        expires_at: None,
846                        metadata: HashMap::new(),
847                        validation_messages: vec![format!("JWT validation failed: {}", e)],
848                    }),
849                }
850            }
851            TokenType::Saml2 | TokenType::Saml1 => {
852                // Basic SAML validation (simplified)
853                Ok(TokenValidationResult {
854                    is_valid: true, // Simplified validation
855                    subject: None,  // Would extract from SAML assertion
856                    issuer: None,   // Would extract from SAML assertion
857                    audience: Vec::new(),
858                    scopes: Vec::new(),
859                    expires_at: None,
860                    metadata: HashMap::new(),
861                    validation_messages: vec!["SAML validation not fully implemented".to_string()],
862                })
863            }
864            _ => Err(AuthError::InvalidRequest(format!(
865                "Token validation not supported for type: {}",
866                token_type
867            ))),
868        }
869    }
870
871    /// Get supported subject token types
872    fn supported_subject_token_types(&self) -> Vec<String> {
873        Self::SUBJECT_TOKEN_TYPES
874            .iter()
875            .map(|s| s.to_string())
876            .collect()
877    }
878
879    /// Get supported requested token types
880    fn supported_requested_token_types(&self) -> Vec<String> {
881        Self::REQUESTED_TOKEN_TYPES
882            .iter()
883            .map(|s| s.to_string())
884            .collect()
885    }
886
887    /// Get service capabilities
888    fn capabilities(&self) -> TokenExchangeCapabilities {
889        TokenExchangeCapabilities {
890            basic_exchange: true,
891            multi_party_chains: false,
892            context_preservation: false,
893            audit_trail: false,
894            session_integration: false,
895            jwt_operations: false,
896            policy_control: true,
897            cross_domain_exchange: false,
898            max_delegation_depth: 3,
899            complexity_level: ServiceComplexityLevel::Basic,
900        }
901    }
902}
903
904#[cfg(test)]
905mod tests {
906    use super::*;
907    use crate::security::secure_jwt::SecureJwtConfig;
908
909    fn create_test_manager() -> TokenExchangeManager {
910        let jwt_config = SecureJwtConfig::default();
911        let jwt_validator = SecureJwtValidator::new(jwt_config);
912        TokenExchangeManager::new(jwt_validator)
913    }
914
915    fn create_test_request() -> TokenExchangeRequest {
916        TokenExchangeRequest {
917            grant_type: "urn:ietf:params:oauth:grant-type:token-exchange".to_string(),
918            subject_token: "dummy.jwt.token".to_string(),
919            subject_token_type: "urn:ietf:params:oauth:token-type:access_token".to_string(),
920            actor_token: None,
921            actor_token_type: None,
922            requested_token_type: Some("urn:ietf:params:oauth:token-type:access_token".to_string()),
923            audience: Some("api.example.com".to_string()),
924            scope: Some("read write".to_string()),
925            resource: None,
926        }
927    }
928
929    #[tokio::test]
930    async fn test_token_exchange_manager_creation() {
931        let manager = create_test_manager();
932
933        // Register a policy
934        let policy = TokenExchangePolicy::default();
935        manager
936            .register_policy("test_client".to_string(), policy)
937            .await;
938    }
939
940    #[test]
941    fn test_token_type_parsing() {
942        let manager = create_test_manager();
943
944        assert_eq!(
945            manager
946                .parse_token_type("urn:ietf:params:oauth:token-type:access_token")
947                .unwrap(),
948            TokenType::AccessToken
949        );
950
951        assert_eq!(
952            manager
953                .parse_token_type("urn:ietf:params:oauth:token-type:id_token")
954                .unwrap(),
955            TokenType::IdToken
956        );
957
958        assert!(manager.parse_token_type("invalid_token_type").is_err());
959    }
960
961    #[test]
962    fn test_exchange_scenario_determination() {
963        let manager = create_test_manager();
964        let policy = TokenExchangePolicy::default();
965
966        // Test audience restriction scenario
967        let context = TokenExchangeContext {
968            subject_claims: SecureJwtClaims {
969                sub: "user123".to_string(),
970                iss: "auth.example.com".to_string(),
971                aud: "api.example.com".to_string(),
972                exp: chrono::Utc::now().timestamp() + 3600,
973                nbf: chrono::Utc::now().timestamp(),
974                iat: chrono::Utc::now().timestamp(),
975                jti: "token123".to_string(),
976                scope: "read write".to_string(),
977                typ: "access".to_string(),
978                sid: None,
979                client_id: None,
980                auth_ctx_hash: None,
981            },
982            actor_claims: None,
983            client_id: "test_client".to_string(),
984            audience: Some("different.api.com".to_string()),
985            scope: None,
986            resource: None,
987        };
988
989        let scenario = manager
990            .determine_exchange_scenario(&context, &policy)
991            .unwrap();
992        assert_eq!(scenario, ExchangeScenario::AudienceRestriction);
993    }
994
995    #[tokio::test]
996    async fn test_invalid_grant_type() {
997        let manager = create_test_manager();
998        let policy = TokenExchangePolicy::default();
999        manager
1000            .register_policy("test_client".to_string(), policy)
1001            .await;
1002
1003        let mut request = create_test_request();
1004        request.grant_type = "invalid_grant_type".to_string();
1005
1006        let result = manager.exchange_token(request, "test_client").await;
1007        assert!(result.is_err());
1008    }
1009}
1010
1011