Skip to main content

Context Trust Model

Context attributes are the request-time values AuthzX evaluates alongside subject and resource data. Unlike subject.attributes or resource.attributes — which are persisted in the AuthzX control plane and gated behind write permissions — context is attached to each individual /v1/authorize call and discarded once the decision is returned.

That makes context powerful (it can express things like "the user's current IP" or "the current time") and also dangerous: if an attacker can influence a context value, they can influence the decision. This page explains the trust hierarchy so you source each attribute from a place your policies can safely rely on.

The trust hierarchy

From most trusted to least trusted:

  1. Server-derived (engine computed) — Values the AuthzX Agent generates itself. Nothing in the request can change them.
    • time — read from the agent's server clock.
    • ip — taken from the TCP connection to your backend.
    • geo — resolved server-side from ip via a country lookup.
  2. Signed JWT claims — Values extracted from a JWT whose signature the agent has verified against the configured IdP.
    • mfa_verified, sub, email, custom claims.
  3. Server-stored attributessubject.attributes and resource.attributes persisted in AuthzX. Trusted because write access to them is itself gated by policy.
  4. Caller-sent custom context — Anything in the context object of the authorize request. Trusted only when your backend sets it from trusted state. Never trusted when it originates from an end-user request body.

The request flow

end user your backend (PEP) AuthzX Agent
┌────────┐ HTTP request ┌────────────────────┐ /v1/authorize ┌──────────────────────┐
│ browser│ ──────────────► │ your service │ ──────────────► │ AuthzX Agent │
│ mobile │ │ - verify JWT │ │ - evaluate policies │
│ etc. │ │ - build context │ │ - return decision │
└────────┘ └────────────────────┘ └──────────────────────┘

Your backend is the Policy Enforcement Point (PEP) — it's the trust boundary where you verify the caller, build the context, and enforce the decision. The AuthzX Agent is the authorization engine — it evaluates and returns the verdict.

The end user never talks to the agent directly. Your backend decides which values survive the crossing.

Per-attribute sourcing

AttributeSource fromNever source from
ipX-Forwarded-For parsed at your edge / load balancerrequest body
timeserver clock on your backend or the PDPrequest header or body
mfa_verifiedverified JWT claima raw boolean field on the request
geoserver-side IP → country lookuprequest body
env, api_versioncompile-time or config-loaded constant on your backendrequest query/body
Custom (session_risk, request_id, ...)your backend's trusted internal stateanything the caller controls

Bad: echoing user input into context

// DON'T — attacker sets env=dev and bypasses a production-only DENY policy
var body struct {
Env string `json:"env"`
}
_ = json.NewDecoder(r.Body).Decode(&body)

authzx.Authorize(ctx, authzx.AuthorizeRequest{
Subject: authzx.Subject{ID: userID},
Resource: authzx.Resource{ID: resourceID},
Action: "read",
Context: map[string]any{"env": body.Env}, // untrusted!
})

Good: sourcing from trusted backend state

// DO — env is a process-level constant, not caller input
const env = "production" // or loaded from your deployment config

mfaVerified, _ := claims["mfa_verified"].(bool) // from a verified JWT

authzx.Authorize(ctx, authzx.AuthorizeRequest{
Subject: authzx.Subject{ID: userID},
Resource: authzx.Resource{ID: resourceID},
Action: "read",
Context: map[string]any{
"env": env,
"mfa_verified": mfaVerified,
"ip": realClientIP(r), // from X-Forwarded-For at the edge
},
})

Same pattern in Node:

// Bad
await authzx.authorize({ ..., context: { env: req.body.env } })

// Good
const env = process.env.APP_ENV ?? "production"
await authzx.authorize({
...,
context: {
env,
mfa_verified: req.user.claims.mfa_verified === true,
ip: req.ip, // Express with `trust proxy` configured
},
})

Rule of thumb

If a policy would behave differently for env=dev vs env=prod, ask: can the user send a request that flips env to dev? If yes, you have a trust bug in your PEP, not a policy bug.

  • Policies — how ALLOW / DENY combine with context.
  • Authorize API — the full request shape, including the context field.
  • Subjects and Resources — the persisted-attribute side of the model.