Skip to content

Authentication Flow

End-to-end authentication and authorisation flow for the Microtec ERP platform.

Protocol: OpenID Connect (OIDC) / OAuth2
Identity Provider: Keycloak 22 with custom SPIs
Realms: microtec (ERP users) · businessowner (tenant admins)


Overview

Microtec ERP uses a dual-token architecture:

  1. Keycloak JWT — issued by Keycloak, validates the user's identity and Keycloak-level roles
  2. ERP Access Context Token — generated by the ERP backend after validating the Keycloak JWT; contains ERP-specific claims (tenant ID, permissions, subscription features)

The Gateway validates only the Keycloak JWT. Individual microservices exchange it for (or validate against) the ERP Access Context Token stored in Redis.


Authentication Sequence — Browser Login


Token Refresh Flow


Logout Flow

[INFO] Backchannel logout is pre-configured by the keycloak-seeding SPI. Each service's logout URL is set to {service-internal-url}/auth/logout.


ERP Access Context Token

The ERP Access Context Token is not a JWT — it is a Redis-stored session object serialised as JSON. It contains:

json
{
  "userId": "uuid",
  "tenantId": "uuid",
  "tenantSlug": "my-company",
  "databaseName": "Microtec_MyCompany",
  "subscriptionTier": "Enterprise",
  "enabledModules": ["Accounting", "Inventory", "HR"],
  "permissions": [
    "invoices:read",
    "invoices:write",
    "reports:export"
  ],
  "features": {
    "maxUsers": 500,
    "zatcaEnabled": true,
    "multiWarehouse": true
  },
  "expiresAt": "2026-05-30T12:00:00Z"
}

This context is resolved per-request by ITenantContextManager and is used to:

  • Route EF Core queries to the correct tenant database
  • Enforce feature-level authorisation ([RequireFeature("zatcaEnabled")])
  • Apply row-level security via EF global query filters

Authentication Flow — Keycloak Custom SPIs

Flow priority correction (keycloak-seeding SPI)

The seeding SPI sets flow priorities on every Keycloak startup:

csharp
// KeycloakProviders/src/...
var flowPriorities = new Dictionary<string, int>
{
    { "mac-auth-flow",     10 },
    { "cookie-auth-flow",  20 },
    { "idp-redirect-flow", 30 },
    { "forms-auth-flow",   40 }
};

[WARNING] If priorities are wrong (e.g., due to a Keycloak upgrade resetting them), restart Keycloak — the seeding SPI restores correct priorities on startup.


Multi-Realm Architecture


Token Claims Reference

Keycloak JWT (access token) — key claims

ClaimDescriptionExample
subUser UUID3bb564df-0f24-4ea6-...
preferred_usernameUsernameahmed@company.com
realm_access.rolesKeycloak realm roles["erp-user", "accounting-admin"]
resource_accessClient-specific roles{"apps-portal": {"roles": ["user"]}}
tenant_idCustom claim — tenant UUIDabc123
expToken expiry (Unix timestamp)1748649600
issIssuerhttps://domain/auth/realms/microtec

Custom claim tenant_id

Injected by the Keycloak seeding SPI via a custom protocol mapper. The backend uses this claim to resolve the tenant context without an extra database lookup.


Security Considerations

  • Token storage: Keycloak-js stores tokens in memory only — never in localStorage or sessionStorage (XSS protection)
  • PKCE: Enabled for all public OIDC clients (code flow with PKCE, no client secret in browser)
  • Token lifetime: Access token = 5 minutes; Refresh token = 30 minutes (configurable per realm)
  • Brute force protection: Enabled in Keycloak realm settings — 5 failed attempts → 15-minute lockout
  • mTLS on Private CAE: All service-to-service traffic inside the private CAE is encrypted and mutually authenticated

Internal Documentation — Microtec Platform Team