OPA Setup
The PBAC authorization server integrates with OPA as an external sidecar process. OPA is not embedded; it runs separately and the AS calls it via HTTP at evaluation time.
Integration architecture
The AS and OPA interact in two directions:
┌─────────────────────────────┐
│ Authorization Server (AS) │
│ │
│ Bundle Server │◄── OPA polls GET /bundles/pbac/bundle.tar.gz
│ (policy_rule + policy_data)│
│ │
│ OPA Clients │──► POST /v1/data/oauth/token (token issuance)
│ - OpaTokenClient │──► POST /v1/data/oauth/authorize (authorize)
│ - OpaAuthorizeClient │──► POST /v1/data/oauth/register (DCR)
│ - OpaRegisterClient │──► POST /v1/data/... (AuthZEN)
│ - OpaDirectClient │
└─────────────────────────────┘
│ │
└────── OPA (sidecar) ────┘
localhost:8181
Bundle server (push): The AS builds and serves a gzipped tarball at GET /bundles/pbac/bundle.tar.gz. OPA polls this endpoint on a configurable interval. The bundle endpoint requires the X-Bundle-API-Key header; configure OPA with PBAC_BUNDLE_API_KEY (see opa/config.yaml). The bundle contains:
- Rego policies — rows from the
policy_ruletable, each placed at its configured path within the bundle - Policy data — rows from the
policy_datatable, merged intodata.json; adata.oauth_configkey is synthesized fromdata.oauthto prevent Rego recursion
Direct evaluation (pull): At token issuance, authorization, DCR, and introspection time, the AS calls OPA's Data API (POST /v1/data/<path>) with request context as input and receives a policy decision.
Starting OPA
OPA_DATA=integration-tests/data.json ./opa/run_opa.sh
OPA_DATA sets the path to an initial data file loaded at startup. For local development, integration-tests/data.json contains client and resource server fixtures used by the integration test suite.
OPA starts listening on http://localhost:8181.
OPA must be running before the AS starts. The AS calls OPA during startup validation and on every token, authorize, and introspect request. If OPA is unavailable, those endpoints will fail.
Policy and data loading
policy_rule table → Rego files
Each row in the policy_rule table has a path (e.g. oauth/token.rego) and a body (Rego source). The bundle server places each body at its path inside the tarball. OPA loads these as standard Rego modules.
policy_data table → data.json
Each row in the policy_data table has a key and a value (JSON). The bundle server merges all rows into a single data.json. Keys map to data.<key> in OPA's data namespace.
A special data.oauth_config key is automatically synthesized from data.oauth to prevent infinite recursion in the token and RAR policies.
OPA client paths
| Client | OPA Path | Triggered by |
|---|---|---|
OpaTokenClient | /v1/data/oauth/token (default, configurable) | /token, /introspect |
OpaAuthorizeClient | /v1/data/oauth/authorize | /authorize |
OpaRegisterClient | /v1/data/oauth/register | /register (DCR) |
OpaDirectClient | configurable per request | /access/v1/evaluation (AuthZEN) |
OPA URL configuration
Set OPA_URL to point the AS at a non-local OPA instance:
OPA_URL=http://opa-sidecar:8181
In Kubernetes, this is typically the cluster-internal service address of an OPA sidecar container or a dedicated OPA deployment.
Next steps
- Configuration — All environment variables including OPA URL and bundle API key
- Kubernetes — Deploy the AS and OPA sidecar to Kubernetes
- Troubleshooting — Diagnose bundle loading failures and policy evaluation issues