👱 Zero Trust Principle: Assume Breach — the API Gateway is the single enforcement perimeter. It validates every token cryptographically, normalizes claims from both Okta and Entra into a common header schema, and backends never see raw IdP tokens.

Architecture: What You Will Build

Your App (localhost:3000)
    │
    │  Bearer JWT from Okta OR Entra
    │
    ▼
Azure API Management (cloud)
    │
    ├─ 1. Strip X-User-* headers (prevent spoofing)
    ├─ 2. Detect issuer (Okta or Entra)
    ├─ 3a. Validate Okta JWT via Okta JWKS
    │      Map groups[] → X-App-Roles
    ├─ 3b. Validate Entra JWT via Entra JWKS
    │      Map roles[] → X-App-Roles
    ├─ 4. Inject: X-User-Id, X-User-Email, X-App-Roles, X-Idp-Source
    └─ 5. DELETE Authorization header
    │
    ▼
Express API Server (localhost:4000)
    Reads only X-App-Roles — never sees raw token

Part A — Create Azure APIM Instance

Step 1: Log in to Azure Portal

Go to: https://portal.azure.com

Step 2: Create a Resource Group

  1. In the top search bar, type Resource groups
  2. Click + Create
  3. Fill in details and click Review + create → Create

Step 3: Search for API Management

  1. In the top search bar, type API Management
  2. Click API Management services
  3. Click + Create

Step 4: Fill in the APIM creation form

Field Value
Resource name authmatrix-apim (must be globally unique)
Organization name AuthMatrix Lab
Pricing tier Developer or Consumption

Click Review + create → Create.

⚠️ Cost warning: Developer tier costs approximately $50/month. Use the Consumption tier for a pay-per-call model.

Step 5: Wait for deployment to complete

When you get the notification "Deployment succeeded", click Go to resource.


Part B — Create the API Definition in APIM

Step 6: Create a new API

  1. In your APIM instance left sidebar, click APIs
  2. Click + Add API
  3. Select HTTP (manual definition)
  4. Fill in the details:
    • Display name: AuthMatrix Resource Server
    • Web service URL: http://host.docker.internal:4000 or your ngrok URL
    • API URL suffix: api

Click Create.

Step 7: Add API operations

Click + Add operation for each endpoint:

Operation Method URL Template Display Name
1 GET /protected/me Get My Identity
2 GET /protected/reports Get Reports
3 POST /protected/reports Create Report
4 GET /protected/users List Users
5 POST /protected/users Create User
6 GET /protected/audit Get Audit Log
7 DELETE /protected/audit Purge Audit Log
8 PUT /protected/settings Update Settings

For each: fill in Display name and URL, then click Save.


Part C — Apply the Dual-IdP Inbound Policy

Step 8: Open the All Operations policy editor

  1. In your API, click All operations in the left list
  2. Click the Policies tab
  3. Click Policy code editor

Step 9: Paste the dual-IdP policy

Open the file at config/azure-apim-policy.xml in this repo. Paste its full content into the APIM policy editor.

Step 10: Customize the policy for your tenants

In the policy XML, replace the placeholder values (e.g., your Okta domain, your Azure Tenant ID) and click Save.


Part D — Update the Express API to Trust Gateway Headers

Step 11: Add gateway header mode to the auth middleware

Add this new endpoint to apps/api-server/src/index.ts:

// —— APIM GATEWAY MODE: Read pre-validated headers from APIM ——————————————————
app.get('/api/gateway/me', (req, res) => {
  const userId  = req.headers['x-user-id'];
  const email   = req.headers['x-user-email'];
  const roles   = (req.headers['x-app-roles'] as string || '').split(',').filter(Boolean);
  const idpSrc  = req.headers['x-idp-source'];

  if (!userId) {
    return res.status(401).json({ error: 'Missing X-User-Id header — request must come via APIM' });
  }

  res.json({
    message:  '✅ Identity verified via Azure APIM gateway',
    identity: { userId, email, roles, idpSource: idpSrc },
  });
});

Step 12: Restart the API server

npm run dev --workspace=apps/api-server

Part E — Test the APIM Gateway Flow

Step 13: Get a real token from your IdP

Use the AuthMatrix dashboard to sign in with Okta or Entra. Get the access token.

Step 14: Test APIM using curl

curl -H "Authorization: Bearer YOUR_OKTA_OR_ENTRA_TOKEN" \
     https://authmatrix-apim.azure-api.net/api/protected/me

Step 15: Test Okta token then Entra token through the SAME APIM gateway

  1. Get an Okta access token → send to APIM → observe X-Idp-Source: okta
  2. Get an Entra access token → send to APIM → observe X-Idp-Source: entra

The backend API receives identical X-App-Roles: Admin regardless of which IdP authenticated the user. The backend is fully IdP-agnostic.

Step 16: Test unauthorized access

curl https://authmatrix-apim.azure-api.net/api/protected/me
# Should get 401 from APIM

Part F — APIM Policy Debugging

Step 17: Enable APIM tracing

  1. In the Azure Portal → your APIM → APIs → AuthMatrix Resource Server
  2. Click Test tab
  3. Select GET /protected/me operation
  4. Add Authorization header and click Trace

✅ Lab 4 Complete — Validation Checklist

  • APIM instance created and deployed
  • API operations defined for all AuthMatrix endpoints
  • Dual-IdP policy XML applied from config/azure-apim-policy.xml
  • Policy customized with your Okta domain and Entra Tenant ID
  • Express API server reachable from APIM (via ngrok or App Service)
  • Okta token sent through APIM → 200 response
  • Entra token sent through APIM → 200 response
  • Tampered token → 401 from APIM