Skip to main content
Version: 1.0

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_rule table, each placed at its configured path within the bundle
  • Policy data — rows from the policy_data table, merged into data.json; a data.oauth_config key is synthesized from data.oauth to 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.

Start OPA first

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

ClientOPA PathTriggered by
OpaTokenClient/v1/data/oauth/token (default, configurable)/token, /introspect
OpaAuthorizeClient/v1/data/oauth/authorize/authorize
OpaRegisterClient/v1/data/oauth/register/register (DCR)
OpaDirectClientconfigurable 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