Skip to main content

Context Trust Model

Context attributes are the request-time values Vengtoo evaluates alongside subject and resource data. Unlike subject.attributes or resource.attributes — which are persisted in the Vengtoo control plane and gated behind write permissions — context is attached to each individual /access/v1/evaluation 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 (Vengtoo-owned) — Values Vengtoo generates itself. Nothing in the request can change them.
    • time — stamped automatically by Vengtoo from its own server clock on every evaluation call. You do not need to pass context.time; if you do, your value is used instead (useful for testing, not recommended in production). The built-in Time Window guardrail and any timeOfDay ABAC conditions read this value — they work without any SDK change.
  2. Vengtoo-stored attributessubject.attributes and resource.attributes persisted in Vengtoo's control plane. Trusted because write access requires a valid API key or client credentials. When the same attribute is also passed in the evaluation request, the stored value wins — request-time attributes supplement stored ones but cannot override them. This means policies always evaluate against the authoritative value in the control plane, not whatever the caller sends.
  3. Caller-sent context — Anything in the context object of the evaluation 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) Vengtoo Agent
┌────────┐ HTTP request ┌────────────────────┐ /access/v1/evaluation ┌──────────────────────┐
│ browser│ ──────────────► │ your service │ ──────────────► │ Vengtoo 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 Vengtoo 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 backendrequest header or body
mfa_verifiedyour IdP or session store, after verifying the user completed MFAa raw boolean field on the request
geoserver-side GeoIP lookup on the client IP, pass the resolved country code as context.georequest 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

A complete production context block, for reference:

"context": {
"ip": "203.0.113.42",
"geo": "US",
"env": "production",
"mfa_verified": true,
"session_risk": "low"
}

time is omitted here intentionally — Vengtoo stamps it automatically.

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)

vengtoo.Authorize(ctx, vengtoo.AuthorizeRequest{
Subject: vengtoo.Subject{ID: userID},
Resource: vengtoo.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

// mfa_verified comes from your own IdP/session store, not from the request
mfaVerified := session.MFAVerified(userID)

vengtoo.Authorize(ctx, vengtoo.AuthorizeRequest{
Subject: vengtoo.Subject{ID: userID},
Resource: vengtoo.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 vengtoo.authorize({ ..., context: { env: req.body.env } })

// Good
const env = process.env.APP_ENV ?? "production"
await vengtoo.authorize({
...,
context: {
env,
mfa_verified: await session.isMFAVerified(req.user.id), // from your IdP/session store
ip: req.ip, // Express with `trust proxy` configured
},
})

Subject and resource attributes

subject.attributes and resource.attributes can be passed in the evaluation request or stored in Vengtoo. When both exist, the stored value wins.

AttributePreferred sourceWhy
subject.attributes.*Stored in Vengtoo via the Management APIStored values win on conflict — callers cannot override them
resource.attributes.*Stored in Vengtoo on the resource recordSame guarantee — stored value is authoritative

Request-time attributes are merged with stored values and are useful for passing attributes that aren't stored in Vengtoo (e.g., a session-computed risk score). If the same key appears in both, the stored value takes precedence. This prevents a caller from escalating their own attributes to bypass a policy condition.

Design principle

For attribute-based evaluation, Vengtoo evaluates rules against what your backend sends — your backend assembles the domain context, Vengtoo renders the verdict. This keeps attribute checks fast and free of relational complexity. Your backend already knows which department a subject belongs to, which tier a resource is in, whether MFA was completed — it passes those facts in; Vengtoo applies the rule.

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.