Auth and Route Protection
Purpose and Scope
Auth and route protection in eve starts with a distinction that prevents many design mistakes: inbound route authorization is separate from outbound tool or connection authorization. Inbound route authorization decides whether a caller may reach the agent HTTP routes at all, and it runs in the channel before model work begins. Outbound authorization happens later, when a tool, OpenAPI connection, MCP connection, or other external integration needs credentials to call another service. Treating these as separate systems keeps user admission, tenant scoping, and third-party service access from being mixed into one prompt-driven decision.
Sources: docs/guides/auth-and-route-protection.md
The guide is written for application developers exposing eve agents over HTTP, especially through the built-in Eve channel. Its core reader problem is not how to authenticate every possible service, but how to place a reliable gate in front of session creation, session continuation, and streaming. Because those routes can start model execution or reveal run output, they need a policy that fails closed, admits only verified principals, and preserves useful identity attributes for later runtime decisions. The page also explains how local development fits without weakening production defaults.
Sources: docs/guides/auth-and-route-protection.md
Relevant Source Files
- docs/guides/auth-and-route-protection.md — first-party guide defining the two auth systems, the protected Eve HTTP routes, the ordered auth walk, standard helpers, custom auth functions, error behavior, and the routeAuth reuse point for custom channels.
Route Auth Model
The route-auth policy lives on the HTTP channel factory, conventionally authored in the channel file for the Eve channel. In practice, this means the authentication decision is part of channel configuration rather than agent instructions, tool code, or frontend state. The guide names three guarded routes: starting a session, sending a follow-up to an existing session, and attaching to the stream for a session. Those three entry points form the user-facing session contract, so the policy protects both the ability to create work and the ability to observe work that is already running.
Sources: docs/guides/auth-and-route-protection.md
The health route is intentionally different. It is always public and skips the authorization walk so load balancers, uptime monitors, and deployment platforms can probe the agent without holding end-user credentials. That exception should not be copied to the session routes. For production browser traffic, eve rejects by default unless an authenticator accepts the request. Anonymous access is therefore a deliberate opt-in, not an accidental fallback. If an application truly wants open access, the guide requires an explicit anonymous helper rather than relying on an empty or missing policy.
Sources: docs/guides/auth-and-route-protection.md
A typical channel configuration combines a production authenticator with a local development fallback. The Vercel OIDC helper is presented as a convenience for Vercel-hosted agents and Vercel-to-Vercel callers, not as a required identity system. Applications that already have cookies, sessions, API keys, or a corporate identity provider should put that authenticator into the same walk. The key architectural point is that custom entries are first-class; they can supplement Vercel OIDC or replace it entirely when the application already owns identity verification.
Sources: docs/guides/auth-and-route-protection.md
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";
export default eveChannel({
auth: [vercelOidc(), localDev()],
});Ordered Auth Walk
The channel accepts either one authentication function or an ordered array. The order matters because eve walks the list from left to right and stops as soon as an entry accepts the caller. An entry accepts by returning a session authentication context. It declines by returning null or undefined, allowing the next provider to inspect the same request. It rejects by throwing an authentication or authorization error. If every entry declines, the request receives an unauthenticated response; if the array is empty, the channel rejects everything rather than silently allowing traffic.
Sources: docs/guides/auth-and-route-protection.md
This ordered walk makes composition straightforward but also makes ordering a security concern. Put application-specific providers first, because they are the best source of real user identity and tenant attributes. Put broad helpers after them. The guide specifically calls out the relationship between Vercel OIDC and the local development helper: Vercel OIDC should appear before the synthetic local fallback so a local bearer token can resolve to its real principal rather than being shadowed. On non-Vercel hosts, omit Vercel OIDC unless accepting Vercel-issued tokens is intentional.
Sources: docs/guides/auth-and-route-protection.md
import { type AuthFn, localDev, vercelOidc } from "eve/channels/auth";
import { eveChannel } from "eve/channels/eve";
import { getSession } from "@/lib/auth";
function appSession(): AuthFn<Request> {
return async (request) => {
const session = await getSession(request);
if (!session) return null;
return {
attributes: { providerId: session.providerId },
authenticator: "app",
principalId: session.userId,
principalType: "user",
};
};
}
export default eveChannel({
auth: [appSession(), vercelOidc(), localDev()],
});The returned context is more than a yes or no. It carries the authenticator name, the principal identifier, the principal type, and arbitrary attributes that become part of the verified session identity. That shape is what lets an application stamp a provider, organization, workspace, or tenant identifier onto the turn without exposing credential selection to the model. The guide’s example uses an application session and stores a provider identifier in attributes, illustrating the intended pattern: verify outside the prompt, attach trusted metadata, and let later code read that trusted context.
Sources: docs/guides/auth-and-route-protection.md
Errors, Rejections, and Custom Channels
Skipping and rejecting are intentionally different outcomes. A provider that does not recognize a request should return null so another provider can try. A provider that recognizes the request and knows it should fail should throw. The guide names specific helpers for unauthenticated and forbidden failures, mapping them to precise status behavior. Use unauthenticated failures when the caller must sign in or present credentials. Use forbidden failures when credentials are present but the caller is not allowed to access the workspace, tenant, route, or operation.
Sources: docs/guides/auth-and-route-protection.md
import { ForbiddenError, UnauthenticatedError } from "eve/channels/auth";
throw new UnauthenticatedError({
code: "authentication_required",
message: "Sign in to continue.",
});
throw new ForbiddenError({
message: "Not allowed on this workspace.",
});Custom channels should not invent a parallel authorization algorithm if they want Eve-compatible behavior. The guide directs custom channel authors using the lower-level channel definition API to call the shared route authorization helper. That reuse point matters because it preserves the same ordered walk semantics, the same skip-versus-throw contract, and the same failure behavior across native and custom transports. A custom channel can change how messages arrive, but it should still make the admission decision before any model work runs or any protected session stream is exposed.
Sources: docs/guides/auth-and-route-protection.md
Inbound Identity and Outbound Authorization
After a request is accepted, the verified identity can guide outbound authorization, but it should not be confused with outbound credentials themselves. The guide’s opening distinction is the rule to preserve: route auth admits the caller; tool and connection auth signs the agent into another system when needed. In multi-tenant applications, the route authenticator should attach trusted tenant or workspace attributes to the session context. Tool executors or connection auth callbacks can then select stored credentials for that tenant without asking the model to choose keys, tokens, or service accounts.
Sources: docs/guides/auth-and-route-protection.md
This pattern is especially important for OAuth-backed MCP servers, OpenAPI connections, or private business APIs. The agent turn can know that the authenticated user belongs to a tenant, but the model should not receive raw secrets and should not decide which customer credential to use from natural-language input. Application code should verify the inbound caller, persist tenant membership in the session auth context, and resolve outbound credentials from server-side storage. That keeps the trust boundary clear: prompts request work, auth code verifies callers, and integration code signs outbound calls.
Sources: docs/guides/auth-and-route-protection.md
Route Contract and Operational Guidance
The protected routes line up with the stable session workflow. A client starts a durable session, continues that session with the current continuation handle, and opens a stream to observe events. Protecting all three routes is necessary because the stream route can reveal output even when it does not start a new turn, while the continuation route can add work to an existing conversation. The health route remains public for operations, but it should only answer health checks; application data, user identity, session state, and run output belong behind the route-auth policy.
Sources: docs/guides/auth-and-route-protection.md
For local development, the practical goal is to keep iteration easy without training production code to accept anonymous browser traffic. The local development helper provides a final fallback for developer machines, while production deployments should prefer explicit authenticators that match the hosting and application identity model. If an application is deployed outside Vercel, using only the application’s own session or API-key verifier may be cleaner than accepting Vercel-issued tokens. If it is deployed on Vercel and called by another Vercel service, the OIDC helper can reduce custom verification work.
Sources: docs/guides/auth-and-route-protection.md
Implementation Checklist and Next Steps
When adding route protection, begin in the Eve channel file and list every authenticator in the order you want it attempted. Add your own session, JWT, or API-key verifier before generic helpers. Return null only when the provider truly does not recognize the caller. Return a session authentication context when the caller is accepted, including the attributes later code needs for tenant and provider decisions. Throw explicit unauthenticated or forbidden errors when the caller is known but invalid for the requested access path.
Sources: docs/guides/auth-and-route-protection.md
After the first policy works, test four paths: a valid application user, an unrecognized request, a recognized but forbidden user, and a local development request. Also verify that the health route remains reachable without credentials and that the three session routes are not reachable without an accepted authenticator. From here, read the sessions and streaming material to understand what each protected route does, then review multi-tenant patterns if your tools or connections must choose credentials based on the verified inbound principal rather than prompt content.
Sources: docs/guides/auth-and-route-protection.md