use crate::errors::{AuthError, Result};
use crate::server::oidc::oidc_session_management::SessionManager;
use crate::server::token_exchange::token_exchange_common::{
ServiceComplexityLevel, TokenExchangeCapabilities, TokenExchangeService, TokenValidationResult,
ValidationUtils,
};
use async_trait::async_trait;
use chrono::{DateTime, Duration, Utc};
use jsonwebtoken::{DecodingKey, EncodingKey};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::str::FromStr;
use std::sync::Arc;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum AuthLevel {
Basic = 1,
Mfa = 2,
High = 3,
}
impl FromStr for AuthLevel {
type Err = AuthError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"basic" => Ok(AuthLevel::Basic),
"mfa" => Ok(AuthLevel::Mfa),
"high" => Ok(AuthLevel::High),
_ => Err(AuthError::InvalidRequest(format!(
"Invalid auth level: {}",
s
))),
}
}
}
impl std::fmt::Display for AuthLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let level_str = match self {
AuthLevel::Basic => "basic",
AuthLevel::Mfa => "mfa",
AuthLevel::High => "high",
};
write!(f, "{}", level_str)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdvancedTokenExchangeConfig {
pub enable_multi_party_chains: bool,
pub max_delegation_depth: usize,
pub require_audit_trail: bool,
pub enable_context_preservation: bool,
pub default_token_lifetime: Duration,
pub supported_subject_token_types: Vec<String>,
pub supported_requested_token_types: Vec<String>,
pub exchange_policies: Vec<TokenExchangePolicy>,
pub cross_domain_settings: CrossDomainExchangeSettings,
pub jwt_signing_key: String,
pub jwt_verification_key: String,
pub trusted_issuers: Vec<String>,
}
impl Default for AdvancedTokenExchangeConfig {
fn default() -> Self {
Self {
enable_multi_party_chains: true,
max_delegation_depth: 3,
require_audit_trail: true,
enable_context_preservation: true,
default_token_lifetime: Duration::try_hours(1).unwrap_or(Duration::zero()),
supported_subject_token_types: vec![
"urn:ietf:params:oauth:token-type:jwt".to_string(),
"urn:ietf:params:oauth:token-type:access_token".to_string(),
"urn:ietf:params:oauth:token-type:refresh_token".to_string(),
"urn:ietf:params:oauth:token-type:id_token".to_string(),
"urn:ietf:params:oauth:token-type:saml2".to_string(),
],
supported_requested_token_types: vec![
"urn:ietf:params:oauth:token-type:jwt".to_string(),
"urn:ietf:params:oauth:token-type:access_token".to_string(),
"urn:ietf:params:oauth:token-type:refresh_token".to_string(),
],
exchange_policies: Vec::new(),
cross_domain_settings: CrossDomainExchangeSettings::default(),
jwt_signing_key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...".to_string(), jwt_verification_key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0B...".to_string(), trusted_issuers: vec![
"https://auth.example.com".to_string(),
"https://login.example.org".to_string(),
],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdvancedTokenExchangeRequest {
pub grant_type: String,
pub subject_token: String,
pub subject_token_type: String,
pub actor_token: Option<String>,
pub actor_token_type: Option<String>,
pub requested_token_type: String,
pub scope: Option<String>,
pub audience: Vec<String>,
pub resource: Vec<String>,
pub exchange_context: Option<ExchangeContext>,
pub policy_requirements: Vec<String>,
pub custom_parameters: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExchangeContext {
pub transaction_id: String,
pub business_context: serde_json::Value,
pub delegation_chain: Vec<DelegationLink>,
pub original_request: Option<RequestMetadata>,
pub security_context: Option<SecurityContext>,
pub custom_fields: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DelegationLink {
pub delegator: String,
pub delegatee: String,
pub delegated_at: DateTime<Utc>,
pub delegation_reason: String,
pub delegated_scopes: Vec<String>,
pub restrictions: Vec<DelegationRestriction>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DelegationRestriction {
TimeLimit { expires_at: DateTime<Utc> },
UsageLimit { max_uses: u32, current_uses: u32 },
IpRestriction { allowed_ips: Vec<String> },
ScopeRestriction { restricted_scopes: Vec<String> },
Custom {
restriction_type: String,
parameters: HashMap<String, serde_json::Value>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestMetadata {
pub client_id: String,
pub user_agent: Option<String>,
pub ip_address: Option<String>,
pub timestamp: DateTime<Utc>,
pub headers: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityContext {
pub session_id: Option<String>,
pub authentication_level: String,
pub mfa_completed: bool,
pub risk_score: f64,
pub device_info: Option<DeviceContext>,
pub location_info: Option<LocationContext>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceContext {
pub device_id: String,
pub device_type: String,
pub trust_level: String,
pub fingerprint: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocationContext {
pub country: Option<String>,
pub city: Option<String>,
pub geo_data: Option<serde_json::Value>,
pub network_info: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenExchangePolicy {
pub id: String,
pub name: String,
pub conditions: Vec<PolicyCondition>,
pub actions: Vec<PolicyAction>,
pub mandatory: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PolicyCondition {
SubjectTokenType { allowed_types: Vec<String> },
ScopeRequirement {
required_scopes: Vec<String>,
all_required: bool,
},
DelegationDepth { max_depth: usize },
ClientAuthorization { authorized_clients: Vec<String> },
TimeRestriction {
allowed_hours: Vec<u8>,
timezone: String,
},
Custom {
condition_type: String,
parameters: HashMap<String, serde_json::Value>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PolicyAction {
Allow,
Deny { reason: String },
RequireStepUp { required_level: String },
RestrictScopes { allowed_scopes: Vec<String> },
RestrictLifetime { max_lifetime: Duration },
Custom {
action_type: String,
parameters: HashMap<String, serde_json::Value>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossDomainExchangeSettings {
pub enabled: bool,
pub trusted_domains: Vec<String>,
pub cross_domain_policies: Vec<CrossDomainPolicy>,
pub require_domain_validation: bool,
}
impl Default for CrossDomainExchangeSettings {
fn default() -> Self {
Self {
enabled: false,
trusted_domains: Vec::new(),
cross_domain_policies: Vec::new(),
require_domain_validation: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossDomainPolicy {
pub id: String,
pub source_domain: String,
pub target_domain: String,
pub allowed_token_types: Vec<String>,
pub required_claims: Vec<String>,
pub scope_mappings: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdvancedTokenExchangeResponse {
pub access_token: String,
pub issued_token_type: String,
pub token_type: String,
pub expires_in: Option<u64>,
pub scope: Option<String>,
pub refresh_token: Option<String>,
pub exchange_audit: Option<ExchangeAuditInfo>,
pub preserved_context: Option<ExchangeContext>,
pub additional_parameters: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExchangeAuditInfo {
pub exchange_id: Uuid,
pub timestamp: DateTime<Utc>,
pub exchange_type: TokenExchangeType,
pub subject_info: SubjectInfo,
pub actor_info: Option<ActorInfo>,
pub policy_decisions: Vec<PolicyDecision>,
pub security_assessments: Vec<SecurityAssessment>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TokenExchangeType {
Impersonation,
Delegation,
Translation,
ContextExchange,
Federation,
PrivilegeEscalation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubjectInfo {
pub subject: String,
pub subject_type: String,
pub original_token_info: TokenInfo,
pub attributes: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActorInfo {
pub actor: String,
pub actor_type: String,
pub actor_token_info: TokenInfo,
pub attributes: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenInfo {
pub token_type: String,
pub issuer: String,
pub audience: Vec<String>,
pub scopes: Vec<String>,
pub expires_at: Option<DateTime<Utc>>,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyDecision {
pub policy_id: String,
pub decision: PolicyDecisionResult,
pub reason: String,
pub applied_modifications: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PolicyDecisionResult {
Allow,
Deny,
Modify,
RequireVerification,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAssessment {
pub assessment_type: String,
pub result: SecurityAssessmentResult,
pub risk_score: f64,
pub details: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SecurityAssessmentResult {
Pass,
Fail,
RequiresReview,
Inconclusive,
}
#[async_trait]
pub trait TokenExchangeProcessor: Send + Sync {
async fn process_exchange(
&self,
request: &AdvancedTokenExchangeRequest,
context: &ExchangeContext,
) -> Result<AdvancedTokenExchangeResponse>;
async fn validate_subject_token(&self, token: &str, token_type: &str) -> Result<TokenInfo>;
async fn validate_actor_token(&self, token: &str, token_type: &str) -> Result<TokenInfo>;
async fn generate_exchanged_token(
&self,
subject_info: &SubjectInfo,
actor_info: Option<&ActorInfo>,
request: &AdvancedTokenExchangeRequest,
) -> Result<String>;
}
pub struct AdvancedTokenExchangeManager {
config: AdvancedTokenExchangeConfig,
session_manager: Arc<SessionManager>,
processors: HashMap<String, Arc<dyn TokenExchangeProcessor>>,
exchange_audit: Arc<tokio::sync::RwLock<Vec<ExchangeAuditInfo>>>,
encoding_key: EncodingKey,
decoding_key: DecodingKey,
}
impl AdvancedTokenExchangeManager {
pub fn new(
config: AdvancedTokenExchangeConfig,
session_manager: Arc<SessionManager>,
) -> Result<Self> {
let encoding_key = EncodingKey::from_rsa_pem(config.jwt_signing_key.as_bytes())?;
let decoding_key = DecodingKey::from_rsa_pem(config.jwt_verification_key.as_bytes())?;
Ok(Self {
config,
session_manager,
processors: HashMap::new(),
exchange_audit: Arc::new(tokio::sync::RwLock::new(Vec::new())),
encoding_key,
decoding_key,
})
}
pub fn register_processor(
&mut self,
token_type: String,
processor: Arc<dyn TokenExchangeProcessor>,
) {
self.processors.insert(token_type, processor);
}
pub async fn exchange_token(
&self,
request: AdvancedTokenExchangeRequest,
) -> Result<AdvancedTokenExchangeResponse> {
self.validate_exchange_request(&request).await?;
let context = request
.exchange_context
.clone()
.unwrap_or_else(|| ExchangeContext {
transaction_id: Uuid::new_v4().to_string(),
business_context: serde_json::Value::Null,
delegation_chain: Vec::new(),
original_request: None,
security_context: None,
custom_fields: HashMap::new(),
});
if context.delegation_chain.len() > self.config.max_delegation_depth {
return Err(AuthError::InvalidRequest(
"Maximum delegation depth exceeded".to_string(),
));
}
self.apply_exchange_policies(&request, &context).await?;
let subject_info = self.validate_and_extract_subject_info(&request).await?;
let actor_info = if request.actor_token.is_some() {
Some(self.validate_and_extract_actor_info(&request).await?)
} else {
None
};
let exchange_type = self.determine_exchange_type(&request, &subject_info, &actor_info);
let processor = self.get_processor(&request.requested_token_type)?;
let mut response = processor.process_exchange(&request, &context).await?;
let audit_info = self
.create_audit_info(
exchange_type,
&subject_info,
&actor_info,
&request,
&context,
)
.await?;
{
let mut audit_log = self.exchange_audit.write().await;
audit_log.push(audit_info.clone());
}
if self.config.require_audit_trail {
response.exchange_audit = Some(audit_info.clone());
let audit_token = self.generate_audit_token(&audit_info)?;
response.additional_parameters.insert(
"audit_token".to_string(),
serde_json::Value::String(audit_token),
);
}
if self.config.enable_context_preservation {
let preserved_context = context.clone();
response.preserved_context = Some(preserved_context.clone());
let delegation_token = self.generate_delegation_token(&preserved_context)?;
response.additional_parameters.insert(
"delegation_token".to_string(),
serde_json::Value::String(delegation_token),
);
}
Ok(response)
}
pub async fn get_exchange_audit(&self) -> Vec<ExchangeAuditInfo> {
let audit_log = self.exchange_audit.read().await;
audit_log.clone()
}
async fn validate_exchange_request(
&self,
request: &AdvancedTokenExchangeRequest,
) -> Result<()> {
if request.grant_type != "urn:ietf:params:oauth:grant-type:token-exchange" {
return Err(AuthError::InvalidRequest(
"Invalid grant type for token exchange".to_string(),
));
}
if !self
.config
.supported_subject_token_types
.contains(&request.subject_token_type)
{
return Err(AuthError::InvalidRequest(format!(
"Unsupported subject token type: {}",
request.subject_token_type
)));
}
if !self
.config
.supported_requested_token_types
.contains(&request.requested_token_type)
{
return Err(AuthError::InvalidRequest(format!(
"Unsupported requested token type: {}",
request.requested_token_type
)));
}
if let Some(ref actor_token_type) = request.actor_token_type
&& !self
.config
.supported_subject_token_types
.contains(actor_token_type)
{
return Err(AuthError::InvalidRequest(format!(
"Unsupported actor token type: {}",
actor_token_type
)));
}
Ok(())
}
async fn apply_exchange_policies(
&self,
request: &AdvancedTokenExchangeRequest,
context: &ExchangeContext,
) -> Result<()> {
if request.subject_token_type == "urn:ietf:params:oauth:token-type:jwt" {
match self.introspect_jwt_token(&request.subject_token) {
Ok(token_claims) => {
if let Some(iss) = token_claims.get("iss").and_then(|v| v.as_str())
&& !self.config.trusted_issuers.contains(&iss.to_string())
{
return Err(AuthError::InvalidRequest(format!(
"Token issued by untrusted issuer: {}",
iss
)));
}
}
Err(_) => {
}
}
}
for policy in &self.config.exchange_policies {
let policy_applies = self.evaluate_policy_conditions(policy, request, context)?;
if policy_applies {
for action in &policy.actions {
match action {
PolicyAction::Deny { reason } => {
return Err(AuthError::InvalidRequest(format!(
"Exchange denied by policy '{}': {}",
policy.name, reason
)));
}
PolicyAction::RequireStepUp { required_level } => {
let auth_level = required_level.parse::<AuthLevel>().map_err(|_| {
AuthError::InvalidRequest(format!(
"Invalid authentication level: {}",
required_level
))
})?;
return self
.handle_step_up_authentication(auth_level, context)
.await;
}
_ => {
}
}
}
}
}
Ok(())
}
fn evaluate_policy_conditions(
&self,
policy: &TokenExchangePolicy,
request: &AdvancedTokenExchangeRequest,
context: &ExchangeContext,
) -> Result<bool> {
for condition in &policy.conditions {
match condition {
PolicyCondition::SubjectTokenType { allowed_types } => {
if !allowed_types.contains(&request.subject_token_type) {
return Ok(false);
}
}
PolicyCondition::DelegationDepth { max_depth } => {
if context.delegation_chain.len() > *max_depth {
return Ok(false);
}
}
PolicyCondition::ScopeRequirement {
required_scopes,
all_required,
} => {
if let Some(ref scope) = request.scope {
let request_scopes: HashSet<&str> = scope.split(' ').collect();
let required: HashSet<&str> =
required_scopes.iter().map(|s| s.as_str()).collect();
if *all_required {
if !required.is_subset(&request_scopes) {
return Ok(false);
}
} else if required.is_disjoint(&request_scopes) {
return Ok(false);
}
}
}
_ => {
}
}
}
Ok(true)
}
async fn validate_and_extract_subject_info(
&self,
request: &AdvancedTokenExchangeRequest,
) -> Result<SubjectInfo> {
let processor = self.get_processor(&request.subject_token_type)?;
let token_info = processor
.validate_subject_token(&request.subject_token, &request.subject_token_type)
.await?;
Ok(SubjectInfo {
subject: token_info
.metadata
.get("sub")
.and_then(|v| v.as_str())
.unwrap_or("unknown")
.to_string(),
subject_type: "user".to_string(), original_token_info: token_info,
attributes: HashMap::new(),
})
}
async fn validate_and_extract_actor_info(
&self,
request: &AdvancedTokenExchangeRequest,
) -> Result<ActorInfo> {
let actor_token = request.actor_token.as_ref().unwrap();
let actor_token_type = request.actor_token_type.as_ref().unwrap();
let processor = self.get_processor(actor_token_type)?;
let token_info = processor
.validate_actor_token(actor_token, actor_token_type)
.await?;
Ok(ActorInfo {
actor: token_info
.metadata
.get("sub")
.and_then(|v| v.as_str())
.unwrap_or("unknown")
.to_string(),
actor_type: "service".to_string(), actor_token_info: token_info,
attributes: HashMap::new(),
})
}
fn determine_exchange_type(
&self,
request: &AdvancedTokenExchangeRequest,
_subject_info: &SubjectInfo,
actor_info: &Option<ActorInfo>,
) -> TokenExchangeType {
if actor_info.is_some() {
TokenExchangeType::Delegation
} else if request.exchange_context.is_some() {
TokenExchangeType::ContextExchange
} else if request.subject_token_type != request.requested_token_type {
TokenExchangeType::Translation
} else {
TokenExchangeType::Impersonation
}
}
fn get_processor(&self, token_type: &str) -> Result<Arc<dyn TokenExchangeProcessor>> {
self.processors.get(token_type).cloned().ok_or_else(|| {
AuthError::InvalidRequest(format!(
"No processor registered for token type: {}",
token_type
))
})
}
async fn create_audit_info(
&self,
exchange_type: TokenExchangeType,
_subject_info: &SubjectInfo,
actor_info: &Option<ActorInfo>,
_request: &AdvancedTokenExchangeRequest,
_context: &ExchangeContext,
) -> Result<ExchangeAuditInfo> {
Ok(ExchangeAuditInfo {
exchange_id: Uuid::new_v4(),
timestamp: Utc::now(),
exchange_type,
subject_info: _subject_info.clone(),
actor_info: actor_info.clone(),
policy_decisions: Vec::new(), security_assessments: Vec::new(), })
}
pub async fn cleanup_old_audit_entries(&self, older_than: DateTime<Utc>) -> usize {
let mut audit_log = self.exchange_audit.write().await;
let original_len = audit_log.len();
audit_log.retain(|entry| entry.timestamp > older_than);
original_len - audit_log.len()
}
pub fn generate_audit_token(&self, audit_info: &ExchangeAuditInfo) -> Result<String> {
use jsonwebtoken::{Algorithm, Header, encode};
use serde_json::json;
let header = Header::new(Algorithm::HS256);
let claims = json!({
"iss": "advanced-token-exchange",
"sub": audit_info.subject_info.subject,
"aud": "audit-verification",
"exp": (Utc::now() + Duration::seconds(3600)).timestamp(),
"iat": Utc::now().timestamp(),
"exchange_id": audit_info.exchange_id,
"exchange_type": audit_info.exchange_type,
"timestamp": audit_info.timestamp,
"policy_decisions": audit_info.policy_decisions.len(),
"security_assessments": audit_info.security_assessments.len()
});
encode(&header, &claims, &self.encoding_key).map_err(|e| {
AuthError::TokenGeneration(format!("Failed to generate audit token: {}", e))
})
}
pub fn validate_delegation_token(&self, token: &str) -> Result<serde_json::Value> {
use jsonwebtoken::{Algorithm, Validation, decode};
let mut validation = Validation::new(Algorithm::HS256);
validation.set_audience(&["delegation-context"]);
validation.set_issuer(&["advanced-token-exchange"]);
let token_data = decode::<serde_json::Value>(token, &self.decoding_key, &validation)
.map_err(|e| AuthError::InvalidToken(format!("Invalid delegation token: {}", e)))?;
Ok(token_data.claims)
}
pub fn generate_delegation_token(&self, context: &ExchangeContext) -> Result<String> {
use jsonwebtoken::{Algorithm, Header, encode};
use serde_json::json;
let header = Header::new(Algorithm::RS256);
let claims = json!({
"iss": "advanced-token-exchange",
"aud": "delegation-context",
"exp": (Utc::now() + Duration::seconds(1800)).timestamp(), "iat": Utc::now().timestamp(),
"transaction_id": context.transaction_id,
"delegation_chain_length": context.delegation_chain.len(),
"delegation_chain": context.delegation_chain,
"business_context": context.business_context,
"custom_fields": context.custom_fields
});
encode(&header, &claims, &self.encoding_key).map_err(|e| {
AuthError::TokenGeneration(format!("Failed to generate delegation token: {}", e))
})
}
pub fn introspect_jwt_token(&self, token: &str) -> Result<serde_json::Value> {
use jsonwebtoken::{Algorithm, Validation, decode};
let mut validation = Validation::new(Algorithm::RS256);
validation.insecure_disable_signature_validation();
let token_data = decode::<serde_json::Value>(token, &self.decoding_key, &validation)
.map_err(|e| AuthError::InvalidToken(format!("Token introspection failed: {}", e)))?;
Ok(token_data.claims)
}
async fn handle_step_up_authentication(
&self,
required_level: AuthLevel,
context: &ExchangeContext,
) -> Result<()> {
if let Some(session_id) = context
.security_context
.as_ref()
.and_then(|sc| sc.session_id.as_ref())
{
match self.session_manager.get_session(session_id) {
Some(session) => {
if let Some(current_level) = session
.metadata
.get("auth_level")
.map(|v| v.as_str())
.and_then(|s| s.parse::<AuthLevel>().ok())
&& current_level >= required_level
{
return Ok(());
}
Err(AuthError::StepUpRequired {
current_level: session
.metadata
.get("auth_level")
.map(|v| v.as_str())
.unwrap_or("basic")
.to_string(),
required_level: required_level.to_string(),
step_up_url: format!(
"/auth/step-up?session_id={}&level={}",
session_id, required_level
),
})
}
None => {
Err(AuthError::Unauthorized(
"No active session found".to_string(),
))
}
}
} else {
Err(AuthError::Unauthorized(
"No session context available".to_string(),
))
}
}
}
#[async_trait]
impl TokenExchangeService for AdvancedTokenExchangeManager {
type Request = AdvancedTokenExchangeRequest;
type Response = AdvancedTokenExchangeResponse;
type Config = AdvancedTokenExchangeConfig;
async fn exchange_token(&self, request: Self::Request) -> Result<Self::Response> {
self.exchange_token(request).await
}
async fn validate_token(&self, token: &str, token_type: &str) -> Result<TokenValidationResult> {
let supported_types = self.supported_subject_token_types();
ValidationUtils::validate_token_type(token_type, &supported_types)?;
if ValidationUtils::is_jwt_token_type(token_type) {
match self.introspect_jwt_token(token) {
Ok(claims) => {
let subject = ValidationUtils::extract_subject(
&claims
.as_object()
.unwrap_or(&serde_json::Map::new())
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
);
let scopes = ValidationUtils::extract_scopes(
&claims
.as_object()
.unwrap_or(&serde_json::Map::new())
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
None,
);
Ok(TokenValidationResult {
is_valid: true,
subject,
issuer: claims
.get("iss")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
audience: claims
.get("aud")
.and_then(|v| v.as_str())
.map(|s| vec![s.to_string()])
.unwrap_or_default(),
scopes,
expires_at: claims.get("exp").and_then(|v| v.as_i64()).and_then(|exp| {
use chrono::{TimeZone, Utc};
Utc.timestamp_opt(exp, 0).single()
}),
metadata: claims
.as_object()
.unwrap_or(&serde_json::Map::new())
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
validation_messages: Vec::new(),
})
}
Err(e) => Ok(TokenValidationResult {
is_valid: false,
subject: None,
issuer: None,
audience: Vec::new(),
scopes: Vec::new(),
expires_at: None,
metadata: std::collections::HashMap::new(),
validation_messages: vec![format!("JWT validation failed: {}", e)],
}),
}
} else {
Ok(TokenValidationResult {
is_valid: true, subject: None,
issuer: None,
audience: Vec::new(),
scopes: Vec::new(),
expires_at: None,
metadata: std::collections::HashMap::new(),
validation_messages: vec![format!(
"Basic validation for token type: {}",
token_type
)],
})
}
}
fn supported_subject_token_types(&self) -> Vec<String> {
self.config.supported_subject_token_types.clone()
}
fn supported_requested_token_types(&self) -> Vec<String> {
self.config.supported_requested_token_types.clone()
}
fn capabilities(&self) -> TokenExchangeCapabilities {
TokenExchangeCapabilities {
basic_exchange: true,
multi_party_chains: self.config.enable_multi_party_chains,
context_preservation: self.config.enable_context_preservation,
audit_trail: self.config.require_audit_trail,
session_integration: true, jwt_operations: true, policy_control: true, cross_domain_exchange: self.config.cross_domain_settings.enabled,
max_delegation_depth: self.config.max_delegation_depth,
complexity_level: ServiceComplexityLevel::Advanced,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_config_creation() {
let config = AdvancedTokenExchangeConfig::default();
assert!(config.enable_multi_party_chains);
assert!(!config.supported_subject_token_types.is_empty());
assert!(!config.supported_requested_token_types.is_empty());
assert!(!config.trusted_issuers.is_empty());
}
#[test]
fn test_jwt_key_functionality() {
use crate::server::oidc::oidc_session_management::SessionManager;
use jsonwebtoken::{DecodingKey, EncodingKey};
let secret = b"test-secret-key-32-bytes-minimum!";
let encoding_key = EncodingKey::from_secret(secret);
let decoding_key = DecodingKey::from_secret(secret);
let config = AdvancedTokenExchangeConfig {
jwt_signing_key: "test-secret-key-32-bytes-minimum!".to_string(),
jwt_verification_key: "test-secret-key-32-bytes-minimum!".to_string(),
..Default::default()
};
let session_manager = Arc::new(SessionManager::new(Default::default()));
let manager = AdvancedTokenExchangeManager {
config,
session_manager,
processors: HashMap::new(),
exchange_audit: Arc::new(tokio::sync::RwLock::new(Vec::new())),
encoding_key,
decoding_key,
};
let audit_info = ExchangeAuditInfo {
exchange_id: Uuid::new_v4(),
timestamp: Utc::now(),
exchange_type: TokenExchangeType::Delegation,
subject_info: SubjectInfo {
subject: "test_user".to_string(),
subject_type: "user".to_string(),
original_token_info: TokenInfo {
token_type: "jwt".to_string(),
issuer: "test".to_string(),
audience: vec!["test".to_string()],
scopes: vec!["read".to_string()],
expires_at: None,
metadata: HashMap::new(),
},
attributes: HashMap::new(),
},
actor_info: None,
policy_decisions: Vec::new(),
security_assessments: Vec::new(),
};
let result = manager.generate_audit_token(&audit_info);
assert!(
result.is_ok(),
"JWT keys should be properly initialized for signing"
);
}
#[test]
fn test_exchange_request_creation() {
let request = AdvancedTokenExchangeRequest {
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange".to_string(),
subject_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9".to_string(),
subject_token_type: "urn:ietf:params:oauth:token-type:jwt".to_string(),
requested_token_type: "urn:ietf:params:oauth:token-type:access_token".to_string(),
actor_token: None,
actor_token_type: None,
scope: Some("read write".to_string()),
audience: vec!["https://api.example.com".to_string()],
resource: Vec::new(),
exchange_context: None,
policy_requirements: Vec::new(),
custom_parameters: HashMap::new(),
};
assert_eq!(
request.grant_type,
"urn:ietf:params:oauth:grant-type:token-exchange"
);
assert_eq!(
request.subject_token_type,
"urn:ietf:params:oauth:token-type:jwt"
);
}
#[test]
fn test_exchange_context_creation() {
let context = ExchangeContext {
transaction_id: "txn_123".to_string(),
business_context: serde_json::json!({
"operation": "payment",
"amount": 100.0
}),
delegation_chain: Vec::new(),
original_request: None,
security_context: None,
custom_fields: HashMap::new(),
};
assert_eq!(context.transaction_id, "txn_123");
assert_eq!(context.business_context["operation"], "payment");
}
#[test]
fn test_delegation_link_creation() {
let link = DelegationLink {
delegator: "service_a".to_string(),
delegatee: "service_b".to_string(),
delegated_at: Utc::now(),
delegation_reason: "API call forwarding".to_string(),
delegated_scopes: vec!["read".to_string(), "write".to_string()],
restrictions: Vec::new(),
};
assert_eq!(link.delegator, "service_a");
assert_eq!(link.delegated_scopes.len(), 2);
}
}