Skip to content

Authentication ​

racletteJS uses session-based authentication with opaque tokens stored in Valkey (Redis). Sessions are transmitted via HttpOnly cookies, providing secure, stateless-from-the-client authentication.

Overview ​

  • Session store: Valkey (Redis-compatible) with configurable TTL
  • Transport: HttpOnly session cookie (name configured via auth.cookie.name; no tokens in localStorage)
  • Admin detection: Tag-based (ADMIN tag in raclette__core-tag)
  • OAuth: Arctic library with 60+ built-in providers + custom OAuth2 support
  • Permissions: Resolver chain with req.can("permission") API

Configuration ​

Authentication is configured in your raclette.config.js (top-level auth block; merged into generated backend config):

js
export default {
  auth: {
    session: {
      ttl: 86400, // Session lifetime in seconds (default: 24h)
      ttlMode: "sliding", // "sliding" resets TTL on activity, "absolute" expires at fixed time
      cleanupInterval: 900,  // How often to prune stale session references (seconds)
    },
    cookie: {
      name: "sid_app", // Cookie name (default: "sid_app" for product apps)
      secure: true, // Set to false for local dev without HTTPS
      sameSite: "strict", // "strict" | "lax" | "none"
      domain: undefined, // Optional: restrict cookie to shared parent domain
    },
    valkey: {
      keyPrefix: "auth:", // Prefix for all auth-related Valkey keys
    },
    // Optional: map auth concerns to named Valkey instance ids (default: all "cache")
    // valkeyInstances: {
    // sessions: "cache",
    // oauthState: "cache",
    // providerTokens: "cache",
    // },
    providers: {
      custom: [], // Custom OAuth2 providers (see OAuth section)
    },
  },
}

Most apps use one Valkey instance with no persistence. The sessions, oauthState, and providerTokens keys are roles, not three mandatory servers β€” they can all point at "cache". See Valkey instances for multi-instance and persistence setups.

Multiple apps on the same host (product + workbench) ​

Browsers store one cookie per name per host (port is ignored). If product view and workbench both used the same cookie name, logging into one app would overwrite the other’s session cookie.

Use a distinct auth.cookie.name per deployment, for example:

DeploymentSuggested auth.cookie.name
Product / main appsid_app (default)
Workbenchsid_workbench

Example workbench snippet:

js
auth: {
  cookie: {
    name: "sid_workbench",
    secure: false, // typical for local HTTP dev
    sameSite: "lax",
  },
},

Use the same hostname for all frontends in dev (e.g. always localhost, not mixed with 127.0.0.1).

How It Works ​

  1. Login (POST /auth/login): Verifies email/password, creates a session in Valkey, sets the configured HttpOnly session cookie.
  2. Every request: The global onRequest hook reads that cookie, looks up the session in Valkey, and populates req.authUser with { userId, isAdmin, provider }.
  3. Protected routes: Use fastify.authenticate as a preHandler to reject unauthenticated requests (401) or non-admin requests when admin-tag is required (403).
  4. Logout (POST /auth/logout): Deletes the session from Valkey and clears the cookie.

Multiple sessions per user (different browsers or devices) are supported. Each login creates a new session; only POST /auth/logout-all invalidates every session for that user.

API Endpoints ​

MethodPathDescription
POST/auth/loginEmail/password login
POST/auth/logoutEnd current session
POST/auth/logout-allEnd all sessions for the current user
GET/auth/sessionCheck current session (add ?expand=user for full user data)
GET/auth/oauth/return-to/csrfCSRF token for return-to (login page only)
POST/auth/oauth/return-toStore post-OAuth return path (httpOnly cookie + CSRF, login page)
GET/auth/oauth/:providerInitiate OAuth flow
GET/auth/oauth/:provider/callbackOAuth callback
GET/auth/oauth/providersList enabled OAuth providers for login page (public)
GET/auth/available-providersList all available OAuth providers (admin)
GET/auth/providersList configured OAuth providers (admin)
POST/auth/providersCreate OAuth provider config (admin)
PATCH/auth/providers/:idUpdate provider config
DELETE/auth/providers/:idDelete provider config
GET/auth/link/confirmCheck pending account link
POST/auth/link/confirmConfirm account link
DELETE/auth/link/confirmDecline account link
GET/auth/asset/:tokenConsume single-use asset download token
GET/auth/session-asset/:tokenDownload via session-bound (multi-use) asset token

Shared project links use a work session document ID (UUID). Guests do not receive a session cookie for the main app.

  • HTTP: send x-work-session-id (the client sets this from the URL).
  • WebSocket: pass sessionId in the socket handshake auth object.

Each request is validated server-side: document exists, isActive, not expired, and the route’s plugin key appears in pluginData. Routes using fastify.authenticatedOrWorksession(pluginKey) reject mutating HTTP methods for work-session callers (read-only). Sensitive plugin APIs should keep fastify.authenticate so only logged-in users can call them; guests typically receive composition data via socket joinConfirmation after validation.

Socket authentication uses the same auth.cookie.name as HTTP when validating the session cookie on the handshake.

Environment Variables ​

VariableDescriptionDefault
AUTH_CALLBACK_BASE_URLBase URL for OAuth callbacksSERVER_URL or http://localhost:3000
RACLETTE_AUTH_COOKIE_NAMEOverrides auth.cookie.name (useful when the backend loads another app’s generated raclette.config.js, e.g. workbench β†’ sid_workbench)From config / sid_app or sid_workbench