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 (
ADMINtag inraclette__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):
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:
| Deployment | Suggested auth.cookie.name |
|---|---|
| Product / main app | sid_app (default) |
| Workbench | sid_workbench |
Example workbench snippet:
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 β
- Login (
POST /auth/login): Verifies email/password, creates a session in Valkey, sets the configured HttpOnly session cookie. - Every request: The global
onRequesthook reads that cookie, looks up the session in Valkey, and populatesreq.authUserwith{ userId, isAdmin, provider }. - Protected routes: Use
fastify.authenticateas a preHandler to reject unauthenticated requests (401) or non-admin requests when admin-tag is required (403). - 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 β
| Method | Path | Description |
|---|---|---|
POST | /auth/login | Email/password login |
POST | /auth/logout | End current session |
POST | /auth/logout-all | End all sessions for the current user |
GET | /auth/session | Check current session (add ?expand=user for full user data) |
GET | /auth/oauth/return-to/csrf | CSRF token for return-to (login page only) |
POST | /auth/oauth/return-to | Store post-OAuth return path (httpOnly cookie + CSRF, login page) |
GET | /auth/oauth/:provider | Initiate OAuth flow |
GET | /auth/oauth/:provider/callback | OAuth callback |
GET | /auth/oauth/providers | List enabled OAuth providers for login page (public) |
GET | /auth/available-providers | List all available OAuth providers (admin) |
GET | /auth/providers | List configured OAuth providers (admin) |
POST | /auth/providers | Create OAuth provider config (admin) |
PATCH | /auth/providers/:id | Update provider config |
DELETE | /auth/providers/:id | Delete provider config |
GET | /auth/link/confirm | Check pending account link |
POST | /auth/link/confirm | Confirm account link |
DELETE | /auth/link/confirm | Decline account link |
GET | /auth/asset/:token | Consume single-use asset download token |
GET | /auth/session-asset/:token | Download via session-bound (multi-use) asset token |
Work sessions (guest links) β
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
sessionIdin the socket handshakeauthobject.
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 β
| Variable | Description | Default |
|---|---|---|
AUTH_CALLBACK_BASE_URL | Base URL for OAuth callbacks | SERVER_URL or http://localhost:3000 |
RACLETTE_AUTH_COOKIE_NAME | Overrides 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 |
Related β
- Valkey instances β one vs multiple Valkey services, persistence,
auth.valkeyInstances - Setting up OAuth Providers β includes redirect URI, login-page flow, and userinfo contract
- Asset Downloads β single-use and session-bound download links (
assetLink) - Core 0.1.x β 0.2.x: App upgrade β packages, config, env, landing routes
- Core 0.1.x β 0.2.x: Plugin auth β
req.userβreq.authUser