Keycloak OIDC Authentication
This document describes the authentication and authorization architecture for Hegemony, implemented using Keycloak as the OIDC identity provider.
Table of Contents
- Overview
- High-Level Architecture
- Keycloak's Role
- Authentication Flows
- Role-Based Access Control
- Operating Modes
- Security Features
- Failure Scenarios & Error Handling
- BFF Auth Gateway
- Internal API Protection
- External Identity Provider Integration
- RBAC Database Schema
- Configuration Reference
- Quick Reference
- Glossary
Overview
Hegemony uses a modern enterprise authentication architecture:
- Frontend: React SPA with PKCE Authorization Code flow via
react-oidc-context - Backend: FastAPI with JWT validation via JWKS
- Worker: Shared secret token (
X-Internal-Token) for internal API calls - Identity Provider: Keycloak as the OIDC/OAuth2 identity provider
Design Principles
| Principle | Implementation |
|---|---|
| Zero-trust | All API requests require authentication except explicitly public endpoints: health checks and webhook triggers (POST /hooks/{path_token} authenticates by signed payload instead) |
| Fail-fast | Production startup fails if security config is missing |
| Defense in depth | Multiple validation layers (JWT + RBAC + internal tokens) |
| Separation of duties | Distinct roles prevent self-approval (operator ≠ approver) |
| Secure by default | Auth enabled by default; dev opt-out is explicit and blocked in prod |
High-Level Architecture
Complete System View
Component Responsibilities
Keycloak's Role
Keycloak serves as the central identity provider for Hegemony, handling all aspects of identity management:
Core Responsibilities
Keycloak Configuration Topology
Token Claims Structure
Access tokens issued by Keycloak include:
{
"sub": "user-uuid",
"preferred_username": "admin",
"email": "[email protected]",
"realm_access": {
"roles": ["admin", "operator", "approver"]
},
"resource_access": {
"hegemony-api": {
"roles": ["admin"]
}
},
"groups": ["/Admins"],
"aud": ["hegemony-api"],
"iss": "http://localhost:8081/realms/hegemony",
"exp": 1706918400,
"iat": 1706914800
}Authentication Flows
User Authentication (Frontend → API)
Worker Internal API Calls
Terraform/OpenTofu State Backend
OpenTofu and Terraform reach managed state with their http backend, which can only send HTTP Basic credentials. So the two backend paths, /tf-states/{name}/state and /tf-states/{name}/lock, also accept Authorization: Basic, with a token as the password (the username is ignored). Every other route refuses Basic, including POST /tf-states/{name}/import, which uploads a state file as a new state: a script sends its token there as Authorization: Bearer.
- A personal access token works as on any route: its scopes are its roles in its organization.
- A run step's token (
hgm_run_…) is minted by the worker throughPOST /internal/runs/{run_id}/tf-state-accessfor onetf.*step. It acts for the run's organization with the operator role, only on the backend paths, and only for the one state it names. Only a digest is stored; it expires, is revoked when the step finishes, and stops working when the run ends. Audit entries record it as therun_tokenauthentication method, naming the run and step.
Platform Registry
A container step pulls from, and caches into, the platform registry with the Docker client's own HTTP Basic login. The registry's proxy asks GET /registry/authorize (policy custom) before it forwards each request. That route answers 404 unless the request carries the proxy's shared secret header, so it is invisible from anywhere but the proxy (the web image's nginx refuses it too); 401 with a Basic challenge when the credential is missing or is not a live run token; 403 when the token's organization may not do what the request asks; and 204 otherwise. Only run tokens are accepted there, never a personal access token or a session.
- The worker mints the token through
POST /internal/runs/{run_id}/registry-accessfor one container-backed step, with the scoperegistry:step:<org slug>. The step may pull from its organization's namespace, the shared organization's andglobal, and may push only into its organization's own cache (orgs/<slug>/<host>/…). Deletes and catalogue listings are refused whatever the scope. - As with the state backend, only a digest is stored; the token expires, is revoked when the step finishes, and stops working when the run ends. Pulls are reads and leave no audit entry; the step's run events record which image was served and from where.
Role-Based Access Control
Hegemony uses a centralized permission registry for API authorization, declared in two files:
- Action registry:
apps/api/auth/actions.yaml— every action is aresource:verbkey (flow:view,run:trigger,secret:manage) with a default policy and a description. Resources that hold a credential reference (git repositories, file repositories, registry credentials, secret backends, inventory providers, notification destinations) also carry a:testaction for their connection or login probe, so the right to see a configuration and the right to make the API use its credential stay separate - Route map:
apps/api/auth/routes.yaml— every API route belongs to exactly one action and inherits its policy. Every route is listed by its own path, including a fixed sub-path that a sibling's{param}wildcard would also match (GET /inventory/objects/{object_type}/resolvebeside.../{object_id}), so the registry, its snapshot test and the generated reference name the route rather than relying on the wildcard - Loader and lookup:
apps/api/auth/permissions.pyflattens both files into one rule table (lookup_permission) - Runtime enforcement:
apps/api/auth/rbac_middleware.py - Runtime overrides: the
permission_action_overridesandpermission_overridestables, edited under Settings → Permissions - OpenAPI auth metadata: enriched from the same registry in
apps/api/main.py
This keeps runtime behavior and documentation aligned without duplicating auth rules in route decorators. The generated permissions reference lists every action with its default policy and routes.
Actions and Overrides
An action is the unit administrators allocate to roles. Its default policy comes from actions.yaml; at runtime two override layers can change it, both persisted in the database, audited, and cached in-process:
| Layer | Table | Settings → Permissions tab | Scope |
|---|---|---|---|
| Action override | permission_action_overrides | Actions | Reallocates a whole action to a policy; every route of the action follows |
| Route override | permission_overrides | Routes | Pins a single method + path to a policy; wins over the action (escape hatch) |
The policy in effect for a request is resolved as route override → action override → action default. public, internal and custom policies are structural and cannot be overridden at either layer, permission:manage is pinned to its default so no override can hand the permission matrix to a lower role, and a target-org policy (org_member/org_admin) can only be assigned where every affected route carries an {org_ref} parameter. Both override layers round-trip through configuration exchange as the permission_action_overrides and permission_overrides bundle sections.
The disabled policy
One override target is not a role. Setting an action or a route to disabled switches it off, and the middleware then refuses every caller — platform admins included, and in dev mode (HEGEMONY_AUTH_DISABLED=true) too. It is override-only: no action may declare it as its default.
The way back is protected at both layers, so an administrator can always reach the editor and assign another policy:
permission:manageis pinned, so the editor's own routes accept no override at all.auth:me— the identity and capability lookups the console loads before it renders anything — may be reallocated, but never disabled.
Like every override it is enforced from the in-process snapshot, so an instance that starts while the database is unreachable serves the registry defaults until its first successful refresh; a disabled endpoint answers normally for that window.
Every refusal by the kill switch is recorded (see Recording Refusals), so a route that stopped answering can be told apart from one that was switched off.
Handler-level narrowing
The middleware decides whether a request may reach a handler at all. A handler may still refuse a specific option within a route its caller legitimately holds, when that one option is more dangerous than the rest of the route:
| Route | Action (policy) | Narrowed to admin in the handler |
|---|---|---|
POST/PUT /flows… | flow:manage (operator) | container.run host-access options and egress widening beyond the flow's network policy |
POST /runs | run:trigger (operator) | ?force=true, which starts a run of a flow that refuses manual runs |
These are deliberately not separate actions: splitting the route would mean an operator who may create flows could not create ordinary ones, and the registry would grow an action per dangerous field. They check principal.has_role("admin") and raise 403 naming the restriction, so the refusal reads differently from the middleware's "you may not call this route".
The narrowing is a property of the handler, not of the registry, so it is not reallocatable through an override. An administrator who needs to hand one of these to a lower role changes the code, not the permission matrix.
Permission Matrix
| Role | Description | Inventory (CRUD) | Flows/Runs | Approvals | View All |
|---|---|---|---|---|---|
| admin | Full administrative access | ✅ | ✅ | ✅ | ✅ |
| operator | Manages flows and runs | ❌ | ✅ | ❌ | ✅ |
| approver | Reviews and approves requests | ❌ | ❌ | ✅ | ✅ |
| auditor | Read-only with audit visibility | ❌ | ❌ | ❌ | ✅ |
| viewer | Basic read-only access | ❌ | ❌ | ❌ | ✅ |
Centralized Enforcement Flow
Request-Scoped Audit Context
Once a request is admitted, the middleware binds a request-scoped audit context around the handler: the principal's subject and username, how they authenticated (jwt, pat, internal, dev_bypass, public), the client address, the user agent, and the route. Audit entries read their actor and client fields from it, so a handler records who acted without threading the principal through every call, and every entry written during the request agrees on the answer. The context is reset when the request finishes.
A route whose policy is public records a masked client address, because nothing authenticated the caller. The address is taken from the connection peer; set HEGEMONY_TRUST_PROXY_HEADERS when a trusted proxy sets X-Forwarded-For and the first hop is used instead.
Recording Refusals
A refused request never reaches a handler, so the middleware is the only place that can record it. Three kinds of refusal get a row in the audit table:
| Refusal | Entry | Actor |
|---|---|---|
| Authorization failure (403): a known caller lacked the role, the org membership, or the platform standing | authz.denied | The caller's principal |
| Authentication failure that still names an identity (401 on a revoked or expired personal access token) | auth.failed | The token's owner |
A route switched off by the disabled policy (403) | authz.denied | The caller's address, because the check runs before authentication |
The audited object is the method and the matched route pattern (POST /orgs/{org_ref}/members), not the concrete path, so one route's history lists every refusal it produced. The recorded policy is the effective one, so a denial on an overridden route names the policy the override put there. An authentication failure that names nobody is log-only — there is no principal to attribute it to and any client can produce one at any rate — while the disabled-policy refusal above is recorded against the caller's address because the route itself is the object. Bursts are throttled — see Access decisions.
Policy Mapping Reference
The branch labels in the diagram are the runtime policy types (roles, target_org_roles, internal_token, …). Policy names in actions.yaml (and in overrides) map to them as follows:
| Policy | Runtime behavior |
|---|---|
public | No authentication required |
authenticated | Any valid JWT |
viewer/auditor/operator/approver/admin | Role-based JWT checks (each role includes those below it; approver is a separate branch under admin) |
org_member/org_admin | Membership in the organization named by the route's {org_ref} parameter (any role / the admin role); platform admins always pass |
internal | X-Internal-Token validation |
custom | Endpoint handles auth explicitly |
disabled | 403 for every caller; assignable only as an override |
Route Scope: Org vs Platform
Every route classifies as org-scoped or platform-scoped (apps/api/auth/org_scope.py), and the classification changes what a role name means:
| Classification | How it is decided | Who satisfies an admin policy |
|---|---|---|
| org | Path is under an ORG_PREFIXES entry | An organization admin of the active org, or a platform admin |
| platform | Everything else | Only a platform admin (realm admin); an org-local admin PAT scope is stripped |
Org-scoped routes defer the role decision until the active organization is resolved, then evaluate the caller's effective org roles. Platform-scoped routes evaluate realm roles directly.
This is a security boundary, not a formality: putting a route that reads across tenants under an org prefix would let any organization admin read every other tenant's data. Deployment-wide endpoints therefore live outside the org prefixes deliberately - GET /platform/health is separate from /dashboard/stats for exactly this reason.
Separation of Duties
The approver role is intentionally separate from operator:
This ensures:
- Operators cannot self-approve their own workflow runs
- Approvers cannot create runs they then approve
- Only admins have full access (for emergency situations)
Operating Modes
Hegemony supports different operating modes for development and production environments.
Mode Comparison
Development Mode Features
When HEGEMONY_AUTH_DISABLED=true:
- Mock Principal: All requests receive a mock admin principal with full roles
- No Keycloak Required: Application runs without Keycloak container
- BFF Tickets Disabled: Returns 400 error (tickets require real auth)
- Internal Token Optional: Worker calls allowed without
X-Internal-Token
# Mock principal returned when auth disabled
Principal(
sub="dev-user",
username="dev",
email="dev@localhost",
realm_roles=["admin", "operator", "auditor"],
client_roles=["admin"],
groups=[],
is_service_account=False,
)Production Mode Enforcements
| Check | Failure Behavior |
|---|---|
HEGEMONY_AUTH_DISABLED=true | Startup fails with ValueError |
HEGEMONY_INTERNAL_API_TOKEN not set | Startup fails with ValueError |
HEGEMONY_KEYCLOAK_ISSUER not set | Startup fails with ValueError |
| Keycloak unreachable | 503 Service Unavailable on auth attempts |
Dual Issuer Configuration (Docker Environments)
In Docker environments, internal and external URLs may differ:
Configuration:
# Internal URL for JWKS fetch (API → Keycloak)
HEGEMONY_KEYCLOAK_ISSUER=http://keycloak:8081/realms/hegemony
# External URL for browser redirects (Browser → Keycloak)
HEGEMONY_KEYCLOAK_PUBLIC_ISSUER=http://localhost:8081/realms/hegemonySecurity Features
Token Storage Strategy
Hegemony uses a hybrid approach to token storage:
| Storage Type | Used For | XSS Risk | Persistence |
|---|---|---|---|
| sessionStorage | JWT tokens for API calls | ⚠️ Readable by JS | Cleared on tab close |
| BFF tickets | SSE connections | ✅ Server-side only | Single-use, 5-min TTL |
XSS Mitigation:
- sessionStorage clears tokens when browser/tab closes (vs localStorage which persists)
- BFF tickets for SSE are already immune to XSS (server-side storage)
- Token refresh handled server-side via OIDC silent renewal
JWT Validation Security
The JWT validator (jwt_validator.py) implements:
| Security Feature | Implementation |
|---|---|
| Algorithm restriction | Only RS256 accepted |
| Signature verification | Via JWKS public keys |
| Issuer validation | Must match HEGEMONY_KEYCLOAK_ISSUER or HEGEMONY_KEYCLOAK_PUBLIC_ISSUER |
| Audience validation | Must include hegemony-api |
| Expiration check | With 30-second clock skew tolerance |
| Required claims | exp, iat, sub must be present |
| JWKS caching | Configurable TTL (default 1 hour) |
Internal Token Security
Worker-to-API authentication uses constant-time comparison to prevent timing attacks:
import hmac
if not hmac.compare_digest(provided_token, settings.internal_api_token):
raise HTTPException(status_code=401, detail="Invalid internal token")Keycloak Security Features
The realm export includes:
| Feature | Configuration |
|---|---|
| Brute force protection | failureFactor: 5, lockout after 5 failures |
| Account lockout | Max 15-minute wait, not permanent |
| SSL requirement | sslRequired: external |
| Registration disabled | No self-registration |
| Token lifespans | Access: 5 min, Session idle: 30 min, Session max: 10 hr |
Failure Scenarios & Error Handling
Authentication Failure Flow
Error Response Catalog
| HTTP Status | Scenario | Response Body | Recovery Action |
|---|---|---|---|
| 401 | Missing token | {"detail": "Missing authentication token"} | Login via Keycloak |
| 401 | Expired token | {"detail": "Token expired"} | Refresh token or re-login |
| 401 | Invalid token | {"detail": "Invalid or expired token"} | Re-login |
| 401 | Invalid internal token | {"detail": "Invalid internal authentication token"} | Check HEGEMONY_INTERNAL_API_TOKEN |
| 403 | Insufficient roles | {"detail": "Insufficient permissions. Required role: X"} | Request role assignment |
| 503 | Keycloak unreachable | {"detail": "Authentication service temporarily unavailable"} | Retry after 30 seconds |
| 503 | Auth not configured | {"detail": "Authentication service not configured"} | Check Keycloak config |
Silent Token Renewal
The UI handles token refresh automatically:
API 401 Recovery
When the API returns 401, the UI handles it gracefully:
BFF Auth Gateway
The BFF (Backend-for-Frontend) auth gateway handles SSE/EventSource connections where browsers cannot use Authorization headers.
The gateway issues single-use, server-side tickets for SSE/EventSource connections (implementation: routers/bff.py). Regular API calls authenticate with a Bearer token.
BFF Ticket Flow
Ticket Security Properties
- Cryptographically random: 256-bit tokens via
secrets.token_urlsafe(32) - Short-lived: 5-minute TTL (configurable via
TICKET_TTL_SECONDS) - Single-use: Tickets are consumed on first validation
- Rate-limited: Max 10 active tickets per user
Rate Limit Behavior
When a user reaches the maximum of 10 active tickets:
- The oldest ticket is evicted to make room for the new one (LRU eviction)
- New ticket issuance succeeds without error
- The evicted ticket becomes invalid immediately
This design prioritizes availability over strict limits, ensuring users can always obtain new tickets for SSE connections.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/auth/bff/ticket | POST | Exchange JWT for session ticket |
/auth/bff/validate | GET | Validate and consume ticket |
Internal API Protection
Secure-by-Default Model
Internal endpoints are protected by X-Internal-Token header validation:
Router Separation
Internal and public monitor endpoints are separated into distinct routers:
| Router | Path Prefix | Auth | Purpose |
|---|---|---|---|
monitors.py | /runs/{id}/monitors | viewer policy via its action in apps/api/auth/routes.yaml (enforced by RBACMiddleware) | UI queries |
monitors_internal.py | /api/v1/monitors | internal policy via its action in apps/api/auth/routes.yaml (enforced by RBACMiddleware) | Worker ingestion |
External Identity Provider Integration
Keycloak supports federation with external identity providers, enabling enterprise SSO scenarios.
Supported Federation Methods
LDAP/Active Directory Integration
For enterprise environments using LDAP or Active Directory:
Configuration (Keycloak Admin Console):
- Navigate to User Federation → Add Provider → LDAP
- Configure connection settings (URL, bind DN, bind credential)
- Set up Mappers for:
- Username (e.g.,
sAMAccountName) - Groups → Realm roles mapping
- Username (e.g.,
SAML 2.0 Identity Provider
For SAML-based SSO with enterprise identity providers:
Configuration:
- Navigate to Identity Providers → Add Provider → SAML v2.0
- Import metadata from enterprise IdP
- Configure Mappers for attribute-to-role mapping
- Set up First Login Flow for user provisioning
Social Login Integration
For development or external collaborator access:
| Provider | Configuration | Use Case |
|---|---|---|
| GitHub | OAuth2 App credentials | Developer access |
| OAuth2 client ID/secret | Corporate Google Workspace | |
| Microsoft | Azure AD app registration | Microsoft 365 organizations |
Role Mapping from External IdPs
Keycloak can map external IdP attributes to Hegemony roles:
Configuration Reference
Complete Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
| Core Auth | |||
HEGEMONY_KEYCLOAK_ISSUER | Yes* | - | Keycloak realm URL for API (e.g., http://keycloak:8081/realms/hegemony) |
HEGEMONY_KEYCLOAK_PUBLIC_ISSUER | No | - | Keycloak realm URL for browser redirects (if different from internal) |
HEGEMONY_KEYCLOAK_AUDIENCE | No | hegemony-api | Expected audience claim in access tokens |
HEGEMONY_KEYCLOAK_UI_CLIENT_ID | No | hegemony-ui | OIDC client ID for the UI |
HEGEMONY_KEYCLOAK_JWKS_CACHE_TTL | No | 3600 | JWKS cache TTL in seconds |
| Auth Control | |||
HEGEMONY_AUTH_DISABLED | No | false | Disable auth for dev (blocked in prod) |
HEGEMONY_ENVIRONMENT | No | dev | Environment (dev, prod, production) |
| Internal API | |||
HEGEMONY_INTERNAL_API_TOKEN | Prod: Yes | - | Shared secret for worker→API calls |
HEGEMONY_INTERNAL_API_TOKEN_OPTIONAL | No | false | Allow missing token in dev |
*Required when HEGEMONY_AUTH_DISABLED=false
Keycloak Sync Variables
| Variable | Required | Description |
|---|---|---|
KEYCLOAK_URL | Yes | Keycloak base URL (e.g., http://keycloak:8080) |
KEYCLOAK_REALM | Yes | Realm name (e.g., hegemony) |
KEYCLOAK_CLIENT_ID | Yes | Admin client ID for sync operations |
KEYCLOAK_CLIENT_SECRET | Yes | Admin client secret |
Client Configuration Summary
| Client ID | Type | Flow | Purpose |
|---|---|---|---|
hegemony-ui | Public | Authorization Code + PKCE | React SPA authentication |
hegemony-api | Bearer-only | - | Access token audience target |
hegemony-automation | Confidential | Client Credentials | CI/CD and automation service accounts |
RBAC Database Schema
Hegemony stores RBAC data in the following tables:
users
Local user profiles synced from Keycloak:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
sub | VARCHAR(255) | Keycloak subject ID (unique, indexed) |
username | VARCHAR(255) | Username (indexed) |
email | VARCHAR(255) | Email address |
display_name | VARCHAR(255) | Display name |
is_active | BOOLEAN | Account active status |
created_at | TIMESTAMPTZ | Creation timestamp |
updated_at | TIMESTAMPTZ | Last update timestamp |
last_login_at | TIMESTAMPTZ | Last login timestamp |
organizations
Multi-tenant organization boundaries. These tables are actively enforced — see Multi-Tenancy for the full model (header contract, per-org RBAC, scope matrix, and integrity rules).
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
slug | VARCHAR(100) | URL-safe identifier (unique, indexed, immutable) |
name | VARCHAR(255) | Organization name |
description | TEXT | Description |
is_active | BOOLEAN | Organization active status |
created_at | TIMESTAMPTZ | Creation timestamp |
updated_at | TIMESTAMPTZ | Last update timestamp |
settings_json | JSONB | Organization-specific settings |
org_memberships
User-organization role bindings:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
user_id | UUID | FK to users.id (CASCADE delete) |
org_id | UUID | FK to organizations.id (CASCADE delete) |
role | VARCHAR(50) | Role within organization (default: viewer) |
created_at | TIMESTAMPTZ | Creation timestamp |
updated_at | TIMESTAMPTZ | Last update timestamp |
Constraints: Unique constraint on (user_id, org_id) - one membership per user per org.
audit_logs
Security event logging:
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
org_id | UUID | FK to organizations.id (nullable; NULL for platform-level events). Stamped from the active-org context |
ts | TIMESTAMPTZ | Event timestamp (indexed) |
event_type | VARCHAR(100) | Event type (indexed) |
actor_sub | VARCHAR(255) | Actor's Keycloak subject ID (indexed) |
actor_username | VARCHAR(255) | Actor's username |
resource_type | VARCHAR(100) | Type of resource affected |
resource_id | VARCHAR(255) | ID of resource affected |
action | VARCHAR(100) | Action performed |
outcome | VARCHAR(50) | Success/failure outcome |
details_json | JSONB | Additional event details |
client_ip | VARCHAR(45) | Client IP address |
user_agent | TEXT | Client user agent |
Keycloak Sync Configuration
To sync users from Keycloak to the local database, configure:
| Variable | Required | Description |
|---|---|---|
KEYCLOAK_URL | Yes | Keycloak base URL (e.g., http://keycloak:8080) |
KEYCLOAK_REALM | Yes | Realm name (e.g., hegemony) |
KEYCLOAK_CLIENT_ID | Yes | Admin client ID for sync operations |
KEYCLOAK_CLIENT_SECRET | Yes | Admin client secret |
Note: The current implementation uses token-based RBAC. Users are provisioned on first authenticated request; new users with no membership auto-join the default org when HEGEMONY_ORG_AUTO_JOIN=true (the default). See Multi-Tenancy for org resolution and per-org role semantics.
Quick Reference
Auth Flow Cheat Sheet
| Scenario | Authentication Method | Token/Credential |
|---|---|---|
| User → UI → API | Bearer Token | Keycloak JWT |
| Worker → API | X-Internal-Token | Shared secret |
| SSE/EventSource | Query param ticket | BFF session ticket |
| CI/CD → API | Bearer Token | hegemony-automation client credentials |
| Dev mode | Mock Principal | No token required |
Common Troubleshooting
| Symptom | Likely Cause | Solution |
|---|---|---|
| 401 on all requests | Token expired or invalid | Re-login, check token expiry |
| 503 "Auth service unavailable" | Keycloak unreachable | Check Keycloak container, network |
| 403 on specific endpoint | Missing required role | Check user's Keycloak roles |
| Startup fails in prod | Missing HEGEMONY_INTERNAL_API_TOKEN | Set the environment variable |
| SSE connection fails | Invalid ticket | Get fresh ticket from /auth/bff/ticket |
| Token issuer mismatch | Docker network vs browser URL | Configure HEGEMONY_KEYCLOAK_PUBLIC_ISSUER |
Glossary
| Term | Full Name | Description |
|---|---|---|
| ABAC | Attribute-Based Access Control | Authorization model that evaluates attributes (user, resource, environment) to make access decisions. More granular than RBAC. |
| AD | Active Directory | Microsoft's directory service for Windows domain networks, commonly used for enterprise user/group management. |
| API | Application Programming Interface | Set of protocols and tools for building software applications. In this context, the FastAPI backend. |
| BFF | Backend-for-Frontend | Architecture pattern where a dedicated backend handles auth for a specific frontend, keeping tokens server-side. |
| CI/CD | Continuous Integration / Continuous Deployment | Automation practices for building, testing, and deploying code changes. |
| CORS | Cross-Origin Resource Sharing | HTTP mechanism that allows a server to indicate allowed origins for cross-origin requests. |
| CRUD | Create, Read, Update, Delete | The four basic operations of persistent storage. |
| IdP | Identity Provider | Service that creates, maintains, and manages identity information (e.g., Keycloak, Okta, Azure AD). |
| JWT | JSON Web Token | Compact, URL-safe token format for securely transmitting claims between parties. Signed with RS256 in this system. |
| JWKS | JSON Web Key Set | A set of public keys used to verify JWT signatures. Fetched from Keycloak's /protocol/openid-connect/certs endpoint. |
| LDAP | Lightweight Directory Access Protocol | Protocol for accessing and maintaining distributed directory information services (user directories). |
| LRU | Least Recently Used | Cache eviction strategy that removes the least recently accessed items first. |
| OAuth2 | Open Authorization 2.0 | Industry-standard authorization framework for token-based access delegation. |
| OIDC | OpenID Connect | Identity layer built on top of OAuth2, adding authentication and user identity claims. |
| PKCE | Proof Key for Code Exchange | OAuth2 extension for public clients (SPAs) that prevents authorization code interception attacks. Pronounced "pixy". |
| RBAC | Role-Based Access Control | Authorization model where permissions are assigned to roles, and users are assigned roles. |
| RS256 | RSA Signature with SHA-256 | Asymmetric signing algorithm using RSA keys. Keycloak signs JWTs with private key; API verifies with public key. |
| SAML | Security Assertion Markup Language | XML-based standard for exchanging authentication and authorization data between IdPs and service providers. |
| SPA | Single-Page Application | Web app that loads a single HTML page and dynamically updates content. The React UI is an SPA. |
| SSE | Server-Sent Events | HTTP-based protocol for servers to push real-time updates to clients. Used for live monitor streaming. |
| SSO | Single Sign-On | Authentication scheme allowing users to log in once and access multiple applications. |
| TLS | Transport Layer Security | Cryptographic protocol for secure communication over networks (HTTPS). |
| TTL | Time To Live | Duration for which a token, ticket, or cached item remains valid before expiration. |
| UUID | Universally Unique Identifier | 128-bit identifier standard (e.g., 550e8400-e29b-41d4-a716-446655440000). Used for database primary keys. |
| XSS | Cross-Site Scripting | Security vulnerability where attackers inject malicious scripts into web pages viewed by other users. |
Keycloak-Specific Terms
| Term | Description |
|---|---|
| Realm | A Keycloak namespace that manages a set of users, credentials, roles, and groups. Hegemony uses the hegemony realm. |
| Client | An application or service that can request authentication. Hegemony has three: hegemony-ui, hegemony-api, hegemony-automation. |
| Realm Roles | Roles defined at the realm level, shared across all clients. Primary authorization mechanism in Hegemony. |
| Client Roles | Roles scoped to a specific client. Reserved for future fine-grained API permissions. |
| Protocol Mapper | Configures how user attributes and roles are mapped into token claims. |
| Identity Brokering | Keycloak feature that delegates authentication to external identity providers (SAML, OIDC, social logins). |
| User Federation | Keycloak feature that syncs users from external directories (LDAP, Active Directory). |
| Bearer-only Client | A client that only validates tokens and never initiates authentication flows. The hegemony-api client is bearer-only. |
| Public Client | A client that cannot securely store secrets (e.g., SPAs). Uses PKCE for security. The hegemony-ui client is public. |
| Confidential Client | A client that can securely store secrets (e.g., backend services). The hegemony-automation client is confidential. |