You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

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:

DirectionClient ID (Who's calling)Client Secret (Who's calling)Audience (Who's being called)
ApiService → IntegrationApiService IDApiService secretIntegrationServiceAPI ID
Integration → ApiServiceIntegrationServiceAPI IDIntegrationServiceAPI secretApiService 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)

Step 4: Azure AD Issues Access Token

What happens:

  • Azure AD generates an access token
  • Token valid for 1 hour
  • Token contains app identity (not user identity)
  • Token returned to ServiceTokenHandler

Who's involved:

  • Microsoft Entra ID
  • ServiceTokenHandler

What's in the token:

  • Issuer: Microsoft Entra ID
  • Audience: IntegrationServiceAPI (who the token is for)
  • App Identity: ApiService's application ID
  • Permissions: What ApiService can do
  • Expiration: 1 hour from now
  • Signature: Cryptographic proof of authenticity

Result:

  • ServiceTokenHandler receives access token
  • Token cached for future requests (1 hour)

Step 5: Token Attached to Request

What happens:

  • ServiceTokenHandler attaches token to the original request
  • Token added as Authorization header: Bearer {token}
  • Request continues to IntegrationServiceAPI

Who's involved:

  • ServiceTokenHandler
  • ApiService

Result:

  • Request sent to IntegrationServiceAPI with authentication proof

Step 6: IntegrationServiceAPI Validates Token

What happens:

  • IntegrationServiceAPI receives request with token
  • Authentication middleware examines the token
  • Validates token is authentic and valid (using cached Azure AD public keys - no Azure AD call needed)

Who's involved:

  • IntegrationServiceAPI
  • JWT Authentication Middleware
  • Azure AD public keys (cached locally, refreshed every 30 minutes)

What's validated:

  • Signature: Proves token came from Azure AD
  • Issuer: Confirms it's from the correct Azure AD tenant
  • Audience: Ensures it's for IntegrationServiceAPI
  • Expiration: Checks it hasn't expired (1-hour lifetime)
  • App Permissions: Verifies ApiService has permission

Result:

  • ✅ Valid token → Request proceeds to controller
  • ❌ Invalid token → Return 401 Unauthorized

Step 7: API Processes Request

What happens:

  • Request validated successfully
  • IntegrationServiceAPI processes the request
  • Executes the requested operation
  • Returns response to ApiService

Who's involved:

  • IntegrationServiceAPI
  • ApiService

Result:

  • Operation completed
  • Response returned to ApiService
  • ApiService continues its workflow

Token Caching & Reuse

Token Caching

ServiceTokenHandler caches tokens to improve performance:

First request:

  1. No cached token available
  2. Request token from Azure AD (takes ~100-200ms)
  3. Cache token for 1 hour
  4. Use token for request

Subsequent requests (within 1 hour):

  1. Check cache for valid token
  2. Use cached token (takes ~1-5ms)
  3. No Azure AD call needed

After 1 hour:

  1. Cached token expired
  2. Request new token from Azure AD
  3. Update cache with new token

Benefits:

  • ⚡ Fast (no Azure AD call for most requests)
  • 📈 Scalable (no rate limits on cached tokens)
  • 🔒 Secure (token automatically refreshed)

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 (not in git)
  • Production: Azure Key Vault or environment variables
  • 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:

  • ✅ Store in Azure Key Vault (production)
  • ✅ Use environment variables
  • ✅ Rotate periodically (every 6-12 months)
  • ❌ Never commit to git
  • ❌ Never hardcode in source files
  • No labels