GOV RBAC
Role-Based Access Control · UK Government
A production-grade RBAC platform spanning four Spring Boot microservices. Manages user identity, role assignment, capability enforcement, and Keycloak IAM integration — all behind a server-side rendered GOV.UK Design System interface. Authentication is OIDC-based with PKCE; authorisation uses capability claims embedded directly in signed JWTs, eliminating per-request access control lookups at runtime.
Authentication uses the OIDC Authorization Code flow with PKCE via Keycloak. Login initiation is rate-limited to 20 requests per 15 minutes per IP. Sessions are regenerated on successful token exchange to prevent session fixation — access, ID, and refresh tokens are stored server-side only and never sent to the browser.
The Active/Inactive status tag reflects the active_ind field, set daily by the inactivity scheduler (60-day threshold). The Create User button is rendered only when the signed-in user holds the User Administration permission role — the template adapts without a separate route.
Administrative actions (Reset password, Lock account) are conditionally rendered by Nunjucks against currentUserRoles — no separate admin template. Password reset generates a four-word temporary passphrase via Keycloak, displayed once and never persisted.
Changes are committed in two steps — this selection form, then a confirmation page showing the before/after diff. On save, ACM traverses the role inheritance graph, resolves the full capability set, then syncs capabilities and systemRoles as Keycloak user attributes so they are embedded in the next JWT.
Every state change is captured by PostgreSQL versioning() triggers writing to mirrored history tables with a sys_period tstzrange validity window. No application-level audit logic — the database trigger is the complete mechanism.
User Creation — Saga Pattern
User creation orchestrates a distributed transaction across UMS, Keycloak (via KM), and ACM. If ACM fails after Keycloak succeeds, a compensation call rolls back the Keycloak account — all three systems stay consistent without a two-phase commit.
JWT Permissions Embedding
Capabilities are written as Keycloak user attributes and embedded in JWTs via protocol mappers registered at startup. Downstream services read capabilities directly from the signed token — no ACM call at request time. HTTP fallback covers tokens predating the mappers.
Account Lifecycle Management
Daily inactivity scheduler (60-day threshold) marks dormant accounts inactive. Hourly lock-sync detects Keycloak brute-force events (5-attempt threshold) and mirrors lock state to ACM. Admin unlock clears Keycloak state and resets failed attempt counts atomically.
Audit Trail — Temporal Tables
PostgreSQL versioning triggers copy every modified row to a history table before the change is applied, recording validity via sys_period tstzrange. Role grant and revocation history is surfaced with modifier names resolved by cross-schema UUID join.
Security — Layered Defence
Server-side sessions with httpOnly/secure cookies; CSRF via synchronizer token pattern; Helmet headers with strict CSP (no unsafe-inline); rate limiting on login; session regeneration on OIDC callback; locked-account guard that destroys sessions at the door.
Permission-Driven UI Rendering
Route-level access control via requireRole middleware. Nunjucks templates render action controls (Reset password, Lock, Manage roles) conditionally against currentUserRoles — one template per page, every permission tier served.
| Service | Stack |
|---|---|
user-management-ui | Node.js · Express · TypeScript |
user-management-service | Spring Boot 3 · Java 17 |
access-control-manager | Spring Boot 3 · Java 17 |
keycloak-manager | Spring Boot 3 · Java 17 |
All four services share a single PostgreSQL instance split across two schemas — user_management and access_control. Identity is correlated via a shared system_user_id UUID; no foreign key crosses schema boundaries.
UMS persists the user record, calls KM to create the Keycloak account, then calls ACM to initialise access-control. If ACM fails, a compensation call to KM deletes the Keycloak account before rolling back the database transaction.
ACM fail → KM [Keycloak rollback] → UMS [DB rollback]
On role change, ACM traverses the inheritance graph and writes resolved capabilities as Keycloak user attributes via KM. Protocol mappers registered at KM startup embed these in every subsequent JWT — no ACM call required during request processing.
Next token: capabilities: ["Create users", ...] embedded as signed claim
After OIDC token exchange the UI fetches the user record from UMS. If locked = true is returned the session is destroyed immediately and the browser is redirected to an account-locked page — the user cannot enter the application. On success the session is regenerated and last_login_date is stamped.
The repository includes a detailed application guide covering every feature, flow, and architectural decision — from the data model and saga pattern through JWT embedding, session security, and GOV.UK component usage.
Saga orchestration, Keycloak compensation, inheritance graph, two-step confirmation
Daily scheduler, 60-day threshold, active_ind, login date stamping
Passphrase generation, brute-force sync, hourly lock scheduler, manual unlock
Temporal tables, versioning triggers, MDC structured logging, correlation IDs
PostgreSQL schemas, entity relationships, cross-schema identity, saga diagram
OIDC PKCE flow, JWT embedding, CSP headers, CSRF, method-level authorisation