How OpenSEO Authorizes Chat Agent Connections Using `resolveUserContextFromHeaders`
OpenSEO validates every chat agent connection by extracting user identity from HTTP headers via the resolveUserContextFromHeaders function in src/middleware/ensure-user/resolve.ts, constructing a typed UserContext that enforces authentication before WebSocket upgrades occur.
The OpenSEO repository implements a robust authentication layer to protect its real-time chat agent endpoints. At the core of this security model sits the resolveUserContextFromHeaders utility, which transforms incoming request headers into a verified user identity object. This function ensures that only authenticated users can establish persistent WebSocket connections for chat interactions.
The Authentication Pipeline in src/middleware/ensure-user/resolve.ts
Header Inspection and Token Extraction
The function begins by examining the incoming request headers for authentication credentials. It checks the Authorization header for a Bearer token or falls back to session cookies when present. The raw JWT string is extracted by stripping the Bearer prefix, preparing it for cryptographic verification.
Token Verification via Better-Auth
Once extracted, the token is passed to the @/better-auth provider for validation. This step verifies the token's digital signature, checks expiration timestamps, and validates the scope claims. If the signature is invalid or the token has expired, the function immediately halts processing.
User Lookup and Context Construction
Upon successful token verification, the authentication provider returns the user's database record. The function then assembles a UserContext object containing:
userId: The primary key identifying the userorganizationId: The organization for multi-tenant isolationpermissions: Capability flags derived from the user's rolesessionId: Optional telemetry identifier for audit trails
This typed context object becomes the authoritative identity representation for the remainder of the request lifecycle.
Protecting Chat Agent Connections
WebSocket upgrade requests for chat agents traverse the same authentication middleware as standard HTTP requests. When a client attempts to establish a chat session, the server invokes resolveUserContextFromHeaders before completing the WebSocket handshake. The function attaches the resolved UserContext to the request object, allowing downstream handlers to enforce organization-specific rate limits and verify chat session permissions.
If authentication fails at any stage, the function throws an UnauthenticatedError, causing the server to reject the connection before any chat agent logic executes. This prevents unauthorized access to real-time communication channels.
Server-Side Implementation Example
import { resolveUserContextFromHeaders } from '@/middleware/ensure-user/resolve';
import { createChatAgent } from '@/services/chatAgent';
export async function POST(request: Request) {
const userContext = await resolveUserContextFromHeaders(request.headers);
const agent = await createChatAgent({
userId: userContext.userId,
organizationId: userContext.organizationId,
});
return new Response(JSON.stringify({ wsUrl: agent.wsUrl }), { status: 200 });
}
Client-Side Header Requirements
async function startChat() {
const token = localStorage.getItem('authToken');
const response = await fetch('/api/chat', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
const { wsUrl } = await response.json();
const socket = new WebSocket(wsUrl);
}
Error Handling and Security Boundaries
The resolveUserContextFromHeaders function operates as a security gate. When the Authorization header is missing, malformed, or contains an invalid token, the function raises an UnauthenticatedError. This exception bubbles up to the server's error handling middleware in src/server.ts, which returns a 401 Unauthorized response to the client.
All protected routes, including the chat agent initialization endpoint wrapped by src/middleware/ensureUser.ts, rely on this function to guarantee that userId and organizationId are present before executing business logic. This design ensures that chat messages are always properly attributed to verified users and that organization-level billing boundaries remain intact.
Summary
- The
resolveUserContextFromHeadersfunction insrc/middleware/ensure-user/resolve.tsserves as the primary authentication boundary for OpenSEO's chat agent connections. - It extracts JWT tokens from the
Authorizationheader or cookies, verifies them via the better-auth provider, and constructs a typedUserContextobject. - WebSocket upgrade requests undergo the same header validation as HTTP requests, preventing unauthorized persistent connections.
- The function throws
UnauthenticatedErrorfor missing or invalid credentials, blocking access before chat agent logic executes. - Downstream services consume the
UserContextto enforce multi-tenant isolation and attach user attribution to chat messages.
Frequently Asked Questions
What happens if the Authorization header is missing when connecting to a chat agent?
The resolveUserContextFromHeaders function detects the absence of credentials and immediately throws an UnauthenticatedError. This error propagates through the middleware stack in src/server.ts, resulting in a 401 Unauthorized response that prevents the WebSocket connection from being established.
Which authentication provider does OpenSEO use to verify the JWT tokens?
According to the source code in src/middleware/ensure-user/resolve.ts, the function delegates token verification to the @/better-auth provider. This provider handles cryptographic signature validation, expiration checks, and scope verification before returning the user record.
Can resolveUserContextFromHeaders handle both Bearer tokens and session cookies?
Yes. The function inspects the Authorization header for Bearer tokens first, but it also checks for session cookies in the cookie header when present. This dual-mode support allows both API clients and browser-based users to authenticate chat agent connections using their preferred credential mechanism.
How does the UserContext enforce multi-tenancy in chat applications?
The UserContext object contains an organizationId field populated during the user lookup phase. Downstream handlers in src/services/chatAgent.ts use this field to isolate chat sessions, enforce rate limits, and validate billing entitlements specific to the user's organization, ensuring strict data separation between tenants.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →