All Posts

Where agent permissions come from

AI agents need room to act, and the source of permission matters. A tool call names the requested action. AgentTrust ID resolves the org, agent, tenant, session, and scope from credentials and server records before evaluating the request.

Permission source

Every authorization check has two parts: the subject and the requested action.

The subject answers who is acting: org, agent, session, token, and delegation. Those values need a trusted source.

The requested action answers what the agent wants to do: read a ticket, write a report, send a message, or call a tool. That part can come from the tool call.

Mixing those two parts creates the problem. The body can describe the action, but it should not be the source of authority.

How it goes wrong

A wrapper may pass model output straight into an authorization call:

{
  "agent_id": "admin-agent",
  "tenant_id": "customer-42",
  "scope": ["tickets:read", "reports:write", "users:delete"],
  "tool_name": "users:delete"
}

The request has an action in it. It also has a story about who the caller is and what the caller can do.

A prompt injection can ask the model to produce that story. A bug in a tool wrapper can copy it into the check. A delegated worker can claim the parent agent's scope.

The fix is to ignore caller-supplied identity fields when deriving authority. The request can carry the action. Credentials and server records decide the subject.

Python example

This flow issues an opaque token for a registered agent, introspects it, and checks an action for the agent returned by introspection. The code never trusts an agent_id value from the tool payload.

from agenttrustid import AgentTrustClient

client = AgentTrustClient.from_env()

agent = client.agents.create(
    name="reporting-agent",
    framework="custom",
    capabilities=["tickets:read", "reports:write"],
)

token = client.tokens.issue(
    agent_id=agent.id,
    scopes=["tickets:read"],
    ttl_seconds=300,
)

token_info = client.tokens.introspect(
    token.token,
    required_scopes=["tickets:read"],
)

if not token_info.active:
    raise PermissionError("inactive agent token")

tool_payload = {
    "claimed_agent_id": "admin-agent",
    "tool_name": "tickets:read",
    "summary": "Read ticket counts for the weekly report.",
}

decision = client.actions.check(
    agent_id=token_info.agent_id,
    tool_name=tool_payload["tool_name"],
    tool_input_summary=tool_payload["summary"],
    action_effect="read",
)

if not decision.allowed:
    raise PermissionError(decision.reason)

The token result identifies the agent. The API key identifies the org. The tool payload describes the requested work.

That distinction matters. claimed_agent_id is data. It is not authority.

Prevention

Resolve org and agent identity from the credential path. Resolve session scope from session creation, token scope, or delegation. Check the requested tool against that scope before execution.

AgentTrust ID has a session owner check for this. If a session belongs to agent A and a request explicitly claims agent B, the unified checker denies the request and records the reason.

Scope checks follow this rule. If a session ceiling allows tickets:read, then a request for users:delete should fail even when the request body names a broader scope.

Solving this with AgentTrust ID

AgentTrust ID ties action checks to org-scoped API keys, opaque token introspection, WIMSE workload identity, DPoP-bound runtime requests, sessions, and delegations.

The WIMSE exchange path takes agent and org from the verified token. The request body can narrow the issued access token through TTL and scopes. It cannot widen the agent, org, or scopes beyond what the verified token allows.

The action check path can derive the agent from a sender-constrained runtime credential. When DPoP is required, the request must include a proof bound to the agent's key. A request body cannot replace that proof.

Sessions add another boundary. The session records the owning agent and scope ceiling. A later request has to fit that session.

Prompt injection can change what the model asks for. It should not change which org the agent belongs to or which tools the platform recorded.

To keep permission checks outside the request body, start with the SDK guide. To talk through identity boundaries in your agents, join the waitlist.