Skip to main content

Identity injection: agents that never hold the credential

Objective

Explain identity injection: how Verdict lets an agent act without ever holding the upstream credential, and why that shrinks the blast radius of a compromised agent compared with giving the agent a long-lived key.

Concept

The naive way to let an agent do real work is to give it credentials: an API key, a database password, a cloud role's secret. The agent keeps them and uses them whenever it decides to act. This is convenient and dangerous. The credential now lives inside a component that can be prompt-injected, jailbroken, or simply talked into misusing it. Anything that captures the agent captures the key, and the key is usually broad and long-lived.

Identity injection inverts that. The agent never holds the upstream credential. When a request is authorized, Verdict injects a short-lived, tightly-scoped identity for that specific action against the specific upstream system, and the action proceeds under that injected identity. The agent presents a request; the firewall, having decided the action is allowed, supplies the credential to the downstream call itself. The agent is a requester, not a key holder.

Two properties follow:

  1. No standing secret to steal. A compromised agent has nothing durable to exfiltrate. There is no long-lived key sitting in its context or memory. The injected identity exists only for an authorized action and expires.
  2. The credential is scoped to the decision. Because the identity is minted per authorized action, it can be narrow: this action, this resource, this short window. That is far tighter than a shared service account with broad, permanent rights.

This is a runtime mechanism inside the firewall, not something the policy engine does on the request path. The Policy Decision Point (OPA or Enterprise OPA) still answers the authorization question. Verdict is what acts on an allow by injecting the identity and carrying out the call, so the agent never needs the secret. Keep the boundary straight: the PDP decides, Verdict enforces and injects, and the agent holds nothing.

A related discipline in the policy is scoping. An approval, or an allow, should authorize a specific action, not a blanket capability. The lab's valid_approval requires input.approval.scope_action == input.action.name: an approval to send one wire does not authorize deleting a backup. Injected identities follow the same principle at the credential layer: narrow, specific, short-lived.

Note that the identity-injection flow is a Verdict runtime behavior. Verdict is "coming soon," so you cannot exercise the injection itself here. The lab exercises the authorization decision that precedes it: only an allowed, in-scope action ever reaches the point where an identity would be injected.

Hands-on lab

In the cloned labs repo, open courses/defend-with-verdict/policy.rego and read the # TODO(learner) that defines valid_approval, especially the scope_action check. You implement that rule in lesson 3; here, connect the test intent to identity injection.

git clone https://github.com/EnforceAuth/university-labs.git
cd university-labs
opa test courses/defend-with-verdict -v

Read test_high_risk_action_denied_on_scope_mismatch. The approval was granted for read_matter, but the agent is attempting send_wire_transfer. Once valid_approval is correct, the scope check fails and the action is denied. Connect this to identity injection: because the decision is scoped to one named action, the credential that would be injected on an allow is scoped too. There is no broad, reusable grant for an attacker to ride. A blanket "the agent may act" would break exactly this protection.

Check for understanding

  1. What does identity injection prevent an agent from ever holding, and why does that shrink the blast radius of a compromised agent?
  2. Which component makes the authorization decision, and which component injects the short-lived identity and carries out the call? Keep the boundary straight.
  3. In the lab, why does an approval scoped to read_matter fail to authorize send_wire_transfer, and how does that map to keeping an injected identity narrow?