Reviewed: 2026-09-06
An authorization event should explain how a decision was reached without reproducing the policy input. Record identifiers, versions, classes, and stable reason codes; keep secrets, raw attributes, relationship graphs, and personal data out. The most useful design separates developer diagnostics, security audit records, and customer-visible history because each audience needs different detail and retention.
Log the decision envelope
Start with a compact schema: event timestamp, decision ID, trace ID, actor class and pseudonymous identifier, action, resource class and pseudonymous identifier, tenant identifier, allow or deny, reason code, policy version, relationship-model version, enforcement point, and latency. Add the authentication assurance level or credential class only when it materially influenced the decision.
Use controlled reason codes such as NO_RELATIONSHIP, TENANT_MISMATCH, POLICY_CONDITION_FALSE, and STALE_CONTEXT. Do not log an evaluated expression containing department, salary band, medical status, or document title. The OWASP Logging Cheat Sheet recommends consistent application logging and collecting event data according to the intended purpose. That discipline is more useful than a free-form reason string.
Pseudonymization is not anonymity. A stable hash of a user ID may still be personal data and can be vulnerable to guessing when the input space is small. Prefer a keyed transform with controlled access and rotation, or an internal opaque identifier already designed for logging. Restrict who can reverse or join identities and document the retention purpose.
Connect events without copying context
Carry a correlation identifier through the request path. The W3C Trace Context specification defines standard HTTP headers for propagating context information in distributed tracing. Store the trace ID with the authorization event, then let an authorized engineer pivot to request traces. Do not put user identity or policy attributes into trace headers: they cross process and organizational boundaries more widely than a protected audit store.
Generate a separate decision ID for retries. That distinguishes three attempts inside one trace and lets the application include a safe support reference in an error response. A customer can report the reference; support tooling retrieves the authorized view without exposing internals in the UI.
Separate diagnostics, audit records, and customer history
Developer diagnostics answer why the code behaved as it did. They need versions, enforcement point, latency, and reason code, usually with short retention. Security audit answers who attempted which sensitive action and whether it succeeded. It needs stronger integrity, access control, time synchronization, retention, and export procedures. Customer history answers what changed in the customer’s domain and should use product language, such as “Export blocked because administrator approval is required.”
The OWASP Logging Cheat Sheet explicitly notes that process monitoring, audit, and transaction logs are usually collected for different purposes than security event logging and often should be kept separate. That is a practical reason to design different schemas and retention rules instead of trying to force one event stream to satisfy every audience.
Do not expose internal group names, policy source, hidden resource existence, or another tenant’s identifiers in a denial message. Also avoid collapsing every denial into “forbidden” in internal data. The response can remain deliberately vague while the protected event holds a precise reason code.
Test the negative space
Create fixtures containing the most sensitive possible attributes, then assert they never appear in application logs, traces, metrics labels, exception messages, or analytics. Test control characters and long values so attacker-controlled identifiers cannot forge records or explode cardinality. Sample successful low-risk decisions if volume demands it, but retain denied or high-impact events according to the audit policy.
Review the schema with four questions: Can an engineer distinguish policy, data, and infrastructure failures? Can a customer support case be traced by reference? Can an auditor reconstruct sensitive actions? Can a reader of the event learn anything they do not need?
Choose one high-volume authorization endpoint and replace its free-form log today. Define five reason codes, add policy and model versions, propagate the trace ID, and add a test proving that raw policy attributes never leave the decision boundary.
Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗