...
┌─────────────────────────────────────────────────────────────────┐
│ SERVICE-TO-SERVICE AUTHENTICATION FLOW │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────┐
│ ApiService │
│ (Needs to call │
│ Integration) │
└────────┬─────────┘
│
│ Step 1: Make API call
│ (e.g., send XML message)
▼
┌──────────────────────────┐
│ ServiceTokenHandler │
│ (Middleware) │
└────────┬─────────────────┘
│
│ Step 2: Need token first!
│ Request token from AzureEntra ADID
│ Sends:
│ - Client ID (stored as env. variable in container app)
│ - Client Secret (stored as env. variable in container app)
│ - Scope
▼
┌──────────────────────────────┐
│ Microsoft Entra ID │
│ Token Endpoint │
└────────┬─────────────────────┘
│
│ Step 3: Entra ID validates
│ ✓ Client ID exists
│ ✓ Client secret matches
│ ✓ Service has permission
▼
┌──────────────────────────────┐
│ Entra ID Returns Token │
│ (for IntegrationServiceAPI) │
└────────┬─────────────────────┘
│
│ Step 4: Token attached to request
│ Authorization: Bearer eyJ...
▼
┌──────────────────────────────┐
│ IntegrationServiceAPI │
│ Validates Token │
└────────┬─────────────────────┘
│
│ Step 5: Token validation
│ ✓ Signature valid
│ ✓ Issuer correct
│ ✓ Audience correct
│ ✓ Not expired
▼
┌──────────────────────────────┐
│ ✅ Process Request │
│ Execute API logic │
│ Return Response │
└──────────────────────────────┘
...
- ServiceTokenHandler checks if it has a valid cached token
- If no valid token, requests new one from Azure ADEntra ID
- Sends client credentials to Azure AD Entra ID token endpoint
Who's involved:
- ServiceTokenHandler
- Microsoft Entra ID
What's sent to Azure ADEntra ID:
- Client ID: ApiService's application ID (stored as env variable in container app)
- Client Secret: ApiService's secret key (stored securelyas env variable in container app)
- Scope:
.default(all permissions the app has) - Grant Type:
client_credentials
...
- Request sent to Azure AD for authentication
...
Step 3:
...
Entra ID Validates Client Credentials
What happens:
- Azure AD Entra ID receives the token request
- Validates the client ID exists in the tenant
- Validates the client secret matches what's registered
- Checks if the app has permission to access IntegrationServiceAPI
...
- If all valid → Proceed to Step 4
- If any invalid → Return error (401 Unauthorized)
...
Step 4:
...
Entra ID Issues Access Token
What happens:
- Azure AD Entra ID generates an access token
- Token valid for 1 hour
- Token contains app identity (not user identity)
- Token returned to ServiceTokenHandler
...
- Signature: Proves token came from Azure ADEntra ID
- Issuer: Confirms it's from the correct Azure AD Entra ID tenant
- Audience: Ensures it's for IntegrationServiceAPI
- Expiration: Checks it hasn't expired (1-hour lifetime)
- App Permissions: Verifies ApiService has permission
...
- No cached token available
- Request token from Azure AD Entra ID (takes ~100-200ms)
- Cache token for 1 hour
- Use token for request
...
- Cached token expired
- Request new token from Azure ADEntra ID
- Update cache with new token
Benefits:
...
- token
...
Security Details
Client Secret
What it is:
- A secret key that proves ApiService's identity
- Like a password for the application (not a user)
- Created in Azure AD app registration
- Must be kept secure
Where it's stored:
- Development: User secrets or
secrets.json - Production: Use environment variables (or in Key Vault)
- Never: Committed to source control
How it's used:
- ServiceTokenHandler reads it from configuration
- Sends it to Azure AD with client ID
- Azure AD validates it matches the registered secret
- If valid, token is issued
Security best practices:
- ✅ Use environment variables (or in Key Vault)
- ✅ Rotate periodically (every 6-12 months)
- ❌ Never commit to git
- ❌ Never hardcode in source files
Token Validation
How IntegrationServiceAPI validates tokens:
Signature Validation
- Uses Azure AD's public keys
- Proves token was issued by Azure AD
- Prevents forged tokens
Issuer Validation
- Checks token came from correct Azure AD tenant
- Prevents tokens from other organizations
Audience Validation
- Ensures token is for IntegrationServiceAPI
- Prevents token reuse across different APIs
Expiration Validation
- Checks token hasn't expired (1 hour)
- Includes 5-minute clock skew tolerance
Permission Validation
- Checks ApiService has required permissions
- Based on Azure AD app role assignments
...
Configuration
ApiService Configuration
...
Where configured:
- Environment variables (recommendedin apiservice container app)
appsettings.json(development only, with secrets.json)
...