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:
- 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 passcontext.time; if you do, your value is used instead (useful for testing, not recommended in production). The built-in Time Window guardrail and anytimeOfDayABAC conditions read this value — they work without any SDK change.
- Vengtoo-stored attributes —
subject.attributesandresource.attributespersisted 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. - Caller-sent context — Anything in the
contextobject 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
| Attribute | Source from | Never source from |
|---|---|---|
ip | X-Forwarded-For parsed at your edge / load balancer | request body |
time | server clock on your backend | request header or body |
mfa_verified | your IdP or session store, after verifying the user completed MFA | a raw boolean field on the request |
geo | server-side GeoIP lookup on the client IP, pass the resolved country code as context.geo | request body |
env, api_version | compile-time or config-loaded constant on your backend | request query/body |
Custom (session_risk, request_id, ...) | your backend's trusted internal state | anything 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.
| Attribute | Preferred source | Why |
|---|---|---|
subject.attributes.* | Stored in Vengtoo via the Management API | Stored values win on conflict — callers cannot override them |
resource.attributes.* | Stored in Vengtoo on the resource record | Same 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.
Related
- Policies — how ALLOW / DENY combine with context.
- Authorize API — the full request shape, including the
contextfield. - Subjects and Resources — the persisted-attribute side of the model.