Learn about all available configuration options for self-hosting Better Auth Studio.
StudioConfig Type
The StudioConfig type defines all configuration options for Better Auth Studio:
import type { StudioConfig } from "better-auth-studio" ;
const config : StudioConfig = {
auth , // Required: Your Better Auth instance
basePath , // Required: URL path where studio is mounted
access , // Optional: Access control
metadata , // Optional: Branding and UI customization
lastSeenAt , // Optional: Last-seen tracking
ipAddress , // Optional: IP geolocation
events , // Optional: Event tracking
tools , // Optional: Tools configuration
};
Required Options
auth
Type: BetterAuth
Your Better Auth instance. This is required for the studio to access your authentication data.
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
};
basePath
Type: string
The URL path where the studio is mounted. Must match the route where you mount the handler.
const config : StudioConfig = {
auth ,
basePath: "/api/studio" , // Access at http://localhost:3000/api/studio
};
The basePath must match your framework’s route path. For example, if using Next.js with app/api/studio/[[...path]]/route.ts, set basePath: "/api/studio".
Access Control
access
Type: StudioAccessConfig
Control who can access the studio interface.
type StudioAccessConfig = {
roles ?: string []; // Array of allowed user roles
allowEmails ?: string []; // Array of allowed email addresses
sessionDuration ?: number ; // Session duration in seconds
secret ?: string ; // Secret for session encryption
};
access.roles
Restrict access to users with specific roles:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
access: {
roles: [ "admin" , "super-admin" ],
},
};
access.allowEmails
Restrict access to specific email addresses:
Combining Roles and Emails
You can combine both for flexible access control:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
access: {
roles: [ "admin" ],
allowEmails: [ "[email protected] " ], // This user gets access even without admin role
},
};
Type: StudioMetadata
Customize the studio’s appearance and branding.
type StudioMetadata = {
title ?: string ;
logo ?: string ;
favicon ?: string ;
company ?: {
name ?: string ;
website ?: string ;
supportEmail ?: string ;
};
theme ?: "dark" | "light" | "auto" ;
colors ?: {
primary ?: string ;
secondary ?: string ;
accent ?: string ;
};
features ?: {
users ?: boolean ;
sessions ?: boolean ;
organizations ?: boolean ;
analytics ?: boolean ;
tools ?: boolean ;
security ?: boolean ;
};
links ?: Array <{ label : string ; url : string }>;
custom ?: Record < string , any >;
};
Basic Branding
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
metadata: {
title: "Acme Admin Dashboard" ,
theme: "dark" ,
},
};
Full Branding
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
metadata: {
title: "Acme Admin Dashboard" ,
logo: "/logo.png" ,
favicon: "/favicon.ico" ,
company: {
name: "Acme Inc." ,
website: "https://acme.com" ,
supportEmail: "[email protected] " ,
},
theme: "dark" ,
colors: {
primary: "#6366f1" ,
secondary: "#8b5cf6" ,
accent: "#ec4899" ,
},
links: [
{ label: "Documentation" , url: "https://docs.acme.com" },
{ label: "Support" , url: "https://support.acme.com" },
],
},
};
Feature Toggles
Control which sections are visible:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
metadata: {
features: {
users: true ,
sessions: true ,
organizations: true ,
analytics: true ,
tools: false , // Hide tools section
security: false , // Hide security section
},
},
};
Last-Seen Tracking
lastSeenAt
Type: StudioLastSeenAtConfig
Enable automatic last-seen tracking for users.
type StudioLastSeenAtConfig = {
enabled ?: boolean ;
columnName ?: string ; // Default: "lastSeenAt"
};
When enabling lastSeenAt, you must add the column to your user table and run migrations.
Basic Setup
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
lastSeenAt: {
enabled: true ,
},
};
Then run your database migration:
Custom Column Name
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
lastSeenAt: {
enabled: true ,
columnName: "last_seen_at" , // Use snake_case column name
},
};
The studio injects hooks into Better Auth that automatically update the last-seen timestamp on sign-in and session creation.
IP Geolocation
ipAddress
Type: StudioIpAddressConfig
Configure IP geolocation for Events and Sessions.
type StudioIpAddressConfig =
| {
provider : "ipinfo" ;
apiToken ?: string ;
baseUrl ?: string ;
endpoint ?: "lite" | "lookup" ; // Default: "lookup"
}
| {
provider : "ipapi" ;
apiToken ?: string ;
baseUrl ?: string ;
}
| {
provider : "static" ;
path : string ; // Path to .mmdb file
};
Using IPInfo
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
ipAddress: {
provider: "ipinfo" ,
apiToken: process . env . IPINFO_TOKEN ,
endpoint: "lookup" , // "lite" for free tier (country only), "lookup" for paid (city/region)
},
};
Using IPApi
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
ipAddress: {
provider: "ipapi" ,
apiToken: process . env . IPAPI_TOKEN ,
},
};
Using MaxMind GeoLite2 (Static)
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
ipAddress: {
provider: "static" ,
path: "./data/GeoLite2-City.mmdb" ,
},
};
Event Tracking
events
Type: EventsConfig
Configure authentication event tracking and analytics.
type EventsConfig = {
enabled ?: boolean ;
tableName ?: string ;
provider ?: EventIngestionProvider ;
client ?: any ;
clientType ?: "postgres" | "prisma" | "drizzle" | "clickhouse" | "sqlite" | "node-sqlite" | "https" | "custom" ;
include ?: AuthEventType [];
exclude ?: AuthEventType [];
batchSize ?: number ;
flushInterval ?: number ;
retryOnError ?: boolean ;
liveMarquee ?: LiveMarqueeConfig ;
onEventIngest ?: ( event : AuthEvent ) => void | Promise < void >;
};
Basic Event Tracking
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
tableName: "auth_events" ,
},
};
Filter Events
Track only specific event types:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
tableName: "auth_events" ,
include: [ "sign-in" , "sign-up" , "sign-out" , "password-reset" ],
},
};
Or exclude specific events:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
tableName: "auth_events" ,
exclude: [ "token-refresh" , "session-check" ],
},
};
Live Marquee
Configure the live events marquee:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
liveMarquee: {
enabled: true ,
pollInterval: 2000 , // Poll every 2 seconds
speed: 0.5 , // Animation speed
pauseOnHover: true , // Pause when hovered
limit: 50 , // Show max 50 events
sort: "desc" , // Newest first
timeWindow: {
since: "1h" , // Show events from last hour
},
colors: {
success: "#10b981" ,
info: "#3b82f6" ,
warning: "#f59e0b" ,
error: "#ef4444" ,
failed: "#dc2626" ,
},
},
},
};
Custom Time Window
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
liveMarquee: {
enabled: true ,
timeWindow: {
custom: 2 * 60 * 60 , // Custom 2 hours in seconds
},
},
},
};
Event Callback
Run custom logic when events are ingested:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
events: {
enabled: true ,
onEventIngest : async ( event ) => {
// Log to external service
await logToAnalytics ( event );
// Send alerts for failed logins
if ( event . type === "sign-in" && event . status === "failed" ) {
await sendAlert ( event );
}
},
},
};
Type: ToolsConfig
Control which tools are shown in the studio interface.
type ToolsConfig = {
exclude ?: StudioToolId [];
};
type StudioToolId =
| "test-oauth"
| "hash-password"
| "run-migration"
| "test-db"
| "validate-config"
| "health-check"
| "export-data"
| "jwt-decoder"
| "token-generator"
| "plugin-generator"
| "uuid-generator"
| "password-strength"
| "oauth-credentials"
| "secret-generator" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
tools: {
exclude: [
"test-oauth" ,
"test-db" ,
"run-migration" ,
"health-check" ,
],
},
};
Environment-Based Configuration
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
tools: {
exclude: process . env . NODE_ENV === "production"
? [ "test-oauth" , "run-migration" , "test-db" ]
: [],
},
};
Complete Example
Here’s a comprehensive configuration example:
import type { StudioConfig } from "better-auth-studio" ;
import { auth } from "./lib/auth" ;
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
// Access control
access: {
roles: [ "admin" , "super-admin" ],
allowEmails: [ "[email protected] " ],
},
// Branding
metadata: {
title: "Acme Admin Dashboard" ,
logo: "/logo.png" ,
theme: "dark" ,
company: {
name: "Acme Inc." ,
website: "https://acme.com" ,
supportEmail: "[email protected] " ,
},
colors: {
primary: "#6366f1" ,
secondary: "#8b5cf6" ,
accent: "#ec4899" ,
},
},
// Last-seen tracking
lastSeenAt: {
enabled: true ,
},
// IP geolocation
ipAddress: {
provider: "ipinfo" ,
apiToken: process . env . IPINFO_TOKEN ,
endpoint: "lookup" ,
},
// Event tracking
events: {
enabled: true ,
tableName: "auth_events" ,
include: [ "sign-in" , "sign-up" , "sign-out" , "password-reset" ],
liveMarquee: {
enabled: true ,
pollInterval: 2000 ,
speed: 0.5 ,
pauseOnHover: true ,
timeWindow: { since: "1h" },
},
onEventIngest : async ( event ) => {
if ( event . type === "sign-in" && event . status === "failed" ) {
await sendAlert ( event );
}
},
},
// Hide sensitive tools in production
tools: {
exclude: process . env . NODE_ENV === "production"
? [ "test-oauth" , "run-migration" , "test-db" ]
: [],
},
};
export default config ;
Environment Variables
Recommended environment variables for production:
# Database
DATABASE_URL = postgresql://user:pass@host:5432/db
# IP Geolocation
IPINFO_TOKEN = your_ipinfo_token
IPAPI_TOKEN = your_ipapi_token
# Better Auth
BETTER_AUTH_SECRET = your_secret_key
# Studio
STUDIO_ACCESS_SECRET = your_studio_secret
Use in your config:
const config : StudioConfig = {
auth ,
basePath: "/api/studio" ,
access: {
secret: process . env . STUDIO_ACCESS_SECRET ,
},
ipAddress: {
provider: "ipinfo" ,
apiToken: process . env . IPINFO_TOKEN ,
},
};
Next Steps
Next.js Setup Set up with Next.js App Router
Express Setup Integrate with Express.js