Skip to main content

Application Integration (PEP)

The Policy Enforcement Point (PEP) is your application, gateway, or middleware. It builds an input document, asks the PDP for a decision, and enforces allow or deny before the request proceeds.

EnforceAuth governs policy lifecycle and evidence — not runtime enforcement. This guide covers the PEP side.

Request flow

HTTP request


┌─────────┐ input JSON ┌─────────┐
│ App PEP │───────────────►│ OPA PDP │
│ │◄───────────────│ │
└─────────┘ allow / deny └─────────┘


Handler or 403

For deployment topology (sidecar vs centralized vs SDK), see Deployment patterns.

Agree on a decision path

Pick one Rego entrypoint and use it everywhere in a system. A common convention:

package authz

default allow := false

allow if {
# your rules
}

Query path: authz/allow → HTTP POST /v1/data/authz/allow.

Structured decisions are also fine (authz/decision returning an object) — see Rego patterns. The PEP and PDP must agree on the path; EnforceAuth decision logs store whichever path OPA evaluated.

Standard input shape

There is no single mandatory schema — design input for your domain. Most teams include:

FieldPurpose
user / principalIdentity, roles, attributes
actionVerb being authorized (read, delete, invoke)
resourceTarget object (id, type, owner, classification)
contextRequest metadata (IP, time, tenant)

Example input.json:

{
"user": {
"id": "user-42",
"roles": ["editor"],
"department": "finance"
},
"action": "read",
"resource": {
"type": "report",
"id": "rpt-9",
"classification": "confidential"
},
"context": {
"tenant_id": "org-acme",
"request_id": "req-abc"
}
}

Document the schema in your policy repo. The PEP and policy authors share ownership of this contract — see Policy authoring.

REST API (any language)

With OPA listening on localhost:8181 (sidecar or daemon):

curl -s -X POST http://127.0.0.1:8181/v1/data/authz/allow \
-H "Content-Type: application/json" \
-d @input.json

Response when allow is true:

{
"result": true
}

When denied, result is false (or absent if you use default allow := false).

For object-shaped decisions at data.authz.decision:

curl -s -X POST http://127.0.0.1:8181/v1/data/authz/decision \
-H "Content-Type: application/json" \
-d '{"input": { ... }}'

Official reference: OPA REST API.

Node.js example

async function isAllowed(input) {
const opaUrl = process.env.OPA_URL ?? 'http://127.0.0.1:8181';
const res = await fetch(`${opaUrl}/v1/data/authz/allow`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input }),
});
if (!res.ok) {
throw new Error(`OPA returned ${res.status}`);
}
const body = await res.json();
return body.result === true;
}

Call from Express/Fastify middleware before route handlers. Pass the same input OPA will log when the decision_logs plugin is enabled.

Go example (HTTP)

type opaResponse struct {
Result bool `json:"result"`
}

func allowed(ctx context.Context, opaURL string, input map[string]any) (bool, error) {
payload, _ := json.Marshal(map[string]any{"input": input})
req, _ := http.NewRequestWithContext(ctx, http.MethodPost,
opaURL+"/v1/data/authz/allow", bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()

var out opaResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return false, err
}
return out.Result, nil
}

For in-process evaluation without HTTP, use the OPA Go SDK with a bundle loaded from your EnforceAuth deployment artifact.

Where to enforce

LayerTradeoff
MiddlewareCentralized, easy to test; must forward enough context in input
Per-handlerFine-grained; risk of inconsistent input shapes
API gatewayUniform for north-south traffic; harder to pass rich resource attributes
Service meshStrong for east-west; policy input comes from mesh metadata

Pick one primary layer per traffic direction. Duplicate checks with different input shapes confuse decision log analysis.

Fail open vs fail closed

When the PDP is unreachable:

StrategyWhen
Fail closed (deny)Default for security-sensitive systems
Fail open (allow)Rare — only with explicit risk acceptance and monitoring

Log PDP errors regardless. EnforceAuth ingests successful evaluations via decision_logs; PDP outages appear as gaps in coverage — monitor fleet status when using status:write.

Bundle server down is different: OPA typically keeps serving the last activated bundle — authorization continues with possibly stale policy. See Bundle polling — failure modes.

AuthZEN and future engines

EnforceAuth's control plane is AuthZEN-oriented — PEP integration patterns stay stable as you adopt Cedar or other engines. Today, OPA PEPs use the REST shape above. See Authorization engines landscape.

Verify end to end

  1. Deploy policy via EnforceAuth → bundle lands at bundle destination.
  2. PDP pulls bundle (PDP integration).
  3. PEP sends traffic → decisions appear in console Decisions.
  4. Policy Coverage moves from Never evaluated to Evaluated.

Use EACommerce as a working reference or follow the 15-Minute Quickstart.

Next