Skip to main content
Version: Latest

Audit

Every policy decision in PBAC is recorded — who requested access, what they requested, what OPA decided, and why. The audit log is append-only and immutable. It answers the question every regulator asks: who accessed what, when, and under what authority?


What gets logged

PBAC logs every policy evaluation at every OAuth decision point. Both allow and deny outcomes are recorded.

Decision pointEndpointWhen
Token issuancePOST /tokenEvery token request
AuthorizationGET /authorizeEvery authorize redirect
IntrospectionPOST /introspectEvery token validation
RegistrationPOST /registerEvery DCR request
CallbackGET /callbackEvery IdP callback

Each audit entry captures three layers of context:

FieldWhat it contains
endpointThe request path (/token, /introspect, etc.)
outcomeallow, deny, or error
clientIdThe OAuth client_id of the requester
subjectIdThe user's sub claim (empty for machine-to-machine flows)
requestSummaryKey request fields — grant type, auth method, token presence
policyInputThe full JSON input that OPA evaluated
policyDecisionThe full JSON output from OPA — scopes, obligations, deny reasons
ipClient IP address
userAgentUser-Agent header
createdAtTimestamp (ISO 8601)

The policyInput and policyDecision fields are the critical pair: they record exactly what OPA saw and exactly what OPA decided, making every decision fully reproducible.


Immutability

The audit log is append-only. Entries cannot be modified or deleted through the Admin API or admin UI. This is a design constraint, not a limitation — compliance frameworks (HIPAA, SOC 2, FedRAMP) require that audit trails be tamper-evident.


Bundle audit

Separate from the request audit log, PBAC tracks policy bundle deployments — when the OPA bundle changed, what version was deployed, and what triggered the change. This answers a different compliance question: what policy was in effect at the time of a given decision?

Bundle audit entries record:

FieldPurpose
versionBundle version identifier
digestContent hash of the bundle
sourceHow the change was deployed (API, git, manual)
timestampWhen the bundle was built and served

Combined with the request audit log, you can reconstruct the full picture: this decision was made at this time, under this policy version, with this input, producing this output.


Querying

The audit log is queryable via the Admin API with filters for any combination of:

  • Endpoint — show only /token or /introspect entries
  • Client — show decisions for a specific client_id
  • Subject — show decisions for a specific user
  • Outcome — show only denials, or only allows
  • Date range — entries between two timestamps

Results are paginated and sorted by timestamp (most recent first). Individual entries can be fetched by ID to retrieve the full policyInput and policyDecision JSON.

The admin UI provides the same filters with a visual interface, including formatted JSON views of the policy input and decision for each entry.


Compliance use cases

FrameworkWhat audit provides
HIPAAWho accessed patient data, under what authority, with full policy context
SOC 2Continuous evidence of access control enforcement and change tracking
FedRAMPDecision-level logging with immutable trails for federal workloads
GDPRRecord of lawful basis for data access (consent, legitimate interest) via policy data
PIPEDACanadian privacy law compliance — consent-based access logged with full decision context

The audit log is not just for compliance reviews. It's operational: when a user reports they can't access a resource, the audit log shows the exact OPA input and output for their request — including which rule denied them and why.


Next steps

  • Policy Model — How the policy decisions that get audited are made
  • Architecture — Where audit fits in the overall system