solidstart-middleware-auth

SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.

16 stars

Best use case

solidstart-middleware-auth is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.

Teams using solidstart-middleware-auth should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/solidstart-middleware-auth/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/backend/solidstart-middleware-auth/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/solidstart-middleware-auth/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How solidstart-middleware-auth Compares

Feature / Agentsolidstart-middleware-authStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

SolidStart middleware, sessions, authentication: createMiddleware with onRequest/onBeforeResponse, useSession for cookies, protected routes, WebSocket endpoints.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# SolidStart Middleware, Sessions & Auth

## Middleware

Configure in `app.config.ts`:
```ts
export default defineConfig({
  middleware: "src/middleware/index.ts"
});
```

### When to Use Middleware (and When Not To)

Use middleware for:
- Request/response header management (CSP, cache control, CORS)
- Request-scoped data via `event.locals` (trace IDs, feature flags, auth hints)
- Early redirects (legacy URLs, locale routing, maintenance mode)
- Lightweight preprocessing (normalizing headers, basic validation)

Avoid middleware for:
- Authorization (middleware does not run on every client-side navigation)
- Heavy computation or database calls (keep it fast and side-effect light)
- Business logic that depends on user state (put checks in queries/actions/API)

### Basic Middleware

```ts
// src/middleware/index.ts
import { createMiddleware } from "@solidjs/start/middleware";

export default createMiddleware({
  onRequest: (event) => {
    console.log("Request:", event.request.url);
    event.locals.startTime = Date.now();
  },
  onBeforeResponse: (event) => {
    const duration = Date.now() - event.locals.startTime;
    console.log(`Request took ${duration}ms`);
  }
});
```

### Lifecycle Events

- `onRequest`: Before route handler (modify headers, store in locals)
- `onBeforeResponse`: After route handler (modify response, logging)

Returning a `Response` from either hook short-circuits the pipeline and skips
remaining middleware and route handlers.

```ts
export default createMiddleware({
  onRequest: (event) => {
    event.locals.userAgent = event.request.headers.get("user-agent");
  },
  onBeforeResponse: (event) => {
    event.response.headers.set("X-Response-Time", "100ms");
  }
});
```

### Accessing Locals

Store in middleware, access with `getRequestEvent`:

```ts
// middleware/index.ts
export default createMiddleware({
  onRequest: (event) => {
    event.locals.user = { id: "123", name: "John" };
  }
});

// In server function
import { getRequestEvent } from "solid-js/web";

const getUser = query(async () => {
  "use server";
  const event = getRequestEvent();
  return event?.locals?.user;
}, "user");
```

### Typing Locals

```ts
// global.d.ts
/// <reference types="@solidjs/start/env" />
declare module "App" {
  interface RequestEventLocals {
    user?: { id: string; name: string };
    startTime?: number;
  }
}
```

### Headers & Cookies

```ts
import { getCookie, setCookie } from "vinxi/http";

export default createMiddleware({
  onRequest: (event) => {
    // Read headers
    const userAgent = event.request.headers.get("user-agent");
    
    // Set headers
    event.request.headers.set("x-custom-header", "value");
    event.response.headers.set("Cache-Control", "max-age=3600");
    
    // Cookies
    const theme = getCookie(event.nativeEvent, "theme");
    setCookie(event.nativeEvent, "session", "abc123", {
      httpOnly: true,
      secure: true,
      maxAge: 60 * 60 * 24
    });
  }
});
```

### Custom Responses & Short-Circuiting

Only `Response` objects can be returned from middleware.

```ts
import { json, redirect } from "@solidjs/router";

export default createMiddleware({
  onRequest: (event) => {
    const { pathname } = new URL(event.request.url);
    if (pathname === "/old-path") {
      return redirect("/new-path", 301);
    }

    const authHeader = event.request.headers.get("Authorization");
    if (!authHeader) {
      return json({ error: "Unauthorized" }, { status: 401 });
    }
  }
});
```

### Building Custom Middleware (Composable Functions)

Prefer small, focused functions and compose them in order.

```ts
import type { FetchEvent } from "@solidjs/start/server";

function withRequestId() {
  return (event: FetchEvent) => {
    const id = crypto.randomUUID();
    event.locals.requestId = id;
    event.response.headers.set("x-request-id", id);
  };
}

function withTiming() {
  return (event: FetchEvent) => {
    event.locals.startTime = Date.now();
  };
}

function withTimingResponse() {
  return (event: FetchEvent) => {
    const ms = Date.now() - event.locals.startTime;
    event.response.headers.set("x-response-time", `${ms}ms`);
  };
}

export default createMiddleware({
  onRequest: [withRequestId(), withTiming()],
  onBeforeResponse: [withTimingResponse()]
});
```

### Extending Existing Middleware

Export shared middleware arrays, then extend them in route-specific configs.

```ts
// src/middleware/base.ts
import type { FetchEvent } from "@solidjs/start/server";

export const baseOnRequest: Array<(e: FetchEvent) => void> = [
  (event) => {
    event.locals.userAgent = event.request.headers.get("user-agent");
  }
];
```

```ts
// src/middleware/index.ts
import { createMiddleware } from "@solidjs/start/middleware";
import { baseOnRequest } from "./base";
import type { FetchEvent } from "@solidjs/start/server";

function withMaintenanceMode() {
  return (event: FetchEvent) => {
    if (process.env.MAINTENANCE === "true") {
      return new Response("Maintenance", { status: 503 });
    }
  };
}

export default createMiddleware({
  onRequest: [...baseOnRequest, withMaintenanceMode()]
});
```

### Redirects & Responses

```ts
import { redirect, json } from "@solidjs/router";

export default createMiddleware({
  onRequest: (event) => {
    const { pathname } = new URL(event.request.url);
    if (pathname === "/old-path") {
      return redirect("/new-path", 301);
    }
    
    const authHeader = event.request.headers.get("Authorization");
    if (!authHeader) {
      return json({ error: "Unauthorized" }, { status: 401 });
    }
  }
});
```

### Chaining Middleware

```ts
function middleware1(event: FetchEvent) {
  event.request.headers.set("x-header1", "value1");
}

function middleware2(event: FetchEvent) {
  event.request.headers.set("x-header2", "value2");
}

export default createMiddleware({
  onRequest: [middleware1, middleware2]
});
```

Order matters: middleware runs in array order; dependencies must come first.

## Sessions

### Basic Session

```ts
// src/lib/session.ts
import { useSession } from "vinxi/http";

type SessionData = {
  userId?: string;
  theme?: "light" | "dark";
};

export async function useAppSession() {
  "use server";
  const session = await useSession<SessionData>({
    password: process.env.SESSION_SECRET!, // Must be 32+ chars
    name: "app-session"
  });
  return session;
}

// Generate: openssl rand -base64 32
```

### Session Operations

```ts
// Get session data
export async function getSessionUserId() {
  "use server";
  const session = await useAppSession();
  return session.data.userId;
}

// Update session
export async function updateSession(updates: Partial<SessionData>) {
  "use server";
  const session = await useAppSession();
  await session.update(updates);
}

// Clear session
export async function clearSession() {
  "use server";
  const session = await useAppSession();
  await session.clear();
}
```

### Using with Queries

```ts
const getCurrentUser = query(async () => {
  "use server";
  const session = await useAppSession();
  if (!session.data.userId) {
    throw redirect("/login");
  }
  return await db.getUser(session.data.userId);
}, "currentUser");
```

## Authentication

### Protected Routes

```tsx
// routes/admin.tsx
import { query, redirect, createAsync } from "@solidjs/router";

const getAdminData = query(async () => {
  "use server";
  const session = await useAppSession();
  if (!session.data.userId) {
    throw redirect("/login");
  }
  
  const user = await db.getUser(session.data.userId);
  if (!user.isAdmin) {
    throw redirect("/");
  }
  
  return await db.getAdminData();
}, "adminData");

export default function AdminPage() {
  const data = createAsync(() => getAdminData(), { deferStream: true });
  return <div>{data()}</div>;
}
```

**Important:** Use `deferStream: true` - server-side redirects can't occur after streaming starts.

### Login Action

```tsx
const loginAction = action(async (formData: FormData) => {
  "use server";
  const email = formData.get("email")?.toString();
  const password = formData.get("password")?.toString();
  
  const user = await db.verifyUser(email, password);
  if (!user) {
    return { error: "Invalid credentials" };
  }
  
  const session = await useAppSession();
  await session.update({ userId: user.id });
  
  throw redirect("/dashboard");
}, "login");
```

### Logout Action

```tsx
const logoutAction = action(async () => {
  "use server";
  const session = await useAppSession();
  await session.clear();
  throw redirect("/");
}, "logout");
```

## WebSocket Endpoints

**Experimental** - use with caution.

### Configuration

```ts
// app.config.ts
export default defineConfig({
  server: {
    experimental: { websocket: true }
  }
}).addRouter({
  name: "ws",
  type: "http",
  handler: "./src/ws.ts",
  target: "server",
  base: "/ws"
});
```

### WebSocket Handler

```ts
// src/ws.ts
import { eventHandler } from "vinxi/http";

export default eventHandler({
  handler() {},
  websocket: {
    async open(peer) {
      console.log("Connection:", peer.id);
    },
    async message(peer, msg) {
      const message = msg.text();
      peer.send(message); // Broadcast
    },
    async close(peer, details) {
      console.log("Closed:", peer.id);
    },
    async error(peer, error) {
      console.error("Error:", error);
    }
  }
});
```

## Best Practices

1. Keep middleware lightweight - no heavy computation
2. Use sessions for auth state - secure, encrypted cookies
3. Use `deferStream: true` for protected routes
4. Type your locals for better TypeScript support
5. Don't rely on middleware for authorization (doesn't run on all requests)
6. Return a `Response` only when you want to terminate the pipeline
7. Compose small middleware functions and keep ordering explicit

Related Skills

convex-component-authoring

16
from diegosouzapw/awesome-omni-skill

How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management

agentuity-cli-auth-login

16
from diegosouzapw/awesome-omni-skill

Login to the Agentuity Platform using a browser-based authentication flow. Use for managing authentication credentials

agent-command-authoring

16
from diegosouzapw/awesome-omni-skill

Create Claude Code slash commands and OpenCode command files that delegate to subagents. Use when creating new commands or refactoring existing ones to follow the delegation pattern.

agent-authoring

16
from diegosouzapw/awesome-omni-skill

Guide for authoring specialized AI agents. Use when creating, updating, or improving agents, choosing models, defining focus areas, configuring tools, or learning agent best practices.

agentuity-cli-project-auth-init

16
from diegosouzapw/awesome-omni-skill

Set up Agentuity Auth for your project. Requires authentication. Use for managing authentication credentials

agentuity-cli-auth-ssh-list

16
from diegosouzapw/awesome-omni-skill

List all SSH keys on your account. Requires authentication. Use for managing authentication credentials

agentuity-cli-auth-ssh-delete

16
from diegosouzapw/awesome-omni-skill

Delete an SSH key from your account. Requires authentication. Use for managing authentication credentials

agentuity-cli-auth-ssh-add

16
from diegosouzapw/awesome-omni-skill

Add an SSH public key to your account (reads from file or stdin). Requires authentication. Use for managing authentication credentials

agentuity-cli-auth-org-unselect

16
from diegosouzapw/awesome-omni-skill

Clear the default organization preference. Use for managing authentication credentials

agentuity-cli-auth-org-current

16
from diegosouzapw/awesome-omni-skill

Show the current default organization. Use for managing authentication credentials

agentuity-cli-auth-machine-setup

16
from diegosouzapw/awesome-omni-skill

Set up machine authentication by uploading a public key for self-hosted infrastructure. Requires authentication. Use for managing authentication credentials

two-factor-authentication-best-practices

16
from diegosouzapw/awesome-omni-skill

This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.