How OpenSEO Determines Authentication Mode: Environment-Driven Configuration
OpenSEO reads the AUTH_MODE environment variable and validates it against a predefined list of supported modes, defaulting to cloudflare_access if the value is missing or invalid.
The every-app/open-seo repository implements a configuration-driven authentication system that automatically wires the correct backend and frontend components based on a single environment variable. Understanding how OpenSEO determines authentication mode is essential for deploying self-hosted instances, integrating with Cloudflare Access, or running local development environments without credentials.
How OpenSEO Authentication Mode Detection Works
The core logic resides in src/lib/auth-mode.ts, which exports utilities for parsing, validating, and querying the current authentication strategy.
Step 1: Define Supported Authentication Modes
The framework maintains a strict allowlist of valid modes. The AUTH_MODES constant array explicitly defines three supported strings: "cloudflare_access", "local_noauth", and "hosted". This constraint prevents runtime errors from typos or unsupported configurations.
Step 2: Parse and Validate the Environment Variable
The getAuthMode(value) function processes the AUTH_MODE environment variable using a Zod enum schema (authModeSchema). This validation ensures type safety at runtime, rejecting malformed inputs before they propagate through the application stack.
Step 3: Apply Safe Defaults and Warnings
If parsing fails due to a missing or invalid value, OpenSEO logs a one-time warning and returns "cloudflare_access" as a safe default. This fallback ensures the application remains secure even when configuration is incomplete.
Authentication Mode Implementation in Core Files
Once determined, the OpenSEO authentication mode drives behavior across the entire stack, from middleware resolution to UI rendering.
User Context Resolution (src/middleware/ensure-user/resolve.ts)
The middleware dispatcher in src/middleware/ensure-user/resolve.ts calls getAuthMode(env.AUTH_MODE) to route each request to the appropriate context handler:
resolveLocalNoAuthContext(): Returns a stub admin user for private development environmentsresolveHostedContext(headers): Initializes the full Better-Auth UI flowresolveCloudflareAccessContext(headers): Extracts identity from Cloudflare Access headers
Auth Instance Creation (src/lib/auth.ts)
The Better-Auth library is only initialized when necessary. The src/lib/auth.ts module conditionally constructs the auth instance by checking isHostedAuthMode(env.AUTH_MODE), preventing unnecessary database connections and middleware overhead in non-hosted modes.
Route Guards and API Protection
API routes selectively enable endpoints based on the current mode. For example, src/routes/api/auth/$.ts and src/routes/api/gsc/oauth/callback.ts verify isHostedAuthMode(env.AUTH_MODE) before registering their handlers, ensuring OAuth callbacks and session endpoints are only accessible when running in hosted mode.
Client-Side Behavior Checks
The isHostedClientAuthMode() function checks import.meta.env.AUTH_MODE on the client side. This ensures the UI layer only renders login screens when the server is configured for hosted authentication, maintaining consistency between the build target and runtime behavior.
Practical Code Examples
Reading and branching based on the authentication mode:
// Example: reading the mode and branching
import { getAuthMode, isHostedAuthMode } from "@/lib/auth-mode";
const mode = getAuthMode(process.env.AUTH_MODE);
if (mode === "local_noauth") {
// Run without any auth – useful for private dev setups
} else if (isHostedAuthMode(mode)) {
// Initialise full Better‑Auth UI
} else {
// Default Cloudflare Access flow
}
Client-side conditional rendering:
// Example: client‑side check (Vite env)
import { isHostedClientAuthMode } from "@/lib/auth-mode";
if (isHostedClientAuthMode()) {
// Render the sign‑in page
} else {
// Skip UI – auth is handled upstream
}
Middleware user resolution:
// Example: middleware that resolves the user context
import { resolveUserContextFromHeaders } from "@/middleware/ensure-user/resolve";
export async function handler(request: Request) {
const ctx = await resolveUserContextFromHeaders(request.headers);
// ctx now contains the authenticated user (or a stub for local_noauth)
}
Summary
- OpenSEO determines authentication mode by reading the
AUTH_MODEenvironment variable and validating it against theAUTH_MODESallowlist insrc/lib/auth-mode.ts. - The
getAuthMode()function uses Zod for type-safe parsing, defaulting to"cloudflare_access"when the input is invalid or missing. - Three modes are supported:
"hosted"(full Better-Auth),"cloudflare_access"(header-based identity), and"local_noauth"(development stub). - The resolved mode drives middleware routing in
src/middleware/ensure-user/resolve.ts, conditional auth initialization insrc/lib/auth.ts, and client-side UI decisions viaisHostedClientAuthMode().
Frequently Asked Questions
What happens if I don't set the AUTH_MODE environment variable?
If AUTH_MODE is undefined or contains an invalid value, OpenSEO logs a warning and defaults to "cloudflare_access". This safe fallback ensures the application remains secure while alerting you to the configuration issue.
Can I use OpenSEO without any authentication?
Yes. Set AUTH_MODE to "local_noauth" to run the application without credentials. In this mode, src/middleware/ensure-user/resolve.ts returns a stub admin context automatically, making it ideal for private development environments.
How does OpenSEO handle authentication differently in hosted mode versus Cloudflare Access mode?
In hosted mode (AUTH_MODE=hosted), OpenSEO initializes the full Better-Auth instance from src/lib/auth.ts and exposes authentication endpoints like src/routes/api/auth/$.ts. In Cloudflare Access mode, the application skips local session management and instead resolves user identity from Cloudflare Access headers via resolveCloudflareAccessContext().
Why does the client need to check the authentication mode separately?
The client-side check via isHostedClientAuthMode() ensures the UI build matches the server configuration. Since import.meta.env.AUTH_MODE is evaluated at build time with Vite, this prevents client-side login components from rendering when the server is configured for external authentication like Cloudflare Access.
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 →