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:

  1. AzureAdScheme - For Azure AD user tokens (Swagger)
  2. CiamScheme - For CIAM user tokens (not currently used)
  3. 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:

  1. Token parsed and identified as static token (issuer + service_name claim)
  2. Request path checked against allowed endpoints
  3. If path matches → Route to StaticTokenScheme
  4. 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 IncomingMessageRequest with 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
  • No labels