This guide provides step-by-step instructions for implementing microservice-to-microservice authentication in your service.
- PostgreSQL database with migrations applied
- Redis instance for caching
- OAuth 2.0 token endpoint configured
- Service registered in the system
Use the admin API to register your service:
curl -X POST https://api.aframp.com/admin/services/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <admin-token>" \
-d '{
"service_name": "my_service",
"allowed_scopes": ["my_service:execute"],
"allowed_targets": ["/api/internal/*"]
}'Response:
{
"service_name": "my_service",
"client_id": "service_my_service",
"client_secret": "svc_secret_abc123...",
"allowed_scopes": ["microservice:internal", "my_service:execute"]
}Important: Store the client_secret securely in your secrets manager. It's only shown once.
Define which endpoints your service can call:
curl -X POST https://api.aframp.com/admin/services/allowlist/add \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <admin-token>" \
-d '{
"calling_service": "my_service",
"target_endpoint": "/api/settlement/*",
"allowed": true
}'Add to your service's main.rs or initialization code:
use aframp_backend::service_auth::{ServiceTokenManager, TokenRefreshConfig};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load configuration
let service_name = std::env::var("SERVICE_NAME")?;
let client_id = std::env::var("SERVICE_CLIENT_ID")?;
let client_secret = std::env::var("SERVICE_CLIENT_SECRET")?;
let token_endpoint = std::env::var("OAUTH_TOKEN_ENDPOINT")?;
// Create token manager
let token_manager = Arc::new(ServiceTokenManager::new(
service_name.clone(),
client_id,
client_secret,
token_endpoint,
TokenRefreshConfig::default(),
));
// Initialize and acquire first token
token_manager.initialize().await?;
// Start background refresh task
token_manager.clone().start_refresh_task();
// Use token_manager in your HTTP client
// ...
Ok(())
}Use the ServiceHttpClient for all internal API calls:
use aframp_backend::service_auth::ServiceHttpClient;
use reqwest::Method;
async fn call_settlement_service(
client: &ServiceHttpClient,
) -> Result<(), Box<dyn std::error::Error>> {
let request = reqwest::Request::new(
Method::POST,
"https://api.aframp.com/api/settlement/process".parse()?,
);
let response = client.execute(request).await?;
if response.status().is_success() {
println!("Settlement processed successfully");
}
Ok(())
}Add verification middleware to your service's internal endpoints:
use axum::{Router, routing::post, middleware};
use aframp_backend::service_auth::{service_token_verification, ServiceAuthState};
use std::sync::Arc;
async fn build_router(
pool: Arc<sqlx::PgPool>,
allowlist: Arc<ServiceAllowlist>,
jwt_secret: String,
) -> Router {
let auth_state = ServiceAuthState {
pool,
allowlist,
jwt_secret,
};
Router::new()
.route("/api/internal/process", post(process_handler))
.route("/api/internal/verify", post(verify_handler))
.layer(middleware::from_fn_with_state(
auth_state,
service_token_verification,
))
}Add these to your service's environment:
# Service identity
SERVICE_NAME=my_service
SERVICE_CLIENT_ID=service_my_service
SERVICE_CLIENT_SECRET=svc_secret_abc123...
# OAuth configuration
OAUTH_TOKEN_ENDPOINT=https://api.aframp.com/oauth/token
# JWT verification
JWT_SECRET=your-jwt-secret
# Database
DATABASE_URL=postgres://user:pass@localhost/aframp
# Redis
REDIS_URL=redis://localhost:6379Test your service authentication:
# Check service registration
curl https://api.aframp.com/admin/services/my_service \
-H "Authorization: Bearer <admin-token>"
# Check allowlist
curl https://api.aframp.com/admin/services/allowlist/my_service \
-H "Authorization: Bearer <admin-token>"
# Monitor metrics
curl https://api.aframp.com/metrics | grep service_tokenSymptom: Service fails to start with "Token acquisition failed"
Solutions:
- Verify
SERVICE_CLIENT_IDandSERVICE_CLIENT_SECRETare correct - Check
OAUTH_TOKEN_ENDPOINTis accessible - Ensure service is registered in the database
- Check network connectivity to OAuth server
Symptom: Service calls return 401 even after token refresh
Solutions:
- Verify JWT_SECRET matches between services
- Check token hasn't expired (should auto-refresh)
- Verify
microservice:internalscope is present - Check service name in
X-Service-Nameheader matches token subject
Symptom: Service calls return 403 with "SERVICE_NOT_AUTHORIZED"
Solutions:
- Check allowlist configuration for your service
- Verify endpoint pattern matches (wildcards must be exact)
- Ensure allowlist entry has
allowed: true - Check cache invalidation (may take up to 5 minutes)
Symptom: Logs show "Service impersonation attempt detected"
Solutions:
- Verify
X-Service-Nameheader matches token subject - Check you're not reusing tokens from another service
- Ensure token manager is initialized with correct service name
# Token acquisition rate
rate(aframp_service_token_acquisitions_total{service_name="my_service"}[5m])
# Token refresh failures (should be 0)
aframp_service_token_refresh_failures_total{service_name="my_service"}
# Authentication success rate
rate(aframp_service_call_authentications_total{calling_service="my_service",result="success"}[5m])
# Authorization denials
rate(aframp_service_call_authorization_denials_total{calling_service="my_service"}[5m])
-
Token Refresh Failure
alert: ServiceTokenRefreshFailure expr: aframp_service_token_refresh_failures_total > 0 for: 1m severity: critical
-
High Authorization Denial Rate
alert: HighServiceAuthDenialRate expr: rate(aframp_service_call_authorization_denials_total[5m]) > 0.05 for: 5m severity: warning
-
Secret Management
- Never commit client secrets to version control
- Use secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)
- Rotate secrets regularly (every 90 days)
-
Token Lifecycle
- Let the token manager handle all token operations
- Don't manually refresh tokens
- Don't cache tokens outside the token manager
-
Error Handling
- Always handle token refresh failures gracefully
- Implement circuit breakers for downstream services
- Log all authentication failures for audit
-
Allowlist Management
- Use wildcard patterns for endpoint groups
- Review allowlist quarterly
- Remove unused permissions promptly
-
Monitoring
- Set up alerts for token refresh failures
- Monitor authentication success rates
- Track authorization denials
- Review MICROSERVICE_AUTH_IMPLEMENTATION.md for detailed architecture
- Check src/service_auth/README.md for API documentation
- Run integration tests:
cargo test --test service_auth_test --features database -- --ignored - Configure mTLS for highest sensitivity endpoints
For issues or questions:
- Check logs for detailed error messages
- Review Prometheus metrics
- Consult the audit log in
service_auth_audittable - Contact the platform team