# How to Implement Authentication Middleware in SXO for Node, Bun, Deno, and Cloudflare Workers

> Implement authentication middleware in SXO for Node Bun Deno and Cloudflare Workers. Learn how to create cross-platform guards with SXOs standard middleware system.

- Repository: [Víctor García/sxo](https://github.com/gc-victor/sxo)
- Tags: how-to-guide
- Published: 2026-03-02

---

**SXO provides a Web Standard-compatible middleware system where you export a default function from [`src/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/middleware.js) that receives a Web Standard `Request` and optional `env` argument; returning a `Response` short-circuits the pipeline, allowing you to implement authentication guards that work across Node, Bun, Deno, and Cloudflare Workers with minimal platform-specific adjustments.**

SXO (Simple X Open) is a lightweight framework designed to run seamlessly across multiple JavaScript runtimes. According to the gc-victor/sxo source code, the middleware architecture leverages Web Standard `Request` and `Response` objects, allowing you to write authentication logic once and deploy it everywhere while still accessing platform-specific secrets and bindings.

## Understanding SXO's Middleware Architecture

The middleware pipeline in SXO consists of three core components that work together to process requests before they reach your SSR handlers.

### The Middleware Loader

Located in [`src/js/server/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/middleware.js), the loader scans your project for `SRC_DIR/middleware.{js,ts,mjs}`. It accepts a default export (either a function or array) or named exports `middlewares`, `middleware`, or `mw`. The loader returns an array of functions, each conforming to the Web Standard signature `(request, env?) => Response | void | Promise<...>`.

### The Middleware Executor

The `executeMiddleware` function, re-exported from [`src/js/runtime/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/js/runtime/middleware.js) and defined in [`src/js/server/core-handler.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/core-handler.js), runs loaded functions sequentially. If any middleware returns a `Response` object, the pipeline short-circuits immediately, sending that response to the client without proceeding to route matching or SSR.

### Platform Integration Points

Each runtime adapter injects the middleware array into the request handler created by `createProdHandler`. The Node adapter ([`src/js/server/prod/node.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/prod/node.js)), Bun adapter ([`src/js/server/prod/bun.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/prod/bun.js)), Deno adapter ([`src/js/server/prod/deno.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/prod/deno.js)), and Cloudflare Workers adapter ([`src/js/server/prod/workers.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/prod/workers.js)) all follow this pattern, ensuring consistent middleware execution across platforms.

## Creating Authentication Middleware for Different Platforms

To implement authentication, create [`src/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/middleware.js) in your project root. While the core logic remains consistent across runtimes, accessing environment variables and platform bindings requires platform-specific syntax.

### Node and Bun

Both Node.js and Bun use `process.env` for environment variables. Your middleware receives the standard `request` object as the first argument.

```javascript
// src/middleware.js
export default async function auth(request) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  try {
    const payload = await verifyJwt(token, process.env.JWT_SECRET);
    request.user = payload;
  } catch (e) {
    return new Response("Invalid token", { status: 401 });
  }
}

```

### Deno

Deno requires explicit permission to access environment variables using `Deno.env.get()`. Ensure you run Deno with the `--allow-env` flag.

```javascript
// src/middleware.js
export default async function auth(request) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  const secret = Deno.env.get("JWT_SECRET");
  if (!secret) {
    return new Response("Server misconfigured", { status: 500 });
  }

  try {
    const payload = await verifyJwt(token, secret);
    request.user = payload;
  } catch {
    return new Response("Invalid token", { status: 401 });
  }
}

```

### Cloudflare Workers

Cloudflare Workers pass platform-specific bindings (KV namespaces, Durable Objects, secrets) via the `env` argument, which is the second parameter to your middleware function. Define these bindings in your [`wrangler.toml`](https://github.com/gc-victor/sxo/blob/main/wrangler.toml) configuration.

```javascript
// src/middleware.js
export default async function auth(request, env) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response("Missing token", { status: 401 });
  }

  const token = authHeader.slice(7);
  const stored = await env.AUTH_TOKENS.get(token);
  if (!stored) {
    return new Response("Invalid token", { status: 401 });
  }

  request.user = JSON.parse(stored);
}

```

## How Middleware Hooks Into the Request Pipeline

Understanding the request flow helps debug authentication issues. When SXO starts a production server, the platform adapter calls `loadUserDefinedMiddlewares()` from [`src/js/server/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/middleware.js) to retrieve your middleware array. This array is passed to `createProdHandler` in [`src/js/server/core-handler.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/core-handler.js) via the `getMiddleware` option.

When an HTTP request arrives, the adapter creates a Web Standard `Request` object and invokes the unified fetch handler. This triggers `executeMiddleware`, which iterates through your authentication functions. If your middleware returns a `Response` (such as a 401 Unauthorized), the pipeline stops immediately. Otherwise, the request proceeds to route matching and SSR.

During development, the loader adds a cache-busting query string (`?t=${Date.now()}`) to [`middleware.js`](https://github.com/gc-victor/sxo/blob/main/middleware.js), enabling hot-reload without server restarts.

## Platform-Specific Environment Access

While the middleware API remains consistent across runtimes, accessing secrets and platform bindings requires platform-specific syntax:

- **Node and Bun**: Use `process.env.VAR_NAME` for environment variables.
- **Deno**: Use `Deno.env.get("VAR_NAME")` and ensure the `--allow-env` flag is set.
- **Cloudflare Workers**: Access bindings via the `env` parameter (second argument), which receives KV namespaces, Durable Objects, and secrets as defined in [`wrangler.toml`](https://github.com/gc-victor/sxo/blob/main/wrangler.toml).

This design allows you to write the core authentication logic once and adapt only the configuration retrieval for each deployment target.

## Summary

- SXO uses a **Web Standard middleware system** defined in [`src/js/server/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/middleware.js) and executed by `executeMiddleware` in [`src/js/server/core-handler.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/core-handler.js).
- Create [`src/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/middleware.js) and export a default function with the signature `(request, env?) => Response | void | Promise<...>`.
- Return a `Response` from your middleware to short-circuit the pipeline, such as sending a 401 for failed authentication.
- **Node/Bun**: Access secrets via `process.env`.
- **Deno**: Access secrets via `Deno.env.get()` with the `--allow-env` flag.
- **Cloudflare Workers**: Access bindings via the `env` argument (second parameter).
- Platform adapters in `src/js/server/prod/*.js` ensure consistent middleware injection across all runtimes.

## Frequently Asked Questions

### What file should I create for SXO middleware?

Create a file named [`middleware.js`](https://github.com/gc-victor/sxo/blob/main/middleware.js) (or `.ts`/`.mjs`) inside your `src` directory (or the directory specified by the `SRC_DIR` environment variable). SXO's loader, located at [`src/js/server/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/middleware.js), automatically discovers this file and expects either a default export containing a function or array of functions, or named exports called `middlewares`, `middleware`, or `mw`.

### How does SXO handle middleware errors?

If your middleware throws an uncaught exception, SXO's `executeMiddleware` function in [`src/js/server/core-handler.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/core-handler.js) will propagate the error. Since middleware runs within the platform's fetch handler, error handling depends on the runtime's default behavior for unhandled exceptions. For production stability, wrap your authentication checks in try-catch blocks and return explicit error Responses with appropriate status codes (such as 500 for server errors or 401 for authentication failures) rather than throwing raw errors.

### Can I use the same middleware across all SXO platforms?

Yes. The middleware API is runtime-agnostic and uses Web Standard `Request` and `Response` objects, allowing you to share the same [`src/middleware.js`](https://github.com/gc-victor/sxo/blob/main/src/middleware.js) file across Node, Bun, Deno, and Cloudflare Workers deployments. Only the method for accessing environment variables or platform bindings differs between runtimes (such as `process.env` for Node/Bun, `Deno.env.get` for Deno, and the `env` parameter for Cloudflare Workers), which you can handle with simple runtime detection or by using the `env` parameter where available.

### How do I access Cloudflare KV in SXO middleware?

In Cloudflare Workers, platform bindings including KV namespaces are passed via the `env` argument, which is the second parameter to your middleware function. First, bind your KV namespace in [`wrangler.toml`](https://github.com/gc-victor/sxo/blob/main/wrangler.toml) (for example, `[[kv_namespaces]] binding = "AUTH_TOKENS"`). Then access it in your middleware via `env.AUTH_TOKENS.get(key)`. The Cloudflare Workers adapter at [`src/js/server/prod/workers.js`](https://github.com/gc-victor/sxo/blob/main/src/js/server/prod/workers.js) ensures the `env` object flows through the request handler chain to your middleware automatically.