AI Agent Permission Model: User Identity, Tool Access, Audit Logs, and Secret Isolation

"MCP Security Best Practices describes token passthrough as an anti-pattern and recommends least-privilege scopes, server-side authorization, and auditable elevation flows."
A team gives the same admin token to an agent because “it is all internal anyway.” Then user A submits a query, and the agent reads user B’s CRM record under the admin identity. Broken permissions are worse than having no agent at all.
This is not a made-up edge case. MCP Security Best Practices explicitly treats token passthrough as an anti-pattern because it bypasses security controls, breaks the audit trail, and crosses trust boundaries. The OWASP AI Agent Security Cheat Sheet also lists tool abuse and privilege escalation as core risks.
The problem comes down to three questions: who does the agent represent, what authorizes the call, and what can it access? The blueprint below covers the full engineering model: the identity mapping table, tool permission fields, the core Secret Vault flow, an audit log schema with redaction rules, a permission decision table, a troubleshooting checklist, and an implementation path.
Identity mapping: who does the agent represent?
When an agent calls a tool, the logging and authorization systems first need to answer one question: who initiated the call, and who is being represented? Those two entities may be the same, or they may be different. Mixing them up leads to permission drift and unusable audit logs.
Identity type reference table
| Type | actor | subject | When to use it | Permission boundary |
|---|---|---|---|---|
| user identity | User A | User A | Direct user interaction | Inherits the user’s permissions |
| service account | system_bot | null | Background jobs and scheduled tasks | System-level permission, independent of any user |
| delegated token | workflow_123 | User A | User-authorized automation workflows | Workflow scope, limited by the user’s grant |
| tenant context | agent_456 | tenant_B | Multi-tenant systems | Tenant isolation; no cross-tenant access |
Field definitions: actor is the entity that initiates the call, such as a user, agent, workflow, or system. The log records the actor ID. subject is the entity being represented, either a user or null. During direct user interaction, actor=subject. When a service account runs a background job, subject=null. delegatedBy identifies which user authorized the workflow. tenantId identifies the tenant and enforces data isolation in multi-tenant systems.
Under the MCP Authorization specification, MCP servers must verify that an access token was issued for the server as the intended audience. The token audience must point to that MCP server’s resource identifier. Tokens should not be placed in a URI query string because URIs can appear in logs, browser history, and proxy caches.
The OWASP Access Control Cheat Sheet emphasizes deny by default, least privilege, and checking permissions on every request. Identity mapping is the first step in that check: actor, subject, and tenantId determine the authorization decision that follows.
Tool permissions: what may the agent call?
Tool registration is not only name, description, and input_schema. The OpenAI Agents SDK tool reference includes fields that control permission and execution behavior.
Tool permission decision table
| Permission control | When to use it | Implementation | Risk |
|---|---|---|---|
| per-tool permission | Each tool needs separate authorization | Set permission_level, such as read/write/admin, when registering the tool | Permission configuration becomes more complex; you need to maintain a matrix |
| scope minimization | Progressive least privilege | Start with low-risk scopes, then expand high-privilege operations through scope challenges | Scope management costs more and may need dynamic adjustment |
| whitelist | Tool allowlist | Allow only specific tool combinations, such as read_customer + summarize | Allowlist maintenance cost, with possible loss of flexibility |
| approval | Human approval | Tools with needs_approval=true pause before execution and wait for approval | Approval adds latency and affects user experience |
OpenAI Agents SDK tool fields include is_enabled for runtime enablement control, so a tool can be disabled dynamically based on user role, tenant, or workflow context. needs_approval marks a tool that requires human approval. After approval, tool_input_guardrails still run. tool_input_guardrails validate inputs, such as PII checks and parameter bounds. tool_output_guardrails validate outputs, such as content filtering.
MCP Security Best Practices recommends progressive least privilege for scope minimization: the initial scope should include only low-risk discovery or read operations, such as read:metadata and list:resources. Higher-privilege operations should be added through precise scope challenges. Avoid wildcard and full-access scopes.
The OWASP AI Agent Security Cheat Sheet recommends per-tool permission scoping: use different tool sets for different trust levels, require explicit authorization for sensitive operations, and fail closed when authorization fails.
Secret isolation: how should the agent access credentials?
An agent should not directly hold long-lived plaintext API keys. The OWASP Secrets Management Cheat Sheet recommends centralized and standardized secret management. A secret management system should also support authentication, authorization, accounting, and lifecycle controls.
Secret access pattern table
| Pattern | Risk | When to use it | Example |
|---|---|---|---|
| Direct possession, such as plaintext .env | High leak risk, no attribution, no revocation | Not recommended | Hard-coded API key |
| Environment variables | Log leakage risk, still weak on attribution and revocation | Single-machine deployment | process.env.API_KEY |
| secret vault | Centralized management, encrypted storage, audit trail, revocation | Production systems | AWS Secrets Manager, HashiCorp Vault |
| secret reference | The agent holds a reference and exchanges it for a short-lived token at execution time | Multi-tenant and high-security systems | vault.get(secretRef) |
The secret lifecycle has four stages: creation should generate short-lived tokens instead of long-lived keys; rotation should happen on a schedule, such as every 30 days, with an automated process that updates the secret and notifies dependent systems; revocation should provide an emergency disable path so a leaked secret can be blocked immediately; expiration should set an expiry time so the credential stops working automatically.
MCP Security Best Practices explicitly says token passthrough is an anti-pattern: passing a user’s OAuth token directly to an agent bypasses security controls, breaks the audit trail, and crosses trust boundaries. The safer design is to issue a delegated token when the user authorizes the agent: short-lived, limited in scope, and explicit about its audience.
The core OWASP Secrets Management principles are centralize, least privilege, automate, and auditing. Secret access should follow least privilege. Manual maintenance increases leak and error risk, while rotation, revocation, and expiration are part of the lifecycle.
Audit logs: who called what, and when?
Audit logs need to reconstruct “who called which tool on behalf of whom, which object was accessed, and what happened” while redacting parameters and secrets.
Audit Log Schema
| Field | Meaning | Redaction rule |
|---|---|---|
| traceId | Call-chain ID, reusing the trace/runId concept from N156 | Do not redact |
| timestamp | Call time in ISO 8601 | Do not redact |
| actor | Entity that initiated the call | Do not redact |
| subject | Entity being represented | Do not redact |
| tool | Tool name | Do not redact |
| action | Operation type, such as read/write/delete | Do not redact |
| resource | Target object | Redact: customer_id → cust_*** |
| outcome | Result, such as success/failure/denied | Do not redact |
Redaction rules: do not record tokens, secrets, passwords, email addresses, phone numbers, or PII. Record who/what/when/where/outcome. For example, store customer_id=12345 as cust_, email=[email protected] as e@***.com, token=Bearer xxx as Bearer ***, and do not record password=secret123 at all.
The OWASP Logging Cheat Sheet says security logs should support investigation, audit, and monitoring, but should not record passwords, session IDs, access tokens, or sensitive personal data. They should record traceable fields such as who, what, when, where, and outcome.
The audit and accountability control family in NIST SP 800-53 reinforces a useful design point: audit logs are the last line of defense in a permission system. When an authorization check fails, the log must record the reason, such as actor has no permission, subject has no permission for the target, or scope is insufficient.
Permission model decision table: choose the right control mix
Identity mapping, tool permissions, Secret isolation, and audit logs are not separate checkboxes. They constrain each other. The table below maps common scenarios to control combinations.
| Scenario | identity type | tool permission | secret access | audit log | Typical application |
|---|---|---|---|---|---|
| Low-risk internal tool | service account | whitelist, read tools only | Environment variables | actor/tool/outcome | Internal report generation, scheduled sync |
| Multi-tenant SaaS | delegated token + tenantId | per-tool permission with tenant filtering | secret vault with tenant isolation | full schema with tenantId | CRM agent, email assistant |
| Financial transaction | user identity + approval | scope minimization + approval | secret reference with short-lived token | full schema + approvalId | Trade approval, funds movement |
| Sensitive data operation | delegated token + approval | whitelist + approval + guardrails | secret vault with emergency revocation | full schema + redaction | Data export, customer lookup |
The OWASP AI Agent Security Cheat Sheet recommends separate tool sets for different trust levels and explicit authorization for sensitive operations. The core idea behind the decision table is composition: high-risk scenarios need layered controls, not one single control pretending to cover everything.
Troubleshooting checklist: common permission symptoms
These are common symptoms, likely causes, checks, and fixes for agent permission problems.
| Symptom | Likely cause | What to check | Fix |
|---|---|---|---|
| The agent gets 403 Forbidden when calling a tool | actor has no tool permission, or subject has no target permission | Check the actor permission_level and the subject’s resource permission | Confirm identity mapping and adjust the permission matrix |
| Logs show an empty actor or confused subject | Identity mapping fields are not being passed correctly | Check whether the agent context contains actor/subject/tenantId | Pass identity fields through the whole call chain |
| Tool call succeeds, but the audit log misses required fields | Audit Log Schema is incomplete | Check whether the log writer includes every field | Complete the schema and add traceId/approvalId |
| User A’s request can read user B’s data | tenantId or subject is not isolated, or an admin token is shared | Check whether delegated tokens are used and tenantId is correct | Use delegated tokens and enforce tenantId validation |
| After secret rotation, the agent still uses the old key | Secret reference was not updated, or rotation did not take effect | Check whether the vault returns the new secret and whether the agent fetches it again | Make rotation update the reference automatically |
| After approval, the tool call still fails | Guardrails failed because of out-of-bounds parameters or PII detection | Check the tool_input_guardrails logs | Adjust the parameters or guardrail rules |
Implementation checklist: build an agent permission model from zero
These are the five core steps for putting the permission model in place.
Step 1: Define identity mapping rules
Decision points: do you need multi-tenant isolation, which means adding tenantId? Do you have background jobs, which means defining a service account? Do you have automation workflows, which means using delegated tokens?
Pseudocode:
interface IdentityContext {
actor: string; // Entity initiating the call
subject: string | null; // Entity being represented
delegatedBy?: string; // Delegation source
tenantId?: string; // Tenant identifier
}
Step 2: Design the tool permission matrix
Decision points: do you need approval, which means needs_approval=true? Do you need dynamic filtering, which means implementing runtime is_enabled checks? Do you need parameter validation, which means implementing tool_input_guardrails?
Code example:
interface ToolPermission {
name: string;
permission_level: 'read' | 'write' | 'admin';
required_scope: string[];
needs_approval: boolean;
is_enabled: (context: IdentityContext) => boolean;
}
Step 3: Connect a secret vault
Decision points: do you need short-lived credentials, which means using a secret reference? Do you need emergency revocation, which means ensuring the vault can disable access immediately?
Code example:
async function getSecret(secretRef: string, context: IdentityContext): Promise<string> {
// Validate identity
await vault.authenticate(context.actor);
// Validate permission
await vault.authorize(context.actor, secretRef);
// Get a short-lived token
const token = await vault.getToken(secretRef, expiresIn: '15m');
// Record audit event
await auditLog.record({
actor: context.actor,
action: 'get_secret',
resource: secretRef,
outcome: 'success'
});
return token;
}
Step 4: Implement audit logs
Decision points: do you need redaction, which means implementing redaction rules? Do you need traceId, which means reusing the trace/runId from N156?
Code example:
interface AuditLogEntry {
traceId: string;
timestamp: Date;
actor: string;
subject: string | null;
tool: string;
action: 'read' | 'write' | 'delete';
resource: string; // Redacted
outcome: 'success' | 'failure' | 'denied';
}
Step 5: Test permission boundaries
Decision points: will you test unauthorized access, such as user A trying to access user B’s data? Will you test token leakage by simulating revocation after a secret leak? Will you test audit traceability by replaying the full call chain through traceId?
Test checklist: unauthorized access test (actor=user_A, resource=tenant_B → should return 403); token leak test (vault.revoke(secretRef) → the agent should no longer be able to get a new token); audit trace test (query the full call chain by traceId → it should include actor/subject/tool/outcome).
Next steps: further reading
An agent permission model spans identity, tools, Secrets, and audit. These related pieces are worth reading next.
Published articles:
- Agent Sandbox Guide: Sandbox solves runtime isolation with containers and Docker. This article covers permission and secret boundaries; the two are complementary.
- Tool Calling in Practice: The basics of tool calling. This article extends them with tool allowlists, per-tool permissions, and input validation.
- AI Agent Monitoring and Recovery: Monitoring and alerting basics. This article adds audit fields and traceId.
Design an AI agent permission model
Design user identity, tool permissions, secret access, and audit logging for a production agent system.
- 1
Step 1: List tools and resources
List the tools, resources, actions, and external systems the agent can touch. Separate read-only operations from write, send, delete, and financial actions. - 2
Step 2: Define the identity context
For every run, define actor, subject, tenant, workflow, and traceId so that user identity, service accounts, and automation workflows do not collapse into one admin identity. - 3
Step 3: Separate identity types
Separate delegated user identity, service accounts, and system maintenance jobs, then define resource boundaries and audit fields for each. - 4
Step 4: Build the tool permission matrix
For every tool, define action, resource, scope, approval, secret, and audit metadata, then run server-side authorization before execution. - 5
Step 5: Connect a secret vault
Store secrets in a vault or credential service, exchange them for short-lived credentials only at the execution layer, and support rotation, revocation, and expiration. - 6
Step 6: Fail closed
Before the tool gateway executes anything, check actor, subject, resource, action, scope, and approval. Reject the call explicitly whenever a check fails. - 7
Step 7: Write redacted audit logs
Record who, what, when, where, outcome, traceId, approvalId, and a redacted resource summary. Add alerts for permission changes, scope elevation, and secret access.
FAQ
When an agent calls a tool, does it represent the user, a service account, or the workflow itself?
Why do I still need per-tool permissions after OAuth authorization succeeds?
Can one admin token let an agent query data for every user?
Can an agent read .env files or user API keys directly?
After approval, can I keep reusing the same high-privilege token?
Should audit logs store parameters, and how do I avoid logging tokens, email addresses, or customer data?
11 min read · Published on: Sep 17, 2026
AI Agent Engineering: Architecture, Evaluation, and Recovery
If you landed here from search, the fastest way to build context is to jump to the previous or next post in this same series.
Previous
AI Agent Cost Control: Model Routing, Tool Budgets, Caching, and Retry Limits
A practical guide to controlling AI agent costs with budget objects, model routing, tool-call limits, prompt caching, Batch/Flex paths, retry circuit breakers, cost logs, and alert fields.
Part 20 of 22
Next
AI Agent State Machine Design: Why Complex Workflows Cannot Rely on Prompts Alone
A practical guide to designing recoverable AI agent workflows with state, events, guards, actions, checkpoints, retries, compensation, approval pauses, and terminal states instead of prompt-only progress tracking.
Part 22 of 22



Comments
Sign in with GitHub to leave a comment