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 environments
  • resolveHostedContext(headers): Initializes the full Better-Auth UI flow
  • resolveCloudflareAccessContext(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_MODE environment variable and validating it against the AUTH_MODES allowlist in src/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 in src/lib/auth.ts, and client-side UI decisions via isHostedClientAuthMode().

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →