From 08d070bb952979a12a4d816445ce3acd47948755 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Thu, 5 Mar 2026 00:22:04 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20rename=20JWT=20issuer=20foxhunt-api?= =?UTF-8?q?-gateway=20=E2=86=92=20foxhunt-api,=20drop=20compat=20aliases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- bin/fxt/src/config.rs | 10 +--------- bin/fxt/tests/test_helpers/mod.rs | 2 +- crates/common/src/test_utils.rs | 6 +++--- crates/config/src/jwt_config.rs | 14 +++++++------- docker-compose.yml | 6 +++--- services/api/benches/proxy_latency.rs | 2 +- services/api/src/auth/jwt/service.rs | 6 +++--- services/api/tests/common/mod.rs | 4 ++-- services/api/tests/grpc_error_handling.rs | 6 +++--- .../tests/grpc_error_handling.rs | 2 +- .../tests/grpc_error_handling.rs | 2 +- services/trading_service/src/auth_interceptor.rs | 2 +- testing/e2e/src/bin/service_orchestrator.rs | 2 +- testing/e2e/src/framework.rs | 2 +- .../integration/smoke_tests/authentication_flow.rs | 6 +++--- .../tests/common/auth_helpers.rs | 4 ++-- 16 files changed, 34 insertions(+), 42 deletions(-) diff --git a/bin/fxt/src/config.rs b/bin/fxt/src/config.rs index ff87ae747..7eaa0bd8e 100644 --- a/bin/fxt/src/config.rs +++ b/bin/fxt/src/config.rs @@ -86,7 +86,7 @@ impl Default for BrokerConfig { #[derive(Debug, Serialize, Deserialize)] pub struct FxtConfig { /// Unified API URL (default: ) - #[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#" diff --git a/bin/fxt/tests/test_helpers/mod.rs b/bin/fxt/tests/test_helpers/mod.rs index d09e2a4ba..ae25c6a09 100644 --- a/bin/fxt/tests/test_helpers/mod.rs +++ b/bin/fxt/tests/test_helpers/mod.rs @@ -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(), } } diff --git a/crates/common/src/test_utils.rs b/crates/common/src/test_utils.rs index 7f1f7caef..60bce52cd 100644 --- a/crates/common/src/test_utils.rs +++ b/crates/common/src/test_utils.rs @@ -57,7 +57,7 @@ pub struct TestJwtClaims { /// Not before timestamp (optional) #[serde(skip_serializing_if = "Option::is_none")] pub nbf: Option, - /// 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, diff --git a/crates/config/src/jwt_config.rs b/crates/config/src/jwt_config.rs index fef172b07..063c5772b 100644 --- a/crates/config/src/jwt_config.rs +++ b/crates/config/src/jwt_config.rs @@ -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 { 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, }; diff --git a/docker-compose.yml b/docker-compose.yml index 0cc871d6a..fb39277ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/services/api/benches/proxy_latency.rs b/services/api/benches/proxy_latency.rs index f8cdd110c..769108904 100644 --- a/services/api/benches/proxy_latency.rs +++ b/services/api/benches/proxy_latency.rs @@ -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()], diff --git a/services/api/src/auth/jwt/service.rs b/services/api/src/auth/jwt/service.rs index 53b769d76..7dddaa8f3 100644 --- a/services/api/src/auth/jwt/service.rs +++ b/services/api/src/auth/jwt/service.rs @@ -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); diff --git a/services/api/tests/common/mod.rs b/services/api/tests/common/mod.rs index 02e76e3ab..a01ac9908 100644 --- a/services/api/tests/common/mod.rs +++ b/services/api/tests/common/mod.rs @@ -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 { 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()], diff --git a/services/api/tests/grpc_error_handling.rs b/services/api/tests/grpc_error_handling.rs index 9b4e79c10..efdc28cfb 100644 --- a/services/api/tests/grpc_error_handling.rs +++ b/services/api/tests/grpc_error_handling.rs @@ -94,7 +94,7 @@ fn create_valid_jwt_token() -> Result { 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 { 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()], diff --git a/services/backtesting_service/tests/grpc_error_handling.rs b/services/backtesting_service/tests/grpc_error_handling.rs index 27ea4c3eb..b5cd40a67 100644 --- a/services/backtesting_service/tests/grpc_error_handling.rs +++ b/services/backtesting_service/tests/grpc_error_handling.rs @@ -91,7 +91,7 @@ fn create_valid_jwt_token() -> Result { 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()], diff --git a/services/ml_training_service/tests/grpc_error_handling.rs b/services/ml_training_service/tests/grpc_error_handling.rs index 31647cb1f..5b08fbc4d 100644 --- a/services/ml_training_service/tests/grpc_error_handling.rs +++ b/services/ml_training_service/tests/grpc_error_handling.rs @@ -82,7 +82,7 @@ fn create_valid_jwt_token() -> Result { 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()], diff --git a/services/trading_service/src/auth_interceptor.rs b/services/trading_service/src/auth_interceptor.rs index b70d7cb5e..da273822d 100644 --- a/services/trading_service/src/auth_interceptor.rs +++ b/services/trading_service/src/auth_interceptor.rs @@ -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(), } } diff --git a/testing/e2e/src/bin/service_orchestrator.rs b/testing/e2e/src/bin/service_orchestrator.rs index 6f988d693..6b1f87dc9 100644 --- a/testing/e2e/src/bin/service_orchestrator.rs +++ b/testing/e2e/src/bin/service_orchestrator.rs @@ -699,7 +699,7 @@ fn parse_service_list(services_str: &str) -> Result> { ]; 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), diff --git a/testing/e2e/src/framework.rs b/testing/e2e/src/framework.rs index 2421a0a15..f853cb3cb 100644 --- a/testing/e2e/src/framework.rs +++ b/testing/e2e/src/framework.rs @@ -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()], diff --git a/testing/integration/smoke_tests/authentication_flow.rs b/testing/integration/smoke_tests/authentication_flow.rs index f38b0ebd9..f4fe38d55 100644 --- a/testing/integration/smoke_tests/authentication_flow.rs +++ b/testing/integration/smoke_tests/authentication_flow.rs @@ -27,7 +27,7 @@ fn create_test_token() -> Result> { 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)?; 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 diff --git a/testing/service-integration/tests/common/auth_helpers.rs b/testing/service-integration/tests/common/auth_helpers.rs index 5f51abf4c..49a69a04f 100644 --- a/testing/service-integration/tests/common/auth_helpers.rs +++ b/testing/service-integration/tests/common/auth_helpers.rs @@ -265,7 +265,7 @@ pub fn create_test_jwt(config: TestAuthConfig) -> Result { 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();