Skip to content

Personal Access Tokens (PATs) ​

Hegemony issues long-lived, opaque Personal Access Tokens (PATs) for authenticating scripts, Postman collections, and the bundled Swagger UI's Authorize dialog. PATs are an alternative to Keycloak-issued JWTs intended for non-interactive use; humans should still sign in through SSO.

PATs are admin-only to issue and revoke. This is enforced centrally by the RBAC middleware (the api_token:manage action in apps/api/auth/actions.yaml).

Token format ​

text
hgm_pat_<43 url-safe base64 chars>
  • Prefix: hgm_pat_ (constant, makes tokens easy to recognize in logs/secret scanners).
  • Body: 32 bytes of secrets.token_urlsafe entropy.
  • The plaintext is shown exactly once when the token is created.
  • Only an HMAC-SHA-256 digest is persisted (personal_access_tokens.token_hash) along with the first 12 chars of the plaintext as a non-secret display_prefix.

Hashing ​

apps/api/services/api_tokens.py computes:

text
token_hash = HMAC-SHA-256(key=HEGEMONY_PAT_PEPPER, msg=plaintext)

If HEGEMONY_PAT_PEPPER is unset, the service falls back to plain SHA-256. Production deployments must set HEGEMONY_PAT_PEPPER — packages/core/ settings.py raises at boot if the variable is empty and HEGEMONY_ENVIRONMENT is prod/production.

Configuration ​

Env varDefaultMeaning
HEGEMONY_PAT_PEPPERemptyHMAC key. Required in production.
HEGEMONY_PAT_MAX_LIFETIME_DAYS365Maximum allowed expires_in_days; 0 disables the cap. When the caller omits expires_in_days and the cap is non-zero, tokens default to the cap.

REST API ​

All endpoints are under /api-tokens (reachable through the UI proxy as /api/api-tokens). All routes require the admin realm role.

POST /api-tokens ​

Issue a new token. Request body:

json
{
  "name": "Postman dev",
  "scopes": ["viewer"],
  "expires_in_days": 30
}

Response (201):

json
{
  "id": "…",
  "user_username": "alice",
  "name": "Postman dev",
  "display_prefix": "hgm_pat_xY1",
  "scopes": ["viewer"],
  "expires_at": "2026-06-25T12:00:00Z",
  "last_used_at": null,
  "created_at": "2026-05-26T12:00:00Z",
  "revoked_at": null,
  "issued_by_username": "admin",
  "token": "hgm_pat_xY1…"
}

The plaintext is in token. Copy it immediately — the server cannot recover it after this response is sent.

GET /api-tokens?include_revoked=&user_username= ​

List tokens. Excludes revoked tokens by default. Filter by owner username.

DELETE /api-tokens/{token_id} ​

Revoke a single token. Returns {"revoked": 1} on success or 404 if unknown. Repeating the call against an already-revoked token returns {"revoked": 0}.

DELETE /api-tokens/by-user/{username} ​

Bulk-revoke all active tokens owned by username. Returns the count of rows affected.

Scopes ​

The allowed scope values mirror the Keycloak realm roles consumed by the RBAC policy:

  • admin
  • operator
  • approver
  • auditor
  • viewer

A PAT's scopes become the bearer's realm_roles for the duration of the request. Issuing an admin-scoped PAT effectively grants full API access — use sparingly.

Defense in depth ​

  • PAT cannot mint or revoke PATs. The RBAC middleware sets request.state.auth_method = "pat" for PAT-authenticated requests; the api_tokens router refuses to act on those calls (HTTP 403). Only JWT sessions can manage tokens.
  • Constant prefix makes leaked tokens easy to grep for in logs and to detect with secret-scanning tools.
  • Last-used throttling. last_used_at is updated at most every 60 seconds per token to avoid a DB write per request, with an in-process LRU cap.

Using a PAT ​

Swagger UI ​

  1. Open /api/docs (link is on the Settings page).
  2. Click Authorize.
  3. Paste the token value (just the hgm_pat_… string, no Bearer prefix).
  4. Endpoints execute against /api/<path> through the UI nginx proxy.

curl / scripts ​

bash
curl -H "Authorization: Bearer hgm_pat_xxxx…" \
     https://hegemony.example.com/api/runs

Postman ​

Set the request Authorization type to Bearer Token and paste the token.

Lifecycle and rotation ​

  • Tokens are revocable at any time from Settings → API Tokens.
  • Rotate by issuing a new token first, switching consumers, then revoking the old token.
  • For a compromised owner account, use DELETE /api-tokens/by-user/{username} to revoke everything they hold.

Known limitations ​

  • Owner usernames are free-text; they are not validated against Keycloak. This keeps the feature self-contained but means typos go unnoticed.
  • PATs do not surface in Keycloak — last_used_at, audit events, and revocation are tracked entirely in the Hegemony DB.
  • There is no per-token rate limiting.

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