Architecture Deployment API

Authentication & Authorisation Flows

Keycloak OIDC Login (Production)

sequenceDiagram
  participant Client as Mobile App / Portal
  participant KC as Keycloak :8090
  participant API as REST API
  participant MW as TenantResolutionMiddleware
  participant Redis as Redis (Blacklist)

  Client->>KC: POST /realms/flowershop/protocol/openid-connect/token
  Note over Client,KC: grant_type=password or PKCE code exchange
  KC-->>Client: access_token (JWT), refresh_token

  Client->>API: GET /api/v1/... Authorization: Bearer {token}
  API->>Redis: TokenBlacklistMiddleware.IsRevoked(jti)?
  Redis-->>API: false
  API->>KC: Validate token signature (JWKS)
  KC-->>API: Valid
  API->>MW: TenantResolutionMiddleware
  MW->>MW: Extract claims: sub, role, vendor_id, vendor_type, customer_id
  MW->>MW: ITenantContextService.Set[Vendor|Customer|Admin]Context()
  MW-->>API: Tenant context set
  API-->>Client: 200 OK

  note over Client,Redis: Error path: token revoked → 401 Unauthorized
  note over Client,Redis: Error path: tenant claims missing → 403 Forbidden

Dev Token (Development Only)

sequenceDiagram
  participant Dev as Developer
  participant API as REST API (Authentication__UseKeycloak=false)

  Dev->>API: POST /api/v1/auth/dev-token {role, vendorId, customerId}
  API->>API: Generate JWT locally (no Keycloak)
  API-->>Dev: access_token (JWT with claims)

  Dev->>API: Any endpoint with Bearer token
  API->>API: TenantResolutionMiddleware reads local JWT claims
  API-->>Dev: 200 OK

Logout + Token Blacklist

sequenceDiagram
  participant Client
  participant API
  participant Redis as Redis (Blacklist)
  participant KC as Keycloak

  Client->>API: POST /api/v1/auth/logout Authorization: Bearer {token}
  API->>Redis: RedisTokenBlacklistService.RevokeAsync(jti, expiry)
  API-->>Client: 204 No Content
  note over API,Redis: Keycloak session is not terminated — only the JWT token is blacklisted

  Client->>API: GET /api/v1/... (same token)
  API->>Redis: IsRevoked(jti)?
  Redis-->>API: true
  API-->>Client: 401 Unauthorized

Multi-Tenant Context Resolution

flowchart TD
  A[Request arrives] --> B[TokenBlacklistMiddleware]
  B -->|jti revoked| Z1[401 Unauthorized]
  B -->|valid| C[TenantResolutionMiddleware]
  C --> D{role claim?}
  D -->|VendorAdmin| E[SetTenantContext vendor_id + vendor_type + user_id]
  D -->|User| F[No vendor claims — falls through to header fallback or 403]
  D -->|PlatformAdmin| G[SetPlatformAdminContext user_id]
  D -->|missing| Z2[403 Forbidden]
  E --> H[TenantValidationBehavior in MediatR pipeline]
  F --> H
  G --> H
  H --> I[Handler receives ITenantContextService.GetTenantContext]