Skip to content

Keycloak OIDC Authentication ​

This document describes the authentication and authorization architecture for Hegemony, implemented using Keycloak as the OIDC identity provider.


Table of Contents ​

  1. Overview
  2. High-Level Architecture
  3. Keycloak's Role
  4. Authentication Flows
  5. Role-Based Access Control
  6. Operating Modes
  7. Security Features
  8. Failure Scenarios & Error Handling
  9. BFF Auth Gateway
  10. Internal API Protection
  11. External Identity Provider Integration
  12. RBAC Database Schema
  13. Configuration Reference
  14. Quick Reference
  15. 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 ​

PrincipleImplementation
Zero-trustAll API requests require authentication except explicitly public endpoints: health checks and webhook triggers (POST /hooks/{path_token} authenticates by signed payload instead)
Fail-fastProduction startup fails if security config is missing
Defense in depthMultiple validation layers (JWT + RBAC + internal tokens)
Separation of dutiesDistinct roles prevent self-approval (operator ≠ approver)
Secure by defaultAuth 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:

json
{
  "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 through POST /internal/runs/{run_id}/tf-state-access for one tf.* 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 the run_token authentication 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-access for one container-backed step, with the scope registry:step:<org slug>. The step may pull from its organization's namespace, the shared organization's and global, 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 a resource:verb key (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 :test action 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}/resolve beside .../{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.py flattens both files into one rule table (lookup_permission)
  • Runtime enforcement: apps/api/auth/rbac_middleware.py
  • Runtime overrides: the permission_action_overrides and permission_overrides tables, 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:

LayerTableSettings → Permissions tabScope
Action overridepermission_action_overridesActionsReallocates a whole action to a policy; every route of the action follows
Route overridepermission_overridesRoutesPins 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:manage is 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:

RouteAction (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 /runsrun: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 ​

RoleDescriptionInventory (CRUD)Flows/RunsApprovalsView All
adminFull administrative access✅✅✅✅
operatorManages flows and runs❌✅❌✅
approverReviews and approves requests❌❌✅✅
auditorRead-only with audit visibility❌❌❌✅
viewerBasic 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:

RefusalEntryActor
Authorization failure (403): a known caller lacked the role, the org membership, or the platform standingauthz.deniedThe caller's principal
Authentication failure that still names an identity (401 on a revoked or expired personal access token)auth.failedThe token's owner
A route switched off by the disabled policy (403)authz.deniedThe 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:

PolicyRuntime behavior
publicNo authentication required
authenticatedAny valid JWT
viewer/auditor/operator/approver/adminRole-based JWT checks (each role includes those below it; approver is a separate branch under admin)
org_member/org_adminMembership in the organization named by the route's {org_ref} parameter (any role / the admin role); platform admins always pass
internalX-Internal-Token validation
customEndpoint handles auth explicitly
disabled403 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:

ClassificationHow it is decidedWho satisfies an admin policy
orgPath is under an ORG_PREFIXES entryAn organization admin of the active org, or a platform admin
platformEverything elseOnly 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:

  1. Mock Principal: All requests receive a mock admin principal with full roles
  2. No Keycloak Required: Application runs without Keycloak container
  3. BFF Tickets Disabled: Returns 400 error (tickets require real auth)
  4. Internal Token Optional: Worker calls allowed without X-Internal-Token
python
# 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 ​

CheckFailure Behavior
HEGEMONY_AUTH_DISABLED=trueStartup fails with ValueError
HEGEMONY_INTERNAL_API_TOKEN not setStartup fails with ValueError
HEGEMONY_KEYCLOAK_ISSUER not setStartup fails with ValueError
Keycloak unreachable503 Service Unavailable on auth attempts

Dual Issuer Configuration (Docker Environments) ​

In Docker environments, internal and external URLs may differ:

Configuration:

bash
# 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/hegemony

Security Features ​

Token Storage Strategy ​

Hegemony uses a hybrid approach to token storage:

Storage TypeUsed ForXSS RiskPersistence
sessionStorageJWT tokens for API calls⚠️ Readable by JSCleared on tab close
BFF ticketsSSE connections✅ Server-side onlySingle-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 FeatureImplementation
Algorithm restrictionOnly RS256 accepted
Signature verificationVia JWKS public keys
Issuer validationMust match HEGEMONY_KEYCLOAK_ISSUER or HEGEMONY_KEYCLOAK_PUBLIC_ISSUER
Audience validationMust include hegemony-api
Expiration checkWith 30-second clock skew tolerance
Required claimsexp, iat, sub must be present
JWKS cachingConfigurable TTL (default 1 hour)

Internal Token Security ​

Worker-to-API authentication uses constant-time comparison to prevent timing attacks:

python
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:

FeatureConfiguration
Brute force protectionfailureFactor: 5, lockout after 5 failures
Account lockoutMax 15-minute wait, not permanent
SSL requirementsslRequired: external
Registration disabledNo self-registration
Token lifespansAccess: 5 min, Session idle: 30 min, Session max: 10 hr

Failure Scenarios & Error Handling ​

Authentication Failure Flow ​

Error Response Catalog ​

HTTP StatusScenarioResponse BodyRecovery Action
401Missing token{"detail": "Missing authentication token"}Login via Keycloak
401Expired token{"detail": "Token expired"}Refresh token or re-login
401Invalid token{"detail": "Invalid or expired token"}Re-login
401Invalid internal token{"detail": "Invalid internal authentication token"}Check HEGEMONY_INTERNAL_API_TOKEN
403Insufficient roles{"detail": "Insufficient permissions. Required role: X"}Request role assignment
503Keycloak unreachable{"detail": "Authentication service temporarily unavailable"}Retry after 30 seconds
503Auth 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 ​

EndpointMethodDescription
/auth/bff/ticketPOSTExchange JWT for session ticket
/auth/bff/validateGETValidate 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:

RouterPath PrefixAuthPurpose
monitors.py/runs/{id}/monitorsviewer policy via its action in apps/api/auth/routes.yaml (enforced by RBACMiddleware)UI queries
monitors_internal.py/api/v1/monitorsinternal 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):

  1. Navigate to User Federation → Add Provider → LDAP
  2. Configure connection settings (URL, bind DN, bind credential)
  3. Set up Mappers for:
    • Username (e.g., sAMAccountName)
    • Email
    • Groups → Realm roles mapping

SAML 2.0 Identity Provider ​

For SAML-based SSO with enterprise identity providers:

Configuration:

  1. Navigate to Identity Providers → Add Provider → SAML v2.0
  2. Import metadata from enterprise IdP
  3. Configure Mappers for attribute-to-role mapping
  4. Set up First Login Flow for user provisioning

Social Login Integration ​

For development or external collaborator access:

ProviderConfigurationUse Case
GitHubOAuth2 App credentialsDeveloper access
GoogleOAuth2 client ID/secretCorporate Google Workspace
MicrosoftAzure AD app registrationMicrosoft 365 organizations

Role Mapping from External IdPs ​

Keycloak can map external IdP attributes to Hegemony roles:


Configuration Reference ​

Complete Environment Variables ​

VariableRequiredDefaultDescription
Core Auth
HEGEMONY_KEYCLOAK_ISSUERYes*-Keycloak realm URL for API (e.g., http://keycloak:8081/realms/hegemony)
HEGEMONY_KEYCLOAK_PUBLIC_ISSUERNo-Keycloak realm URL for browser redirects (if different from internal)
HEGEMONY_KEYCLOAK_AUDIENCENohegemony-apiExpected audience claim in access tokens
HEGEMONY_KEYCLOAK_UI_CLIENT_IDNohegemony-uiOIDC client ID for the UI
HEGEMONY_KEYCLOAK_JWKS_CACHE_TTLNo3600JWKS cache TTL in seconds
Auth Control
HEGEMONY_AUTH_DISABLEDNofalseDisable auth for dev (blocked in prod)
HEGEMONY_ENVIRONMENTNodevEnvironment (dev, prod, production)
Internal API
HEGEMONY_INTERNAL_API_TOKENProd: Yes-Shared secret for worker→API calls
HEGEMONY_INTERNAL_API_TOKEN_OPTIONALNofalseAllow missing token in dev

*Required when HEGEMONY_AUTH_DISABLED=false

Keycloak Sync Variables ​

VariableRequiredDescription
KEYCLOAK_URLYesKeycloak base URL (e.g., http://keycloak:8080)
KEYCLOAK_REALMYesRealm name (e.g., hegemony)
KEYCLOAK_CLIENT_IDYesAdmin client ID for sync operations
KEYCLOAK_CLIENT_SECRETYesAdmin client secret

Client Configuration Summary ​

Client IDTypeFlowPurpose
hegemony-uiPublicAuthorization Code + PKCEReact SPA authentication
hegemony-apiBearer-only-Access token audience target
hegemony-automationConfidentialClient CredentialsCI/CD and automation service accounts

RBAC Database Schema ​

Hegemony stores RBAC data in the following tables:

users ​

Local user profiles synced from Keycloak:

ColumnTypeDescription
idUUIDPrimary key
subVARCHAR(255)Keycloak subject ID (unique, indexed)
usernameVARCHAR(255)Username (indexed)
emailVARCHAR(255)Email address
display_nameVARCHAR(255)Display name
is_activeBOOLEANAccount active status
created_atTIMESTAMPTZCreation timestamp
updated_atTIMESTAMPTZLast update timestamp
last_login_atTIMESTAMPTZLast 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).

ColumnTypeDescription
idUUIDPrimary key
slugVARCHAR(100)URL-safe identifier (unique, indexed, immutable)
nameVARCHAR(255)Organization name
descriptionTEXTDescription
is_activeBOOLEANOrganization active status
created_atTIMESTAMPTZCreation timestamp
updated_atTIMESTAMPTZLast update timestamp
settings_jsonJSONBOrganization-specific settings

org_memberships ​

User-organization role bindings:

ColumnTypeDescription
idUUIDPrimary key
user_idUUIDFK to users.id (CASCADE delete)
org_idUUIDFK to organizations.id (CASCADE delete)
roleVARCHAR(50)Role within organization (default: viewer)
created_atTIMESTAMPTZCreation timestamp
updated_atTIMESTAMPTZLast update timestamp

Constraints: Unique constraint on (user_id, org_id) - one membership per user per org.

audit_logs ​

Security event logging:

ColumnTypeDescription
idUUIDPrimary key
org_idUUIDFK to organizations.id (nullable; NULL for platform-level events). Stamped from the active-org context
tsTIMESTAMPTZEvent timestamp (indexed)
event_typeVARCHAR(100)Event type (indexed)
actor_subVARCHAR(255)Actor's Keycloak subject ID (indexed)
actor_usernameVARCHAR(255)Actor's username
resource_typeVARCHAR(100)Type of resource affected
resource_idVARCHAR(255)ID of resource affected
actionVARCHAR(100)Action performed
outcomeVARCHAR(50)Success/failure outcome
details_jsonJSONBAdditional event details
client_ipVARCHAR(45)Client IP address
user_agentTEXTClient user agent

Keycloak Sync Configuration ​

To sync users from Keycloak to the local database, configure:

VariableRequiredDescription
KEYCLOAK_URLYesKeycloak base URL (e.g., http://keycloak:8080)
KEYCLOAK_REALMYesRealm name (e.g., hegemony)
KEYCLOAK_CLIENT_IDYesAdmin client ID for sync operations
KEYCLOAK_CLIENT_SECRETYesAdmin 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 ​

ScenarioAuthentication MethodToken/Credential
User → UI → APIBearer TokenKeycloak JWT
Worker → APIX-Internal-TokenShared secret
SSE/EventSourceQuery param ticketBFF session ticket
CI/CD → APIBearer Tokenhegemony-automation client credentials
Dev modeMock PrincipalNo token required

Common Troubleshooting ​

SymptomLikely CauseSolution
401 on all requestsToken expired or invalidRe-login, check token expiry
503 "Auth service unavailable"Keycloak unreachableCheck Keycloak container, network
403 on specific endpointMissing required roleCheck user's Keycloak roles
Startup fails in prodMissing HEGEMONY_INTERNAL_API_TOKENSet the environment variable
SSE connection failsInvalid ticketGet fresh ticket from /auth/bff/ticket
Token issuer mismatchDocker network vs browser URLConfigure HEGEMONY_KEYCLOAK_PUBLIC_ISSUER

Glossary ​

TermFull NameDescription
ABACAttribute-Based Access ControlAuthorization model that evaluates attributes (user, resource, environment) to make access decisions. More granular than RBAC.
ADActive DirectoryMicrosoft's directory service for Windows domain networks, commonly used for enterprise user/group management.
APIApplication Programming InterfaceSet of protocols and tools for building software applications. In this context, the FastAPI backend.
BFFBackend-for-FrontendArchitecture pattern where a dedicated backend handles auth for a specific frontend, keeping tokens server-side.
CI/CDContinuous Integration / Continuous DeploymentAutomation practices for building, testing, and deploying code changes.
CORSCross-Origin Resource SharingHTTP mechanism that allows a server to indicate allowed origins for cross-origin requests.
CRUDCreate, Read, Update, DeleteThe four basic operations of persistent storage.
IdPIdentity ProviderService that creates, maintains, and manages identity information (e.g., Keycloak, Okta, Azure AD).
JWTJSON Web TokenCompact, URL-safe token format for securely transmitting claims between parties. Signed with RS256 in this system.
JWKSJSON Web Key SetA set of public keys used to verify JWT signatures. Fetched from Keycloak's /protocol/openid-connect/certs endpoint.
LDAPLightweight Directory Access ProtocolProtocol for accessing and maintaining distributed directory information services (user directories).
LRULeast Recently UsedCache eviction strategy that removes the least recently accessed items first.
OAuth2Open Authorization 2.0Industry-standard authorization framework for token-based access delegation.
OIDCOpenID ConnectIdentity layer built on top of OAuth2, adding authentication and user identity claims.
PKCEProof Key for Code ExchangeOAuth2 extension for public clients (SPAs) that prevents authorization code interception attacks. Pronounced "pixy".
RBACRole-Based Access ControlAuthorization model where permissions are assigned to roles, and users are assigned roles.
RS256RSA Signature with SHA-256Asymmetric signing algorithm using RSA keys. Keycloak signs JWTs with private key; API verifies with public key.
SAMLSecurity Assertion Markup LanguageXML-based standard for exchanging authentication and authorization data between IdPs and service providers.
SPASingle-Page ApplicationWeb app that loads a single HTML page and dynamically updates content. The React UI is an SPA.
SSEServer-Sent EventsHTTP-based protocol for servers to push real-time updates to clients. Used for live monitor streaming.
SSOSingle Sign-OnAuthentication scheme allowing users to log in once and access multiple applications.
TLSTransport Layer SecurityCryptographic protocol for secure communication over networks (HTTPS).
TTLTime To LiveDuration for which a token, ticket, or cached item remains valid before expiration.
UUIDUniversally Unique Identifier128-bit identifier standard (e.g., 550e8400-e29b-41d4-a716-446655440000). Used for database primary keys.
XSSCross-Site ScriptingSecurity vulnerability where attackers inject malicious scripts into web pages viewed by other users.

Keycloak-Specific Terms ​

TermDescription
RealmA Keycloak namespace that manages a set of users, credentials, roles, and groups. Hegemony uses the hegemony realm.
ClientAn application or service that can request authentication. Hegemony has three: hegemony-ui, hegemony-api, hegemony-automation.
Realm RolesRoles defined at the realm level, shared across all clients. Primary authorization mechanism in Hegemony.
Client RolesRoles scoped to a specific client. Reserved for future fine-grained API permissions.
Protocol MapperConfigures how user attributes and roles are mapped into token claims.
Identity BrokeringKeycloak feature that delegates authentication to external identity providers (SAML, OIDC, social logins).
User FederationKeycloak feature that syncs users from external directories (LDAP, Active Directory).
Bearer-only ClientA client that only validates tokens and never initiates authentication flows. The hegemony-api client is bearer-only.
Public ClientA client that cannot securely store secrets (e.g., SPAs). Uses PKCE for security. The hegemony-ui client is public.
Confidential ClientA client that can securely store secrets (e.g., backend services). The hegemony-automation client is confidential.

Released as open source under the AGPL-3.0-or-later license. Development is sponsored by Rexonix s.r.o.. Contact — [email protected].