Skip to main content
Version: Latest

How it works

PBAC separates authorization from your applications so that one policy plane governs access across every user, service, and AI agent — with a complete audit trail.

PolicyArc is an authorization server. Clients request tokens. OPA decides what goes in them. Resource servers validate tokens via introspect. Every decision is logged.


Where PBAC fits in your stack

Your identity providers, applications, and AI agents stay where they are. PBAC is the authorization layer in between — with every decision logged for audit.


Architecture

Three actors:

  • Authorization Server (AS) — issues tokens, manages clients, serves policy bundles to OPA, provides the introspect and admin APIs.
  • OPA — evaluates Rego policy rules against JSON policy data. The AS calls OPA at every token, authorize, register, and introspect request. OPA decides; the AS enforces.
  • Resource Server (RS) — any API that accepts tokens from this AS. Validates tokens by calling /introspect. Enforces the decision and any obligations OPA returned.

This separation means you don't embed authorization logic in your applications. Your apps talk OAuth. OPA handles the rules. You change policy without touching application code.


The token lifecycle

Every access decision follows the same pattern:

  1. Client requests a token — via /token (machine-to-machine) or /authorize (user-interactive). The request includes grant type, scopes, and the target resource.
  2. AS calls OPA — OPA receives the full request context: who the client is, what it's asking for, its software statement claims, and environment data (IP, user agent).
  3. OPA evaluates — checks denylists, resolves client entitlements, validates grant types, evaluates per-resource rules and any custom extensions. Returns: allow/deny, granted scopes, and optional modifiers (TTL override, JIT single-use, obligations).
  4. AS issues the token (or rejects) — if allowed, the AS generates an opaque token, stores both the OPA input and output alongside it, and returns the token to the client.
  5. Client presents the token to an RS — standard Bearer token in the Authorization header.
  6. RS calls /introspect — passes the token plus transaction context: the specific resource and action being requested right now.
  7. OPA re-evaluates — same policy, but now with RS-supplied context merged in. Can narrow scopes further, add obligations, or deny based on the specific action.
  8. RS enforces — reads the introspect response: active (allow/deny), scope (what's permitted), and obligations (what else to do — audit, mask data, rate limit).

Policy is evaluated twice. Once at token issuance (can this client get a token at all? with what scopes?) and again at introspect (is this specific action on this specific resource allowed right now?). This is the key difference from systems that make one decision at login time.

For your RS integration, this means a single HTTP call (POST /introspect) gives you the full policy decision — no SDK, no library, no OPA client to maintain.


Access patterns

Machine-to-machine (client credentials)

For service accounts, background jobs, and API-to-API calls. No user involved.

User-delegated (authorization code)

For user-facing applications. User authenticates with an external Identity Provider.

OPA selects which IdP to redirect to — based on the requested resource, resource type, or ACR value. This is configured in policy data, not hardcoded.


What OPA sees

At every evaluation, OPA receives the full request context as input:

FieldWhat it contains
input.resources[]What the client is requesting: resource type, resource ID, actions (scopes)
input.subjectWho the user is: subject ID, IdP, ACR level, claims (only for user-delegated flows)
input.context.clientThe client: client_id, client_type, software_statement (all claims from DCR)
input.context.grant_typeWhich OAuth grant is being used
input.context.environmentIP address, user agent
input.context.rs_contextAt introspect: the RS-supplied transaction context (action, resource)

OPA evaluates this against policy data (data.oauth_config) and returns a decision. The AS never makes authorization decisions itself — OPA is the single decision point.

This is the full context OPA has at decision time. Your policy can use any of it — client identity, user claims, environment, transaction context. If you can express it as a Rego rule over this input, PBAC can enforce it.


Key principles

  • OPA decides, the AS enforces. The AS handles OAuth protocol mechanics. OPA handles authorization logic. They are separated so policy can change without code deployments.
  • Policy evaluated twice. At token issuance and at introspect. The second evaluation can narrow, add obligations, or deny based on what the RS reports about the actual request.
  • Everything is stored. Both the OPA input and output are persisted with every token. The audit log records every decision with full context.
  • Tokens are resource-bound. A token is always scoped to specific resource types and (optionally) specific resource servers. A token for one RS cannot be used at another.
  • Policy controls token behavior. OPA can override the token TTL, or mark a token as single-use (jit_single_use) so it is revoked after the first introspect. The RS doesn't need to know — it just sees active: true or active: false.

Next steps

  • Trust & Identity — How PBAC federates with IdPs and controls who is trusted
  • Clients & Access — How clients register, authenticate, and gain access
  • Policy Model — How OPA decides what's allowed
  • Token Lifecycle — How tokens flow from issuance through introspection
  • Audit — How every decision is recorded for compliance