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]