AI agents are increasingly being used to perform actions across multiple business systems.
An agent may receive a request from a user, call an internal API, retrieve customer information, invoke another service, and continue the workflow without direct interaction from the user.
In a single-tenant application, this can already be challenging.
In a multi-tenant system, the security model becomes significantly more important.
Consider this flow:
User
|
v
AI Agent
|
+----> Customer API
|
+----> Billing API
|
+----> Document API
|
+----> Reporting API
The critical question is:
Which identity should each downstream service trust?
If every request uses the same application identity, downstream services may lose the ability to distinguish users and tenants.
A stronger architecture preserves the original user's security context while allowing the agent to act on the user's behalf.
This is where On-Behalf-Of (OBO) token exchange becomes useful.
The Multi-Tenant Agent Identity Problem
Imagine an application serving three organizations:
Tenant A
--------
User A1
User A2
Tenant B
--------
User B1
User B2
Tenant C
--------
User C1
User C2
A user from Tenant A asks an agent:
"Show me the open invoices for my organization."
The agent may need to call:
Invoice API
A naive implementation might use:
User
|
v
Agent
|
| Application Token
v
Invoice API
The Invoice API sees:
Agent Application
It does not necessarily know:
Which user?
Which tenant?
Which permissions?
The agent has effectively become a shared identity.
That creates a confused-deputy risk.
What Is On-Behalf-Of Token Exchange?
OBO allows a service to exchange a user's access token for another token intended for a downstream resource.
Conceptually:
User
|
| User Token
v
Agent Application
|
| OBO Exchange
v
Downstream API Token
|
v
Invoice API
The downstream token represents delegated access rather than simply being a generic application credential.
The downstream API can then validate the token and apply its own authorization rules.
The Security Context
A useful model is:
User Identity
|
v
Tenant Context
|
v
Permissions
|
v
Agent
|
v
Delegated Token
|
v
Downstream API
The important principle is:
The agent should not be allowed to invent the user's identity or tenant.
The security context should originate from trusted authentication infrastructure.
Application Token vs Delegated Token
Consider two approaches.
Application Identity
Agent
|
| Application Token
v
API
The API sees:
agent-service
The application may need to perform all user-level authorization itself.
Delegated Identity
User
|
v
Agent
|
| Delegated Token
v
API
The API can evaluate the delegated identity and permissions.
This provides a stronger identity chain when downstream services need user-aware authorization.
Multi-Tenant Identity Flow
A typical architecture can look like:
Identity Provider
|
v
User
|
v
Agent Application
|
OBO Exchange
|
+---------------+---------------+
| | |
v v v
Customer API Billing API Document API
| | |
+---------------+---------------+
|
Tenant Policies
Each downstream API should independently validate the received token.
Never Trust a Tenant ID From the Prompt
Consider this request:
"Show me invoices for tenant-b."
The agent should not simply generate:
{
"tenantId": "tenant-b"
}
and pass it to the API.
The authenticated user's tenant context should come from trusted identity information.
For example:
Authenticated User
|
v
Tenant = tenant-a
|
v
Agent Request
|
v
Invoice API
|
X
tenant-b
The model does not get to redefine the security boundary.
Tenant Context Should Be Derived, Not Invented
A dangerous pattern is:
public async Task<IReadOnlyList<Invoice>> GetInvoices(
string tenantId)
{
return await db.Invoices
.Where(x => x.TenantId == tenantId)
.ToListAsync();
}
If tenantId originates from model-generated input, the agent can potentially request another tenant.
A safer approach derives tenant context from authenticated security information:
public async Task<IReadOnlyList<Invoice>> GetInvoices(
UserContext userContext)
{
var tenantId = userContext.TenantId;
return await db.Invoices
.Where(x => x.TenantId == tenantId)
.ToListAsync();
}
The user request can contain filters.
It should not determine the authorization boundary.
OBO Token Exchange Flow
A simplified flow looks like:
1. User authenticates
|
v
2. Agent receives user token
|
v
3. Agent requests delegated token
|
v
4. Identity system validates delegation
|
v
5. New token issued for API
|
v
6. Agent calls downstream API
|
v
7. API validates token
|
v
8. API applies authorization
The agent should not modify the original token.
It requests another token for the specific downstream resource.
Why Audience Matters
Tokens are generally intended for particular resources.
A token issued for:
Agent API
should not automatically be accepted by:
Billing API
The downstream service should validate the token's intended audience.
Conceptually:
Token
|
+--> Issuer
+--> Audience
+--> Subject
+--> Tenant
+--> Permissions
+--> Expiration
This prevents a token intended for one service from being blindly accepted by another.
Token Validation in ASP.NET Core
A downstream ASP.NET Core API can configure JWT bearer authentication:
builder.Services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = configuration["Identity:Authority"];
options.TokenValidationParameters =
new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true
};
});
The exact authority, audience, and validation configuration depends on the identity architecture.
The important principle is that the API must validate the token independently.
Authorization After Authentication
Authentication establishes:
Who is calling?
Authorization establishes:
What can they do?
For example:
[Authorize(Policy = "Invoices.Read")]
[HttpGet("invoices")]
public async Task<IActionResult> GetInvoices()
{
// Authorization already evaluated.
}
A downstream API should not assume that a valid token automatically means the caller can perform every operation.
Tenant-Aware Authorization
Suppose a token contains:
tenant_id = tenant-a
and:
permission = invoices.read
The API can enforce:
Tenant = tenant-a
Permission = invoices.read
before accessing data.
Conceptually:
Request
|
v
Token Validation
|
v
Tenant Validation
|
v
Permission Validation
|
v
Data Access
Tenant Isolation in the Data Layer
Application-level authorization is important, but the data query should also respect tenant scope.
For example:
var tenantId = userContext.TenantId;
var invoices = await db.Invoices
.Where(x => x.TenantId == tenantId)
.ToListAsync(cancellationToken);
This creates a direct relationship between:
Authenticated Context
|
v
Tenant Scope
|
v
Database Query
Do not depend on the model to supply the tenant identifier.
OBO Does Not Automatically Solve Tenant Isolation
This is an important distinction.
OBO can preserve delegated identity.
It does not automatically guarantee:
Tenant A -> Tenant A data only
The downstream API still needs correct authorization and data isolation.
A secure architecture therefore has:
OBO
+
Token Validation
+
Permission Checks
+
Tenant Filtering
=
Delegated Multi-Tenant Access
Delegated Permissions vs Application Permissions
A downstream API may support two different access models.
Delegated Access
User
|
v
Agent
|
v
API
The operation is performed on behalf of a user.
Application Access
Agent
|
v
API
The application acts independently.
These models should not be mixed accidentally.
For user-driven agent workflows, delegated access is often more appropriate when the downstream API needs to enforce the user's permissions.
Scope Design
Avoid creating one broad permission:
api.access
for every operation.
Prefer focused scopes or permissions such as:
invoices.read
invoices.write
customers.read
documents.read
reports.create
The exact naming scheme should match the organization's authorization model.
Fine-grained permissions reduce the impact of a compromised or misconfigured agent.
Least Privilege for Agents
An agent should receive only the permissions required for the current workflow.
For example:
Customer Support Agent
----------------------
customers.read
tickets.read
tickets.write
It should not automatically receive:
billing.admin
users.delete
security.manage
even if those capabilities exist somewhere in the organization.
Tool Permissions and API Permissions Are Different
An agent may have access to a tool:
invoice.search
but that does not mean every invoice operation is allowed.
The tool can call:
GET /invoices
with delegated access.
But:
DELETE /invoices/{id}
should require a different authorization policy.
Think of permissions at multiple layers:
Agent Capability
|
v
Tool Permission
|
v
API Permission
|
v
Tenant Authorization
|
v
Data Authorization
Avoid Shared High-Privilege Tokens
A dangerous architecture is:
All Users
|
v
Agent
|
v
Admin Token
|
v
All APIs
If the agent is manipulated, the attacker effectively gains the privileges of the shared administrator identity.
A delegated model is safer for user-driven operations:
User
|
v
Agent
|
v
Delegated Token
|
v
API
The API can enforce the user's permitted operations.
Token Lifetime
Delegated tokens should have appropriate lifetimes.
A long-running agent workflow creates an interesting problem:
Workflow Starts
|
v
Token Issued
|
v
Long Processing
|
v
Token Expires
|
v
Next API Call
The workflow must have a secure way to obtain a valid token when needed.
Do not simply store a user access token indefinitely.
Never Store Access Tokens in Workflow State
Avoid:
{
"workflowId": "wf-100",
"accessToken": "eyJ..."
}
Durable workflow state may be persisted for long periods.
Instead, store only the information required to reconstruct the authorized operation.
For example:
{
"workflowId": "wf-100",
"userId": "user-123",
"tenantId": "tenant-a",
"operation": "invoice-review"
}
A secure token acquisition mechanism can obtain the appropriate credential when the next operation is executed.
Background Workflows Need Special Attention
Consider:
HTTP Request
|
v
Agent
|
v
Start Background Workflow
|
v
HTTP Request Ends
The user's original request is now finished.
The background workflow may continue for several minutes or hours.
The application should not assume that the original access token remains valid for the entire workflow.
Instead, define how authorization is evaluated for long-running operations.
Reauthorization for Long-Running Workflows
For sensitive actions:
Workflow Starts
|
v
Authorization Check
|
v
Wait
|
v
Authorization Check
|
v
Sensitive Operation
This can prevent an operation from continuing with permissions that have since been revoked.
The exact reauthorization strategy depends on the business requirement.
User Revocation
Suppose:
10:00
User has invoices.write
10:05
Workflow starts
10:10
Permission revoked
10:20
Workflow attempts invoice update
The system should define whether the update is:
Allowed
or:
Denied
For sensitive systems, current authorization should generally be evaluated before executing the protected operation.
Tenant Switching Attacks
Agents may receive prompts such as:
"Switch to Tenant B and show me its invoices."
The system should not interpret this as a valid identity transition.
A tenant switch is an authorization event.
It should require an explicit mechanism rather than natural-language instructions.
For example:
User
|
v
Tenant Selection
|
v
Authorization
|
v
New Security Context
The model should never create the new security context itself.
Cross-Tenant Testing
Create a test environment with:
Tenant A
--------
Invoice A1
Invoice A2
Tenant B
--------
Invoice B1
Invoice B2
Then authenticate as Tenant A and test:
"Show Invoice A1."
Expected:
Allowed
Then:
"Show Invoice B1."
Expected:
Denied
Then test indirect access:
"Give me the total invoice amount for Tenant B."
Expected:
Denied
This is important because an agent may attempt to retrieve information through aggregation instead of direct record access.
Test Tool Chaining
Consider:
customer.search
|
v
customer.details
|
v
invoice.search
|
v
invoice.export
An agent may have permission to call the first three but not the last.
Test whether it can construct an equivalent export using the permitted tools.
Authorization should apply to the resulting capability, not merely individual tool names.
Test Confused Deputy Scenarios
A confused deputy occurs when the agent has more authority than the user.
For example:
User A
|
v
Agent
|
| Admin Token
v
Customer API
|
v
Tenant B
The agent becomes a privileged intermediary.
Test explicitly:
User permission < Agent permission
and verify that the agent cannot use its own authority to exceed the user's permitted access when performing delegated operations.
Test Token Substitution
A security test should attempt to use:
Token for API A
against:
API B
The expected result should be rejection.
This validates audience enforcement.
Similarly, test:
Expired Token
Malformed Token
Wrong Issuer
Wrong Audience
Missing Permission
Wrong Tenant
Test Permission Escalation
An agent may be instructed:
"Use the administrator capability to complete this task."
The authorization layer should reject the operation if the user does not possess the required permission.
Do not rely on prompt instructions such as:
"You are an administrator."
Security decisions must come from trusted authorization context.
Token Claims Should Not Be Blindly Trusted
A downstream API should validate the token's:
Issuer
Audience
Signature
Expiration
Relevant permissions
Relevant identity claims
Then apply its own authorization policy.
Do not accept arbitrary HTTP headers such as:
X-Tenant-Id: tenant-b
as authoritative tenant identity unless they are protected by a trusted service-to-service mechanism.
Use Strong Service Boundaries
A useful architecture is:
Identity
|
v
User
|
v
Agent
|
OBO Exchange
|
+---------+---------+
| |
v v
Customer API Invoice API
| |
v v
Tenant Policy Tenant Policy
| |
v v
Customer DB Invoice DB
Each API remains responsible for its own authorization.
The agent coordinates.
It does not become the central authorization authority.
Audit the Delegation Chain
When an agent performs an action, logs should help answer:
Who initiated it?
Which tenant?
Which agent?
Which tool?
Which downstream API?
Which permission?
Which operation?
When?
Was the request delegated?
A useful audit record might contain:
public sealed record AgentAuditEvent(
string EventId,
string UserId,
string TenantId,
string AgentId,
string ToolName,
string Resource,
string Action,
DateTimeOffset Timestamp,
bool Allowed);
Do not log sensitive tokens.
Correlation IDs
Distributed agent workflows should use correlation IDs:
User Request
|
v
Correlation ID
|
+----> Agent
|
+----> Customer API
|
+----> Billing API
|
+----> Audit System
This makes it possible to reconstruct an agent workflow across multiple services.
Audit User and Agent Separately
An audit record should distinguish:
Initiating User
from:
Executing Service
For example:
User:
user-123
Agent:
support-agent
API:
invoice-service
This is more useful than recording only:
Caller:
agent-service
Protect Audit Logs
Audit data can itself contain sensitive information.
Use appropriate controls for:
Access
Retention
Integrity
Redaction
Monitoring
Do not allow ordinary application users to modify security audit records.
Common Mistakes
Using One Admin Token for Every User
This destroys user-level authorization boundaries.
Trusting Tenant IDs From Prompts
Natural-language input is not a trusted security context.
Passing User Tokens to Every API
Tokens should be intended for the resource receiving them.
Ignoring Token Audience
A token issued for one API should not automatically work against another.
Storing Tokens in Workflow State
Long-lived workflow state is an inappropriate place for sensitive credentials.
Assuming OBO Solves Authorization
OBO provides delegated identity. APIs still need authorization policies.
Giving Agents Excessive Permissions
Use least privilege for tools and downstream APIs.
Trusting Custom Headers
Headers such as X-Tenant-Id should not automatically be considered authoritative.
Ignoring Background Workflows
A user's permissions can change while an agent workflow is still running.
Auditing Only the Agent
Downstream APIs should maintain their own security-relevant audit records.
Troubleshooting
The Downstream API Returns 401
Check:
Token signature
Issuer
Audience
Expiration
Authorization scheme
Authentication failure usually occurs before application authorization.
The API Returns 403
The token may be valid but insufficiently authorized.
Check:
Scopes
Roles
Permissions
Tenant
Resource policy
The Agent Can Access Another Tenant
Check whether:
Tenant ID comes from prompt input
Tenant ID comes from query parameters
API trusts custom headers
Database query lacks tenant filtering
Agent has excessive application permissions
OBO Exchange Fails
Check:
Original token
Delegation configuration
Requested resource
Token audience
Application permissions
Credential configuration
Long-Running Workflow Fails Later
Check:
Token expiration
Credential refresh
Permission revocation
Connection state
Workflow authorization
User Can Perform an Operation Directly but Not Through the Agent
Compare:
Direct API Token
with:
Delegated Agent Token
The delegated token may be missing the required permission or audience.
Agent Can Read Data but Cannot Update It
This may be correct.
Read and write operations should generally have separate authorization requirements.
Best Practices
Preserve user identity across delegated operations.
Use OBO when downstream APIs need user-aware delegated authorization.
Never let the model define tenant identity.
Derive tenant context from trusted security information.
Validate token issuer, audience, signature, and lifetime.
Use least-privilege permissions.
Separate read and write permissions.
Do not use shared administrator tokens for user-driven workflows.
Never store access tokens in durable workflow state.
Plan token lifetime for long-running agent workflows.
Re-evaluate authorization before sensitive long-running operations.
Enforce tenant filtering at the data-access layer.
Validate authorization independently in every downstream API.
Test cross-tenant access explicitly.
Test token substitution and audience validation.
Test confused-deputy scenarios.
Audit both the initiating user and executing agent.
Use correlation IDs across agent and API calls.
Protect audit logs from unauthorized modification.
Treat OBO as identity delegation, not as a replacement for authorization.
A Production-Oriented Architecture
A robust multi-tenant agent architecture can look like this:
User
|
v
Authentication
|
v
User Security Context
+-------------------+
| User |
| Tenant |
| Permissions |
+-------------------+
|
v
AI Agent
|
Tool Authorization
|
v
OBO Token Exchange
|
+---------------+---------------+
| | |
v v v
Customer API Billing API Document API
| | |
v v v
Auth + Authz Auth + Authz Auth + Authz
| | |
v v v
Tenant Aware Tenant Aware Tenant Aware
Data Access Data Access Data Access
The security boundary exists at every API.
The agent coordinates access but does not override downstream authorization.
Example End-to-End Request
Suppose a user asks:
"Show me the unpaid invoices for my company."
The workflow should conceptually be:
1. User authenticates
|
v
2. Application establishes user context
|
v
3. Agent interprets the request
|
v
4. Agent selects invoice.search
|
v
5. Application requests delegated access
|
v
6. Downstream API validates token
|
v
7. API determines tenant from trusted identity
|
v
8. API applies invoices.read permission
|
v
9. Database query filters tenant
|
v
10. Authorized results returned
|
v
11. Agent generates response
Notice that the model never determines:
Tenant
Permission
Database role
Token audience
Those remain outside the model.
Security Test Matrix
A useful regression suite should contain scenarios such as:
| Scenario | Expected Result |
|---|---|
| Valid delegated token | Allowed |
| Expired token | Denied |
| Wrong audience | Denied |
| Wrong issuer | Denied |
| Missing permission | Denied |
| Cross-tenant request | Denied |
| Prompt attempts tenant switch | Denied |
| Admin operation without permission | Denied |
| Valid read operation | Allowed |
| Unauthorized write operation | Denied |
| Token stored in workflow state | Prohibited |
| Revoked permission before execution | Denied |
| Tool chaining to restricted operation | Denied |
| Application token used where delegated token is required | Denied |
Deployment Checklist
Before deploying a multi-tenant agent using delegated identity, verify:
[ ] User identity is authenticated
[ ] Tenant identity comes from trusted context
[ ] Agent permissions are defined
[ ] Tool permissions are defined
[ ] Downstream APIs validate tokens
[ ] Token audience is enforced
[ ] Token lifetime is validated
[ ] Tenant isolation is enforced
[ ] Database queries apply tenant scope
[ ] Cross-tenant tests pass
[ ] Token substitution tests pass
[ ] Admin escalation tests pass
[ ] Long-running workflows handle expiration
[ ] Revoked permissions are handled
[ ] Tokens are never written to logs
[ ] Tokens are not stored in durable workflow state
[ ] User and agent identities are audited
[ ] Correlation IDs are available
[ ] Downstream APIs perform independent authorization
Frequently Asked Questions
What problem does OBO solve?
OBO allows an application or agent to obtain a downstream access token representing delegated access on behalf of an authenticated user.
Does OBO automatically enforce tenant isolation?
No. Tenant isolation remains the responsibility of the authorization and data-access architecture.
Should an AI agent receive administrator permissions?
Generally no. Agent capabilities should follow least privilege and should be limited to the operations required by the workflow.
Can the agent choose which tenant to access?
The agent can interpret a user's request, but it should not be able to redefine the authenticated tenant context. Any tenant change must go through an explicit authorization process.
Should access tokens be stored in agent memory?
No. Tokens are sensitive credentials and should not be placed in conversation memory or durable workflow state.
What happens when a token expires during a workflow?
The application needs a defined strategy for obtaining a valid credential. Sensitive operations should also consider whether the user's current permissions are still valid.
Is a delegated token enough to protect an API?
No. The API must independently validate the token and enforce its own authorization policies.
Why is audience validation important?
It ensures that a token intended for one resource is not automatically accepted by another resource.
Should every API trust the agent?
No. Each downstream API should establish its own authentication and authorization boundary.
What is the biggest security risk in multi-tenant agent architectures?
One of the most important risks is allowing a privileged agent identity to act on behalf of users without preserving their tenant and permission boundaries.
Conclusion
Multi-tenant AI agents introduce a fundamental identity challenge.
The agent may need to access multiple downstream services, but those services still need to know who initiated the operation and what that user is allowed to do.
A shared application identity can make this difficult:
User
|
v
Agent
|
v
Admin Identity
|
v
All Tenant Data
A delegated model provides a stronger security boundary:
User
|
v
Agent
|
v
OBO Token Exchange
|
v
Delegated Token
|
v
Downstream API
|
v
Tenant + Permission Checks
|
v
Authorized Data
The key principle is:
An AI agent should coordinate actions on behalf of a user without becoming more powerful than the user.
OBO can help preserve that delegated identity across service boundaries, but it is only one part of the security architecture.
A production-grade multi-tenant agent should combine:
Delegated Identity
+
Token Validation
+
Least Privilege
+
Tenant Isolation
+
API-Level Authorization
+
Secure Token Lifecycle
+
Auditability
+
Security Testing
When these controls work together, agents can operate across multiple services while maintaining clear boundaries between users, tenants, tools, APIs, and data.

Join the conversation! Your thoughts help the community grow.