Step-by-Step Process & Entities
Entities Involved
1. ApiService
- The core backend API
- Location:
Source/EGU.PartnerPortal.ApiService - Calls IntegrationServiceAPI for external system integrations
- Validates tokens when IntegrationServiceAPI calls it
2. ServiceTokenHandler (Middleware)
- HTTP message handler that intercepts outgoing requests
- Location:
Source/EGU.PartnerPortal.ApiService/Middleware/Handler/ServiceTokenHandler.cs - Automatically gets tokens from Azure AD
- Attaches tokens to requests going to IntegrationServiceAPI
3. Microsoft Entra ID (Azure AD Tenant)
- Microsoft's cloud identity service
- Tenant ID:
6073ce8b-73f3-4df4-9b80-5e40cdc6965f - Issues tokens for service-to-service communication
- Validates client credentials (client ID + client secret)
4. IntegrationServiceAPI
- External system integration API
- Location:
Source/EGU.PartnerPortal.IntegrationServiceAPI - Calls ApiService to create/update work orders and instructions
- Validates tokens when ApiService calls it
High-Level Authentication Flow
┌─────────────────────────────────────────────────────────────────┐
│ 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 Azure AD
│ Sends:
│ - Client ID
│ - Client Secret
│ - Scope
▼
┌──────────────────────────────┐
│ Microsoft Entra ID │
│ Token Endpoint │
└────────┬─────────────────────┘
│
│ Step 3: Azure AD validates
│ ✓ Client ID exists
│ ✓ Client secret matches
│ ✓ Service has permission
▼
┌──────────────────────────────┐
│ Azure AD 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 │
└──────────────────────────────┘
Bidirectional Communication
Both Services Call Each Other
The authentication works in both directions:
Direction 1: ApiService → IntegrationServiceAPI
- ApiService sends work orders, XML messages, completion notifications
- Uses ApiService's client credentials (ID + secret)
- Gets token for IntegrationServiceAPI audience
- Token proves "I am ApiService and I can call IntegrationServiceAPI"
Direction 2: IntegrationServiceAPI → ApiService
- IntegrationServiceAPI creates/updates work orders, instructions
- Uses IntegrationServiceAPI's client credentials (ID + secret)
- Gets token for ApiService audience
- Token proves "I am IntegrationServiceAPI and I can call ApiService"
Same Process, Different Credentials
The authentication flow is identical in both directions:
- Same steps (1-7 below)
- Same token lifetime (1 hour)
- Same caching mechanism
- Same validation process
Only the credentials differ:
| Direction | Client ID (Who's calling) | Client Secret (Who's calling) | Audience (Who's being called) |
|---|---|---|---|
| ApiService → Integration | ApiService ID | ApiService secret | IntegrationServiceAPI ID |
| Integration → ApiService | IntegrationServiceAPI ID | IntegrationServiceAPI secret | ApiService ID |
Why Bidirectional?
ApiService calls IntegrationServiceAPI when:
- Sending work order to external system
- Sending completion notification
- Sending cancellation notice
IntegrationServiceAPI calls ApiService when:
- Receiving work order from external system
- Receiving instruction from external system
- Updating work order status
Each service authenticates itself when making the call, proving its identity to the other service.
Step-by-Step Authentication Process
Note: The steps below show ApiService calling IntegrationServiceAPI, but the process is identical in reverse (IntegrationServiceAPI calling ApiService) - just swap the service names and credentials.
Step 1: ApiService Needs to Call IntegrationServiceAPI
What happens:
- ApiService needs to send data to IntegrationServiceAPI
- Example: Sending an XML message to external system
- Makes HTTP request to IntegrationServiceAPI endpoint
Who's involved:
- ApiService
- ServiceTokenHandler (automatically intercepts)
Result:
- Request intercepted by ServiceTokenHandler
- Handler recognizes authentication is needed
Step 2: ServiceTokenHandler Requests Token
What happens:
- ServiceTokenHandler checks if it has a valid cached token
- If no valid token, requests new one from Azure AD
- Sends client credentials to Azure AD token endpoint
Who's involved:
- ServiceTokenHandler
- Microsoft Entra ID
What's sent to Azure AD:
- Client ID: ApiService's application ID
- Client Secret: ApiService's secret key (stored securely)
- Scope:
.default(all permissions the app has) - Grant Type:
client_credentials
Result:
- Request sent to Azure AD for authentication
Step 3: Azure AD Validates Client Credentials
What happens:
- Azure AD 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
Who's involved:
- Microsoft Entra ID
What Azure AD checks:
- ✅ Does this client ID exist?
- ✅ Does the client secret match?
- ✅ Does this app have permission to call IntegrationServiceAPI?
Result:
- If all valid → Proceed to Step 4
- If any invalid → Return error (401 Unauthorized)