Introduction
AI agents often handle information that belongs to different users, conversations, applications, or business processes. A customer-support agent may process one customer's account while another request is being handled at the same time. An internal agent may work with sensitive company documents while a separate session handles public information.
If these sessions are not properly isolated, one session can accidentally receive context belonging to another session.
Session isolation is therefore an important part of AI agent architecture. It is not enough to create separate chat windows in the user interface. The underlying conversation state, tool access, identity, retrieved data, and application state must also be scoped correctly.
Microsoft Foundry provides capabilities for building agent-based applications, but session isolation ultimately depends on how the application defines and enforces boundaries around each agent execution.
What Is an AI Agent Session?
A session represents the state associated with a particular interaction or workflow.
A simplified session can contain:
Session
|
+-- Conversation history
+-- User/application context
+-- Agent configuration
+-- Tool interactions
+-- Retrieved information
+-- Temporary state
For example:
User A
|
v
Session A
|
+-- Conversation A
+-- Tools
+-- Retrieved data
User B
|
v
Session B
|
+-- Conversation B
+-- Tools
+-- Retrieved data
The critical requirement is that information created for Session A should not unintentionally become available to Session B.
Why Session Isolation Matters
Consider a customer-support application.
User A asks:
What is the status of my order?
The agent retrieves order information and stores it in the conversation context.
A few seconds later, User B starts a new conversation.
If the application accidentally reuses User A's conversation state, User B might receive information from User A.
The problem is not necessarily the model itself. It can originate from application state management.
Common sources of session leakage include:
Reusing conversation identifiers
Global in-memory state
Incorrect database queries
Shared caches
Incorrect user-to-session mappings
Reusing tool results
Incorrect retrieval filters
Shared temporary files
Inadequate authorization checks
Session isolation therefore needs to be designed across the entire application.
Session Isolation Is More Than Conversation History
A common mistake is to isolate only the chat history.
For example:
Session A
|
+-- Conversation A
while using shared state for everything else:
Global
|
+-- User profile
+-- Retrieved documents
+-- Tool results
+-- Temporary files
This can still create cross-session data exposure.
A stronger design treats session-related information as scoped state:
Session A
|
+-- Conversation
+-- User context
+-- Retrieval scope
+-- Tool state
+-- Temporary resources
Session B
|
+-- Conversation
+-- User context
+-- Retrieval scope
+-- Tool state
+-- Temporary resources
Use a Unique Session Identifier
Every conversation or workflow should have a unique identifier.
For example:
var sessionId = Guid.NewGuid().ToString();
The application can associate the identifier with the authenticated principal and relevant application context.
Conceptually:
User
|
+-- User ID
|
+-- Session ID
|
+-- Agent ID
A database record might look like:
{
"sessionId": "sess-7f31",
"userId": "user-184",
"agentId": "support-agent"
}
The important point is that a session ID should not be treated as authorization by itself.
A client should not be able to change the session identifier and gain access to another user's conversation.
Validate Session Ownership
Every request should verify that the session belongs to the authenticated user or permitted application context.
For example:
public async Task<Session> GetSessionAsync(
string sessionId,
string userId)
{
var session = await _sessionStore.GetAsync(sessionId);
if (session == null)
throw new KeyNotFoundException();
if (session.UserId != userId)
throw new UnauthorizedAccessException();
return session;
}
This is a simple but important boundary.
Do not rely on:
sessionId == authorization
Instead:
Authenticated Identity
|
v
Session Ownership Check
|
v
Session State
Keep Session State Scoped
Application code should avoid global mutable state for user-specific agent sessions.
This pattern is risky:
public static class AgentState
{
public static List<string> Messages { get; } = new();
}
All users can potentially interact with the same collection.
Instead, scope state by session:
public class SessionState
{
public string SessionId { get; init; }
public List<string> Messages { get; } = new();
}
Persistent applications should generally store important session state in a durable store rather than relying on process memory.
That also makes the application more resilient when instances restart or scale horizontally.
Be Careful With Distributed Caches
Caching can introduce subtle session-isolation problems.
Suppose an application uses:
cache key:
agent-response
Every user could potentially receive the same cached value.
Instead, include the relevant scope:
cache key:
agent:{agentId}:user:{userId}:session:{sessionId}:response:{requestId}
The exact key structure depends on the application, but the principle is consistent: a cache key must reflect the security boundary of the data being cached.
Do not include more identifying information than necessary, and avoid putting sensitive data directly into cache keys when the infrastructure exposes them broadly.
Isolate Retrieval Context
Retrieval-augmented generation introduces another important boundary.
Suppose two customers have separate documents:
Customer A
|
+-- invoice-a.pdf
+-- contract-a.pdf
Customer B
|
+-- invoice-b.pdf
+-- contract-b.pdf
A query from Customer A should not retrieve Customer B's documents.
A conceptual retrieval query might include a tenant or user scope:
var documents = await _search.SearchAsync(
query,
tenantId: currentTenantId);
The retrieval layer should enforce this scope.
Do not rely on the model to ignore documents belonging to another tenant.
The safer flow is:
Authenticated Identity
|
v
Tenant/User Scope
|
v
Retrieval Filter
|
v
Allowed Documents
|
v
Agent
Tool Calls Must Respect Session Boundaries
An agent may call tools that access application data.
For example:
get_customer_profile
get_order
search_documents
create_ticket
The tool itself should verify the caller's scope.
A dangerous pattern is:
Agent
|
v
get_order(orderId)
|
v
Return order
The API should not assume that knowing an order ID proves authorization.
A stronger design is:
Agent
|
v
get_order(orderId, userContext)
|
v
Authorization Check
|
v
Order
For example:
var order = await _orders.GetForCustomerAsync(
orderId,
authenticatedUserId);
This prevents the model from bypassing application authorization by supplying another identifier.
Separate Sessions From Agent Configuration
Two sessions can use the same agent configuration without sharing their state.
For example:
Support Agent
|
+-- Session A
|
+-- Session B
|
+-- Session C
The agent's general instructions can remain shared, while conversation state remains session-specific.
This is an important distinction:
Shared:
- Agent definition
- General instructions
- Tool definitions
Isolated:
- Conversation history
- User data
- Retrieved private documents
- Temporary state
- Tool results
Shared configuration should not accidentally contain user-specific information.
Handle Concurrent Requests
A single session can also receive multiple requests at the same time.
For example:
Session A
|
+---- Request 1
|
+---- Request 2
If both requests modify the same conversation state simultaneously, updates can arrive in the wrong order.
Use appropriate concurrency controls such as:
Optimistic concurrency
Version numbers
Database transactions
Request serialization
Session-level locks where appropriate
A simple version field might look like:
{
"sessionId": "sess-7f31",
"version": 12
}
A request can verify that the session is still at version 12 before writing version 13.
This helps prevent one request from silently overwriting another.
Session Isolation in Multi-Tenant Applications
Multi-tenant applications need at least two boundaries:
Tenant
|
+-- User
|
+-- Session
|
+-- Agent
For example:
Tenant A
|
+-- User 1
| +-- Session 1
| +-- Session 2
|
+-- User 2
+-- Session 3
Tenant B
|
+-- User 3
+-- Session 4
A query for Session 3 should never return Session 1 or Session 4 data.
The database schema, authorization layer, cache, retrieval system, and tool APIs should all respect the tenant boundary.
Do Not Trust Client-Supplied Tenant IDs
A request might contain:
{
"tenantId": "tenant-b",
"sessionId": "session-123"
}
The application should not automatically trust this value.
The tenant should normally be derived from the authenticated identity or a trusted authorization context.
Conceptually:
Authentication
|
v
Trusted Tenant Context
|
v
Session Lookup
|
v
Tenant Validation
This prevents a client from simply changing a tenant identifier in the request.
Logging Without Exposing Sensitive Data
Session-level observability is important, but logs can themselves become a source of data leakage.
Log identifiers that help investigation:
sessionId
userId hash or internal identifier
agentId
requestId
timestamp
tool name
execution status
Avoid logging sensitive conversation content unless there is a documented reason and appropriate protection.
A useful correlation structure is:
requestId
|
+-- sessionId
|
+-- agentId
|
+-- tool calls
This makes it possible to trace an execution without unnecessarily copying sensitive data into logs.
Common Mistakes
Reusing Session IDs
A session identifier should have a clearly defined lifecycle and ownership.
Using Global Variables for Conversation State
Global mutable state can mix data between users and sessions.
Trusting Session IDs From the Client
Always verify ownership against authenticated identity.
Filtering Data Only in the Prompt
Do not retrieve unauthorized data and expect the model to ignore it.
Ignoring Tool-Level Authorization
Every sensitive tool should enforce its own access rules.
Using Unscoped Cache Keys
Cached responses must respect user and tenant boundaries.
Ignoring Concurrent Requests
Multiple requests can update the same session at the same time.
Logging Complete Conversations by Default
Detailed logs can create another sensitive data store.
Troubleshooting Session Leakage
When you suspect that one session is receiving another session's data, investigate the entire request path.
Step 1: Trace the Request
Start with the request ID and identify its session.
Step 2: Verify Session Ownership
Confirm the authenticated identity and session mapping.
Step 3: Inspect State Storage
Check whether the conversation state is stored under the correct key.
Step 4: Inspect Cache Keys
Look for keys that omit user, tenant, or session scope.
Step 5: Check Retrieval Filters
Verify that search queries apply the correct tenant or user filter.
Step 6: Check Tool Authorization
Confirm that tools independently validate access to requested resources.
Step 7: Check Concurrency
Look for simultaneous writes or stale session versions.
This process is usually more effective than focusing only on the model's response.
Best Practices
Give every conversation a unique session identifier.
Associate each session with a trusted identity.
Validate session ownership on every request.
Scope state by tenant, user, and session where appropriate.
Keep shared agent configuration separate from user state.
Use authorization checks inside sensitive tools.
Apply tenant filters before retrieval results reach the model.
Use security-aware cache keys.
Protect concurrent session updates.
Keep sensitive information out of unnecessary logs.
Test cross-user and cross-tenant access explicitly.
Treat session isolation as an application-wide security boundary.
Advantages and Disadvantages
Advantages | Disadvantages |
|---|---|
Prevents cross-session data exposure | Requires careful state management |
Supports multi-user agent applications | Adds authorization checks |
Makes debugging and auditing easier | Distributed systems increase complexity |
Works with multi-tenant architectures | Cache and retrieval layers need additional controls |
Supports safer concurrent workloads | Poor session design can still introduce subtle leaks |
Production Checklist
[ ] Every session has a unique identifier
[ ] Session ownership is validated
[ ] Tenant scope is enforced
[ ] Conversation state is isolated
[ ] Cache keys include required security scope
[ ] Retrieval queries enforce access boundaries
[ ] Tools perform authorization checks
[ ] Shared agent configuration contains no user-specific state
[ ] Concurrent session updates are controlled
[ ] Sensitive data is protected in logs
[ ] Cross-user access tests are automated
[ ] Cross-tenant access tests are automated
[ ] Session lifecycle and retention are documented
Summary
AI agent session isolation is a security and architecture concern that extends beyond conversation history.
A reliable design separates shared agent configuration from private session state and ensures that every session is associated with a trusted identity and appropriate tenant scope.
The same boundary must be maintained across databases, caches, retrieval systems, tools, APIs, and logs. A secure conversation ID alone is not sufficient.
For Microsoft Foundry-based agent applications, the surrounding application architecture remains responsible for enforcing these boundaries. The model should operate within the data and tools that the application has already authorized.
The practical goal is simple: one session should only be able to access the state, data, and actions that its authenticated context is allowed to use.

Join the conversation! Your thoughts help the community grow.