Configuration
Full configuration reference for defineKeyloom - sessions, cookies, providers, adapters, and RBAC.
Configuration
Keyloom is configured via defineKeyloom.
import { defineKeyloom, memoryAdapter } from "@keyloom/core";
export default defineKeyloom({
baseUrl: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000",
session: { strategy: "database", ttlMinutes: 60, rolling: true },
adapter: memoryAdapter(),
providers: [],
rbac: { enabled: false },
cookie: { name: "keyloom", sameSite: "lax", secure: true, path: "/" },
secrets: { authSecret: process.env.AUTH_SECRET ?? "dev-secret" },
});Configuration Options
Core Settings
baseUrl: string (required)
External URL of your application used for OAuth callbacks and email links.
baseUrl: process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"adapter: Adapter (required)
Database adapter for persisting users, sessions, and tokens.
import { PrismaAdapter } from "@keyloom/adapters";
import { PrismaClient } from "@prisma/client";
const db = new PrismaClient();
adapter: PrismaAdapter(db)secrets: { authSecret: string } (required)
Secret key for encryption and signing. Use base64url encoding for best security.
secrets: {
authSecret: process.env.AUTH_SECRET! // Generate with: openssl rand -base64url 32
}
#### `magicLink?: MagicLinkConfig` (optional)
Magic link (passwordless) authentication configuration.
```ts
magicLink: {
enabled: true,
defaultTtlMinutes: 15,
autoCreateUser: true,
verifyPath: "/auth/magic-link/verify",
}email?: EmailConfig (optional)
Email provider configuration for magic link authentication.
email: {
provider: {
type: "smtp", // or "resend"
config: {
host: process.env.SMTP_HOST!,
port: parseInt(process.env.SMTP_PORT!),
secure: false,
auth: {
user: process.env.SMTP_USER!,
pass: process.env.SMTP_PASS!,
},
},
},
from: process.env.EMAIL_FROM!,
}appName?: string (optional)
Application name used in email templates and user-facing messages.
appName: "My App"
### Session Configuration
#### `session.strategy: "jwt" | "database"` (default: "database")
- **database**: Sessions stored in database, server-side revocation
- **jwt**: Stateless JWT tokens, edge-friendly, no database queries
#### `session.ttlMinutes: number` (default: 60)
Session time-to-live in minutes.
#### `session.rolling: boolean` (default: true)
Whether to refresh session expiration on activity.
```ts
session: {
strategy: "database",
ttlMinutes: 60 * 24, // 24 hours
rolling: true
}Cookie Configuration
cookie.name: string (default: "keyloom")
Name of the session cookie.
cookie.sameSite: "lax" | "strict" | "none" (default: "lax")
SameSite cookie attribute for CSRF protection.
cookie.secure: boolean (default: auto-detected)
Whether cookies require HTTPS. Auto-detected based on baseUrl.
cookie.maxAgeSec: number (default: 604800 = 7 days)
Maximum age of cookies in seconds.
cookie: {
name: "my-app-session",
sameSite: "lax",
secure: true,
path: "/",
domain: ".example.com", // Optional: for subdomain sharing
maxAgeSec: 60 * 60 * 24 * 30 // 30 days
}JWT Configuration (when strategy = "jwt")
jwt.algorithm: string (default: "EdDSA")
JWT signing algorithm. Supported: EdDSA, RS256, HS256.
jwt.accessTTL: string (default: "10m")
Access token time-to-live (e.g., "10m", "1h", "1d").
jwt.refreshTTL: string (default: "30d")
Refresh token time-to-live.
jwt.clockSkewSec: number (default: 60)
Clock skew tolerance in seconds for JWT validation.
jwt.includeOrgRoleInAccess: boolean (default: false)
Whether to include organization roles in access tokens.
jwt: {
algorithm: "EdDSA",
accessTTL: "15m",
refreshTTL: "7d",
clockSkewSec: 60,
includeOrgRoleInAccess: true,
keyRotationDays: 90,
keyOverlapDays: 7
}Provider Configuration
providers: Provider[] (default: [])
Array of OAuth/OpenID Connect providers.
import { github, google, microsoft } from "@keyloom/providers";
providers: [
github({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scopes: ["read:user", "user:email"]
}),
google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
})
]RBAC Configuration
rbac.enabled: boolean (default: false)
Enable role-based access control features.
rbac.defaultRole: string (optional)
Default role assigned to new organization members.
rbac.roles: Record<string, string[]> (optional)
Role definitions mapping role names to permission arrays.
rbac: {
enabled: true,
defaultRole: "member",
roles: {
owner: ["*"],
admin: ["users:*", "billing:*", "settings:*"],
member: ["users:read", "content:*"],
viewer: ["users:read", "content:read"]
}
}Plugin Configuration
plugins: KeyloomPlugin[] (default: [])
Array of Keyloom plugins to extend functionality.
import { createPasskeyPlugin } from "@keyloom/plugin-passkey";
plugins: [
createPasskeyPlugin()
]Environment Variables
Required
# Core authentication secret (generate with: openssl rand -base64url 32)
AUTH_SECRET=your-secret-key-here
# Application URL for OAuth callbacks
NEXT_PUBLIC_APP_URL=https://your-app.comProvider Secrets
# GitHub OAuth
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Microsoft OAuth
MICROSOFT_CLIENT_ID=your-microsoft-client-id
MICROSOFT_CLIENT_SECRET=your-microsoft-client-secretDatabase (if using database adapter)
# Prisma
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# Or other database connection stringsStrategy Comparison
Database Strategy
Pros:
- Server-side session revocation
- Session introspection and management
- Audit trails and session monitoring
- Fine-grained control over session lifecycle
Cons:
- Database queries on every request
- More complex horizontal scaling
- Requires database adapter
Best for: Traditional web applications, admin dashboards, applications requiring session management
JWT Strategy
Pros:
- Stateless and edge-friendly
- No database queries for session validation
- Simple horizontal scaling
- Works well with CDNs and edge functions
Cons:
- Cannot revoke tokens before expiration
- Larger cookie/header size
- Token rotation complexity
Best for: APIs, microservices, edge-deployed applications, high-scale stateless apps
Configuration Validation
Keyloom validates your configuration at startup:
import { validateKeyloomConfig } from "@keyloom/core";
try {
validateKeyloomConfig(config);
} catch (error) {
console.error("Configuration error:", error.message);
}Common validation errors:
- Missing required
adapterwhen using database strategy - Invalid
cookie.sameSitevalues - Negative
session.ttlMinutes - Invalid JWT configuration
Best Practices
Security
- Use strong, randomly generated
AUTH_SECRET(32+ bytes) - Enable
cookie.securein production (HTTPS) - Use
sameSite: "lax"for CSRF protection - Rotate secrets regularly
- Store secrets in environment variables, never in code
Performance
- Choose JWT strategy for high-scale, stateless applications
- Use database strategy when you need session management features
- Configure appropriate session TTL based on security requirements
- Use connection pooling for database adapters
Development
- Start with
memoryAdapter()for local development - Use shorter session TTL in development for testing
- Enable detailed error logging in development
- Test OAuth flows with ngrok or similar tunneling tools
Migration Guide
From JWT to Database Strategy
- Add database adapter to configuration
- Update session strategy
- Run database migrations
- Update server-side session reading code
- Test session persistence and revocation
From Database to JWT Strategy
- Configure JWT settings
- Update session strategy
- Remove database adapter (optional)
- Update client-side session handling
- Test token refresh flows
Troubleshooting
Configuration not loading
- Check file path and export syntax
- Verify environment variables are set
- Check for TypeScript compilation errors
OAuth callback errors
- Verify
baseUrlmatches your domain - Check provider callback URL configuration
- Ensure HTTPS in production
Session not persisting
- Check cookie configuration
- Verify database adapter connection
- Check browser cookie settings and HTTPS requirements
See also
How is this guide?