refactor: rename JWT issuer foxhunt-api-gateway → foxhunt-api, drop compat aliases

- JWT issuer now foxhunt-api across all 16 files (services, tests, config, docker-compose)
- Remove serde alias api_gateway_url from FxtConfig (no backwards compat)
- Remove api_gateway CLI alias from e2e orchestrator
- All services must deploy simultaneously for JWT validation to match

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-05 00:22:04 +01:00
parent 075f715fa1
commit 08d070bb95
16 changed files with 34 additions and 42 deletions

View File

@@ -86,7 +86,7 @@ impl Default for BrokerConfig {
#[derive(Debug, Serialize, Deserialize)]
pub struct FxtConfig {
/// Unified API URL (default: <https://api.fxhnt.ai>)
#[serde(default = "default_api_url", alias = "api_gateway_url")]
#[serde(default = "default_api_url")]
pub api_url: String,
/// Log level (default: info)
@@ -251,14 +251,6 @@ mod tests {
assert_eq!(config.token_storage, "keyring");
}
#[test]
fn test_backwards_compat_alias() {
// Old config files using api_gateway_url must still deserialize correctly.
let toml_str = r#"api_gateway_url = "http://legacy:50051""#;
let config: FxtConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.api_url, "http://legacy:50051");
}
#[test]
fn test_config_with_broker_section() {
let toml_str = r#"

View File

@@ -49,7 +49,7 @@ impl Default for TestJwtConfig {
Self {
// Use same secret as API Gateway tests for compatibility
secret: "test-secret-must-be-at-least-64-characters-long-for-security-validation-ok-1234567890".to_string(),
issuer: "foxhunt-api-gateway".to_string(),
issuer: "foxhunt-api".to_string(),
audience: "foxhunt-services".to_string(),
}
}

View File

@@ -57,7 +57,7 @@ pub struct TestJwtClaims {
/// Not before timestamp (optional)
#[serde(skip_serializing_if = "Option::is_none")]
pub nbf: Option<u64>,
/// Issuer (should be "foxhunt-api-gateway")
/// Issuer (should be "foxhunt-api")
pub iss: String,
/// Audience (should be "foxhunt-services")
pub aud: String,
@@ -93,7 +93,7 @@ impl Default for TestJwtConfig {
.unwrap_or_else(|_| {
"test-secret-must-be-at-least-64-characters-long-for-security-validation-ok-1234567890".to_string()
}),
issuer: "foxhunt-api-gateway".to_string(),
issuer: "foxhunt-api".to_string(),
audience: "foxhunt-services".to_string(),
}
}
@@ -518,7 +518,7 @@ mod tests {
#[test]
fn test_jwt_config_default() {
let config = TestJwtConfig::default();
assert_eq!(config.issuer, "foxhunt-api-gateway");
assert_eq!(config.issuer, "foxhunt-api");
assert_eq!(config.audience, "foxhunt-services");
assert!(
config.secret.len() >= 64,

View File

@@ -29,7 +29,7 @@ pub struct JwtConfig {
)]
pub jwt_secret: SecretString,
/// JWT issuer (e.g., "foxhunt-api-gateway")
/// JWT issuer (e.g., "foxhunt-api")
pub jwt_issuer: String,
/// JWT audience (e.g., "foxhunt-services")
@@ -154,7 +154,7 @@ impl JwtConfig {
///
/// Reads from:
/// - JWT_SECRET: Signing secret (minimum 64 characters)
/// - JWT_ISSUER: Token issuer (default: "foxhunt-api-gateway")
/// - JWT_ISSUER: Token issuer (default: "foxhunt-api")
/// - JWT_AUDIENCE: Token audience (default: "foxhunt-services")
fn load_from_env() -> Result<Self> {
let jwt_secret = std::env::var("JWT_SECRET").context(
@@ -162,7 +162,7 @@ impl JwtConfig {
)?;
let jwt_issuer =
std::env::var("JWT_ISSUER").unwrap_or_else(|_| "foxhunt-api-gateway".to_string());
std::env::var("JWT_ISSUER").unwrap_or_else(|_| "foxhunt-api".to_string());
let jwt_audience =
std::env::var("JWT_AUDIENCE").unwrap_or_else(|_| "foxhunt-services".to_string());
@@ -286,7 +286,7 @@ mod tests {
jwt_secret: SecretString::from(
"JcqslC17wjp3hG/O1bHLwsVS7CfmfbJuXccnJ4XFJMeC3dhV1s46C4NhmDNCHK/o+7j7ok5uYJdqGcOU+NhBSA==".to_string()
),
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-services".to_string(),
rotation_date: Some("2025-10-18".to_string()),
};
@@ -298,7 +298,7 @@ mod tests {
fn test_jwt_config_validation_too_short() {
let config = JwtConfig {
jwt_secret: SecretString::from("short_secret_32chars_only!!!!!".to_string()),
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-services".to_string(),
rotation_date: None,
};
@@ -316,7 +316,7 @@ mod tests {
let weak_secret = "a".repeat(70); // 70 chars but all same character
let config = JwtConfig {
jwt_secret: SecretString::from(weak_secret),
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-services".to_string(),
rotation_date: None,
};
@@ -329,7 +329,7 @@ mod tests {
fn test_jwt_config_debug_redacts_secret() {
let config = JwtConfig {
jwt_secret: SecretString::from("test-secret-should-not-appear".to_string()),
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-services".to_string(),
rotation_date: None,
};

View File

@@ -199,7 +199,7 @@ services:
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=foxhunt-dev-root
- JWT_SECRET=${JWT_SECRET:-dev_secret_key_change_in_production}
- JWT_ISSUER=foxhunt-api-gateway
- JWT_ISSUER=foxhunt-api
- JWT_AUDIENCE=foxhunt-services
# TLS Configuration - Wave H1 mTLS implementation
- TLS_ENABLED=${TLS_ENABLED:-false}
@@ -255,7 +255,7 @@ services:
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=foxhunt-dev-root
- JWT_SECRET=${JWT_SECRET:-dev_secret_key_change_in_production}
- JWT_ISSUER=foxhunt-api-gateway
- JWT_ISSUER=foxhunt-api
- JWT_AUDIENCE=foxhunt-services
- BENZINGA_API_KEY=${BENZINGA_API_KEY:-demo_key_please_replace}
# DBN Data Configuration - Wave 153 Real Data Integration
@@ -313,7 +313,7 @@ services:
- VAULT_ADDR=http://vault:8200
- VAULT_TOKEN=foxhunt-dev-root
- JWT_SECRET=${JWT_SECRET:-dev_secret_key_change_in_production}
- JWT_ISSUER=foxhunt-api-gateway
- JWT_ISSUER=foxhunt-api
- JWT_AUDIENCE=foxhunt-services
# GPU Configuration
- NVIDIA_VISIBLE_DEVICES=all

View File

@@ -52,7 +52,7 @@ fn generate_test_jwt() -> String {
iat: now,
exp: now + 3600,
nbf: Some(now),
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-services".to_string(),
roles: vec!["trader".to_string()],
permissions: vec!["trading.submit_order".to_string()],

View File

@@ -103,7 +103,7 @@ impl JwtConfig {
Ok(Self {
jwt_secret,
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-services".to_string(),
})
}
@@ -454,7 +454,7 @@ mod tests {
.await
.expect("Should create config with valid JWT_SECRET");
assert_eq!(config.jwt_issuer, "foxhunt-api-gateway");
assert_eq!(config.jwt_issuer, "foxhunt-api");
assert_eq!(config.jwt_audience, "foxhunt-services");
assert!(config.jwt_secret.len() >= 64);
@@ -484,7 +484,7 @@ mod tests {
match result {
Ok(config) => {
// Verify config was loaded successfully
assert_eq!(config.jwt_issuer, "foxhunt-api-gateway");
assert_eq!(config.jwt_issuer, "foxhunt-api");
assert_eq!(config.jwt_audience, "foxhunt-services");
assert!(config.jwt_secret.len() >= 64);

View File

@@ -18,7 +18,7 @@ impl Default for TestJwtConfig {
fn default() -> Self {
Self {
secret: "test-secret-must-be-at-least-64-characters-long-for-security-validation-ok-1234567890".to_string(),
issuer: "foxhunt-api-gateway".to_string(),
issuer: "foxhunt-api".to_string(),
audience: "foxhunt-services".to_string(),
}
}
@@ -98,7 +98,7 @@ pub fn generate_invalid_signature_token(user_id: &str) -> Result<String> {
iat: now,
exp: now + 3600,
nbf: Some(now), // Not before: valid from now
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-services".to_string(),
roles: vec!["trader".to_string()],
permissions: vec!["api.access".to_string()],

View File

@@ -94,7 +94,7 @@ fn create_valid_jwt_token() -> Result<String> {
sub: "test_user_001".to_string(),
exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
iat: Utc::now().timestamp() as usize,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-trading".to_string(),
roles: vec!["trader".to_string()],
permissions: vec!["trade:submit".to_string(), "trade:cancel".to_string()],
@@ -135,7 +135,7 @@ fn create_expired_jwt_token() -> Result<String> {
sub: "test_user_002".to_string(),
exp: (Utc::now() - Duration::hours(1)).timestamp() as usize, // Expired 1 hour ago
iat: (Utc::now() - Duration::hours(2)).timestamp() as usize,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-trading".to_string(),
roles: vec!["trader".to_string()],
permissions: vec!["trade:submit".to_string()],
@@ -408,7 +408,7 @@ async fn test_submit_order_insufficient_role_returns_permission_denied() -> Resu
sub: "readonly_user".to_string(),
exp: (Utc::now() + ChronoDuration::hours(1)).timestamp() as usize,
iat: Utc::now().timestamp() as usize,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-trading".to_string(),
roles: vec!["viewer".to_string()], // Read-only role
permissions: vec!["read:orders".to_string()],

View File

@@ -91,7 +91,7 @@ fn create_valid_jwt_token() -> Result<String> {
sub: "test_user_001".to_string(),
exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
iat: Utc::now().timestamp() as usize,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-backtesting".to_string(),
roles: vec!["trader".to_string()],
permissions: vec!["backtest:run".to_string()],

View File

@@ -82,7 +82,7 @@ fn create_valid_jwt_token() -> Result<String> {
sub: "test_ml_user_001".to_string(),
exp: (Utc::now() + Duration::hours(1)).timestamp() as usize,
iat: Utc::now().timestamp() as usize,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-ml-training".to_string(),
roles: vec!["ml_engineer".to_string()],
permissions: vec!["ml:train".to_string(), "ml:manage".to_string()],

View File

@@ -119,7 +119,7 @@ impl Default for AuthConfig {
enable_jwt: false, // API Gateway handles JWT
enable_api_keys: false, // API Gateway handles API keys
enable_mtls: false, // API Gateway handles mTLS
jwt_issuer: "foxhunt-api-gateway".to_string(),
jwt_issuer: "foxhunt-api".to_string(),
jwt_audience: "foxhunt-trading-service".to_string(),
}
}

View File

@@ -699,7 +699,7 @@ fn parse_service_list(services_str: &str) -> Result<Vec<ServiceType>> {
];
break;
},
"api" | "api_gateway" | "gateway" => services.push(ServiceType::ApiGateway),
"api" | "gateway" => services.push(ServiceType::ApiGateway),
"trading" => services.push(ServiceType::TradingService),
"backtesting" => services.push(ServiceType::BacktestingService),
"ml_training" | "ml" => services.push(ServiceType::MLTrainingService),

View File

@@ -112,7 +112,7 @@ impl E2ETestFramework {
sub: "e2e_test_user".to_string(),
exp: now + 3600, // 1 hour expiration
iat: now,
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-services".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
roles: vec!["trader".to_string(), "admin".to_string()],

View File

@@ -27,7 +27,7 @@ fn create_test_token() -> Result<String, Box<dyn std::error::Error>> {
let claims = Claims {
sub: "test_user".to_string(),
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-services".to_string(),
iat: now,
exp: now + 3600, // Valid for 1 hour
@@ -77,7 +77,7 @@ async fn test_jwt_token_validation() {
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
assert_eq!(token_data.claims.sub, "test_user");
assert_eq!(token_data.claims.iss, "foxhunt-api-gateway");
assert_eq!(token_data.claims.iss, "foxhunt-api");
assert_eq!(token_data.claims.aud, "foxhunt-services");
assert!(token_data.claims.roles.contains(&"trader".to_string()));
@@ -98,7 +98,7 @@ async fn test_jwt_token_expiration() {
let expired_claims = Claims {
sub: "test_user".to_string(),
iss: "foxhunt-api-gateway".to_string(),
iss: "foxhunt-api".to_string(),
aud: "foxhunt-services".to_string(),
iat: now - 7200, // Issued 2 hours ago
exp: now - 3600, // Expired 1 hour ago

View File

@@ -265,7 +265,7 @@ pub fn create_test_jwt(config: TestAuthConfig) -> Result<String> {
sub: config.user_id,
iat: now.timestamp() as u64,
exp: (now + config.expiry_duration).timestamp() as u64,
iss: "foxhunt-api-gateway".to_string(), // MUST match API Gateway JwtConfig
iss: "foxhunt-api".to_string(), // MUST match API Gateway JwtConfig
aud: "foxhunt-services".to_string(), // MUST match API Gateway JwtConfig
roles: config.roles,
permissions: config.permissions,
@@ -433,7 +433,7 @@ mod tests {
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
let mut validation = Validation::new(Algorithm::HS256);
validation.validate_exp = false; // Disable expiry check to decode
validation.set_issuer(&["foxhunt-api-gateway"]);
validation.set_issuer(&["foxhunt-api"]);
validation.set_audience(&["foxhunt-services"]);
let jwt_secret = get_test_jwt_secret();