Overview
The Partner Portal's IntegrationServiceAPI receives incoming work order messages from a legacy WCF (Windows Communication Foundation) service. This service uses a pre-configured static JWT token for authentication instead of dynamic Azure AD tokens.
Key Characteristics:
- Static JWT token (never expires, same token always used)
- Signed with symmetric key (HS256 algorithm)
- Restricted to specific endpoint only
- Designed for legacy system integration
...
Entities Involved
1. WCF Service (External Legacy System)
- Windows Communication Foundation service
- Sends work order XML messages to IntegrationServiceAPI
- Uses pre-configured static JWT token
- Token stored in config.json (use App Service Editor to view config.json )
2. IntegrationServiceAPI
- Receives incoming work order messages
- Validates static JWT tokens
- Location:
Source/EGU.PartnerPortal.IntegrationServiceAPI - Processes XML messages and creates/updates work orders in ApiService
3. Authentication Middleware
- Multi-scheme authentication handler
- Routes static tokens to StaticTokenScheme validator
- Location:
Source/EGU.PartnerPortal.IntegrationServiceAPI/Services/Middleware/AuthenticationConfiguration.cs - Enforces endpoint path restrictions
4. IncomingTrafficController
- Handles WCF incoming messages
- Location:
Source/EGU.PartnerPortal.IntegrationServiceAPI/Controller/IncomingTrafficController.cs - Endpoint:
/api/ReceiveWCFIncomingMessage - Processes XML data and maps to internal DTOs
...
Authentication Flow
┌─────────────────────────────────────────────────────────────────┐
│ WCF SERVICE STATIC TOKEN AUTHENTICATION │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────────────────┐
│ WCF Service │
│ (External legacy system) │
└──────┬───────────────────────┘
│
│ Has static JWT token
│ configured in settings
│
│ Step 1: Prepare message
│ - ClientID
│ - XML Data (byte[])
│ - MessageType
▼
┌──────────────────────────────────┐
│ HTTP Request │
│ POST /api/ReceiveWCFIncoming... │
│ Authorization: Bearer eyJ0eX... │
│ Body: IncomingMessageRequest │
└──────┬───────────────────────────┘
│
│ Step 2: Request arrives
▼
┌──────────────────────────────────┐
│ IntegrationServiceAPI │
│ Multi-Scheme Authenticator │
└──────┬───────────────────────────┘
│
│ Step 3: Read token & route
│ Parse JWT token:
│ - Issuer: "PartnerPortal"?
│ - Claim: service_name="WCFservice"?
│ - Path: /api/ReceiveWCFIncoming...?
│
├─── All Valid ───┐ Invalid ────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Route to │ │ Validate │ │ 401 │
│ StaticToken │ │ Token │ │ Unauthorized│
│ Scheme │ │ │ │ │
└──────────────┘ └──────┬───────┘ └──────────────┘
│
│ Step 4: Token validation
│ - Signature (HS256 symmetric key)
│ - Issuer = "PartnerPortal"
│ - Audience = "EGU.PartnerPortal"
│ - Lifetime (if expiration set)
│
├─── Valid ────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Forward to │ │ 200 OK │
│ Controller │ │ │
└──────┬───────┘ └──────────────┘
│
│ Step 5: Process message
▼
┌──────────────────────┐
│ IncomingTraffic │
│ Controller │
└──────┬───────────────┘
│
│ Step 6: Decode & validate XML
│ Step 7: Call ApiService
│ Step 8: Return response
▼
┌──────────────────────┐
│ Success response │
│ sent to WCF │
└──────────────────────┘
...
Static JWT Token
Token Format
The static JWT token is a standard JSON Web Token with three parts:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload (Claims):
{
"iss": "PartnerPortal",
"aud": "EGU.PartnerPortal",
"service_name": "WCFservice",
"exp": 1893456000,
"iat": 1733456000
}
Signature:
- Signed using HS256 (HMAC-SHA256) algorithm
- Uses symmetric secret key shared between WCF and IntegrationServiceAPI
Token Generation (WCF Side)
The WCF service must generate the JWT token using the shared secret key:
// Pseudocode - WCF service implementation
var secretKey = "<base64-encoded-secret-key>";
var signingKey = new SymmetricSecurityKey(Convert.FromBase64String(secretKey));
var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim("iss", "PartnerPortal"),
new Claim("aud", "EGU.PartnerPortal"),
new Claim("service_name", "WCFservice")
};
var token = new JwtSecurityToken(
issuer: "PartnerPortal",
audience: "EGU.PartnerPortal",
claims: claims,
expires: DateTime.UtcNow.AddYears(10), // Long-lived token
signingCredentials: credentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
// Store tokenString in Azure environment variable
...
IntegrationServiceAPI Configuration
Required Settings
File: appsettings.json
{
"AccessToken": {
"SecretKey": "<base64-encoded-secret-key>"
}
}
Secret Key:
- Base64-encoded symmetric key
- Must match the key used by WCF service to generate tokens
- Used to verify token signatures
- Must be kept secure and never exposed
Authentication Configuration
Location: IntegrationServiceAPI/Services/Middleware/AuthenticationConfiguration.cs:148-177
The middleware configures three authentication schemes:
- AzureAdScheme - For Azure AD user tokens (Swagger)
- CiamScheme - For CIAM user tokens (not currently used)
- StaticTokenScheme - For WCF service static tokens
StaticTokenScheme Configuration:
.AddJwtBearer(AuthenticationConstants.StaticTokenScheme, opts =>
{
var secretKey = configuration["AccessToken:SecretKey"]?.Trim().TrimEnd(',');
var signingKey = new SymmetricSecurityKey(Convert.FromBase64String(secretKey));
opts.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "PartnerPortal",
ValidateAudience = true,
ValidAudience = "EGU.PartnerPortal",
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
...
Endpoint Restrictions
Allowed Endpoint
Static tokens are ONLY valid for this endpoint:
/api/ReceiveWCFIncomingMessage
Enforcement Logic:
private static readonly string[] AllowedWcfEndpoints = new[]
{
"/api/ReceiveWCFIncomingMessage"
};
private static bool IsAllowedWcfEndpoint(PathString requestPath)
{
return AllowedWcfEndpoints.Any(endpoint =>
requestPath.StartsWithSegments(endpoint, StringComparison.OrdinalIgnoreCase));
}
Path Validation Flow:
- Token parsed and identified as static token (issuer + service_name claim)
- Request path checked against allowed endpoints
- If path matches → Route to StaticTokenScheme
- If path doesn't match → Reject with 401 Unauthorized
Security Benefit:
- Prevents static token misuse on other API endpoints
- Limits attack surface if token is compromised
- Ensures token only works for its intended purpose
...
Step-by-Step Authentication Process
Step 1: WCF Service Prepares Request
What happens:
- WCF service has incoming work order message to send
- Retrieves pre-configured static JWT token from config.json stored in Azure
- Prepares
IncomingMessageRequestwith XML data - Attaches token to Authorization header
Who's involved:
- WCF Service
Result:
- HTTP POST request ready with Bearer token
...
Step 2: Request Arrives at IntegrationServiceAPI
What happens:
- HTTP request received by IntegrationServiceAPI
- Multi-scheme authentication middleware intercepts request
- Reads Authorization header and extracts JWT token
Who's involved:
- IntegrationServiceAPI
- Authentication Middleware
Result:
- Token extracted for routing decision
...
Step 3: Token Routing Decision
What happens:
- Middleware parses JWT token (without validating yet)
- Checks issuer claim: Is it "PartnerPortal"?
- Checks service_name claim: Is it "WCFservice"?
- Checks request path: Is it
/api/ReceiveWCFIncomingMessage?
Who's involved:
- Multi-Scheme Authentication Handler
Result:
- If all conditions match → Route to StaticTokenScheme
- Otherwise → Route to AzureAdScheme (which will fail)
...
Step 4: Token Validation
What happens:
- StaticTokenScheme validator receives token
- Validates signature using symmetric secret key
- Validates issuer = "PartnerPortal"
- Validates audience = "EGU.PartnerPortal"
- Validates lifetime (if expiration set)
- Validates all required claims present
Who's involved:
- StaticTokenScheme Validator
- Secret Key (from appsettings.json)
What's validated:
- Signature: Token signed with correct secret key
- Issuer: Exactly "PartnerPortal"
- Audience: Exactly "EGU.PartnerPortal"
- Lifetime: Token not expired (5-minute clock skew allowed)
- Claims: service_name claim present
Result:
- Valid token → Request proceeds to controller
- Invalid token → 401 Unauthorized response
...
Step 5: Controller Processes Message
What happens:
- Authentication successful
- Request forwarded to
IncomingTrafficController.ReceiveWCFIncomingMessage - Controller processes the incoming message
- Message data processed and forwarded to ApiService
Who's involved:
- IncomingTrafficController
- IncomingTrafficService
Result:
- Authenticated request processed
- Calls ApiService to create/update work order
...
Step 6: Response Returned
What happens:
- Processing result returned to WCF service
- Success or error message
- HTTP 200 OK with JSON response
Who's involved:
- IntegrationServiceAPI
- WCF Service
Result:
- WCF service receives confirmation
- Transaction complete