Auth

Connection auth - upgrade()

The upgrade function in src/hooks.ws.js runs on every new WebSocket connection. Return user data to attach to the connection, or false to reject it.

// src/hooks.ws.js
export function upgrade({ cookies }) {
  const session = validateSession(cookies.session_id);
  if (!session) return false;
  return { id: session.userId, name: session.name };
}

Whatever you return is available as ctx.user in all live() functions.

Per-module auth - guard()

Export a _guard from any src/live/ file. It runs before every function in that file.

// src/live/admin.js
import { live, guard, LiveError } from 'svelte-realtime/server';

export const _guard = guard((ctx) => {
  if (ctx.user?.role !== 'admin')
    throw new LiveError('FORBIDDEN', 'Admin only');
});

export const deleteUser = live(async (ctx, userId) => {
  await db.users.delete(userId);
});

Both functions in this file require admin access.

Composable guards

Chain multiple guards. They run in order, and earlier ones can enrich ctx for later ones:

export const _guard = guard(
  (ctx) => {
    if (!ctx.user) throw new LiveError('UNAUTHORIZED');
  },
  (ctx) => {
    ctx.permissions = lookupPermissions(ctx.user.id);
  },
  (ctx) => {
    if (!ctx.permissions.includes('write'))
      throw new LiveError('FORBIDDEN');
  }
);

Per-function auth

Check ctx.user inside any live() function:

export const deleteItem = live(async (ctx, id) => {
  if (!ctx.user) throw new LiveError('UNAUTHORIZED');
  if (ctx.user.role !== 'admin') throw new LiveError('FORBIDDEN');
  await db.items.delete(id);
});

Stream access control

Use the access option on streams to filter events per connection:

export const notifications = live.stream('notifications', async (ctx) => {
  return db.notifications.forUser(ctx.user.id);
}, {
  merge: 'crud',
  access(ctx, event, data) {
    return data.userId === ctx.user.id;
  }
});

Only events where access returns true are sent to that client.

Global middleware

Use live.middleware() for cross-cutting auth:

import { live, LiveError } from 'svelte-realtime/server';

live.middleware(async (ctx, next) => {
  if (!ctx.user) throw new LiveError('UNAUTHORIZED');
  return next();
});

Under the hood, upgrade() runs inside the adapter’s WebSocket handshake. See svelte-adapter-uws WebSocket docs for the full connection lifecycle.

Was this page helpful?