better-auth-electron
Better Auth integration for Electron desktop apps with secure IPC, context isolation, and encrypted session storage
Best use case
better-auth-electron is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Better Auth integration for Electron desktop apps with secure IPC, context isolation, and encrypted session storage
Teams using better-auth-electron should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/better-auth-electron/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How better-auth-electron Compares
| Feature / Agent | better-auth-electron | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Better Auth integration for Electron desktop apps with secure IPC, context isolation, and encrypted session storage
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Better Auth - Electron Desktop Integration
**Better Auth** works seamlessly with Electron using the React client in the renderer process with secure IPC patterns.
## AI Tooling
**IMPORTANT**: Before implementing Better Auth in Electron, consult:
- **AI Documentation**: `https://better-auth.com/llms.txt`
- **MCP Server**: `https://mcp.chonkie.ai/better-auth/better-auth-builder/mcp`
Use Context7 to look up Better Auth patterns:
```
get_library_docs({ libraryName: "better-auth", topic: "react client" })
get_library_docs({ libraryName: "better-auth", topic: "session management" })
```
---
## Installation
```bash
# Install Better Auth and electron-store for session persistence
npm install better-auth electron-store
```
## Architecture
```
┌─────────────────────────────────────────────────────┐
│ Main Process │
│ - Session validation via IPC │
│ - Secure token storage (electron-store) │
│ - Auth state management │
└───────────────────┬─────────────────────────────────┘
│ IPC (contextBridge)
┌───────────────────▼─────────────────────────────────┐
│ Preload Script │
│ - Expose safe auth APIs to renderer │
│ - No direct Node.js access │
└───────────────────┬─────────────────────────────────┘
│
┌───────────────────▼─────────────────────────────────┐
│ Renderer Process │
│ - Better Auth React client │
│ - UI components (React/shadcn) │
│ - Uses window.authApi from preload │
└─────────────────────────────────────────────────────┘
```
---
## Backend Configuration
Your Electron app needs a Better Auth backend (can be local or remote):
**Backend (`server/auth.ts`):**
```typescript
import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { db } from "./db"
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "sqlite" }),
emailAndPassword: {
enabled: true,
},
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
// Trust Electron app origin
trustedOrigins: [
"app://.", // Electron custom protocol
"file://", // File protocol
"http://localhost", // Dev server
],
})
```
---
## Main Process
**`main.ts`:**
```typescript
import { app, BrowserWindow, ipcMain, shell } from "electron"
import Store from "electron-store"
import path from "path"
// Secure persistent storage for auth state
const store = new Store({
name: "auth",
encryptionKey: "your-encryption-key", // Use secure key management
})
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true, // REQUIRED - security
nodeIntegration: false, // REQUIRED - security
sandbox: true, // RECOMMENDED
},
})
// Open OAuth callbacks in external browser
win.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("http")) {
shell.openExternal(url)
return { action: "deny" }
}
return { action: "allow" }
})
win.loadFile("index.html")
}
// IPC handlers for auth operations
ipcMain.handle("auth:get-stored-session", async () => {
return store.get("session", null)
})
ipcMain.handle("auth:store-session", async (_, session) => {
store.set("session", session)
})
ipcMain.handle("auth:clear-session", async () => {
store.delete("session")
})
ipcMain.handle("auth:get-api-url", async () => {
return process.env.AUTH_API_URL || "http://localhost:3000"
})
app.whenReady().then(createWindow)
```
---
## Preload Script
**`preload.ts`:**
```typescript
import { contextBridge, ipcRenderer } from "electron"
// Expose secure auth API to renderer
contextBridge.exposeInMainWorld("authApi", {
// Session persistence
getStoredSession: () => ipcRenderer.invoke("auth:get-stored-session"),
storeSession: (session: unknown) => ipcRenderer.invoke("auth:store-session", session),
clearSession: () => ipcRenderer.invoke("auth:clear-session"),
// Config
getApiUrl: () => ipcRenderer.invoke("auth:get-api-url"),
// Events
onAuthStateChange: (callback: (session: unknown) => void) => {
ipcRenderer.on("auth:state-changed", (_, session) => callback(session))
},
})
// Type declarations for renderer
declare global {
interface Window {
authApi: {
getStoredSession: () => Promise<unknown>
storeSession: (session: unknown) => Promise<void>
clearSession: () => Promise<void>
getApiUrl: () => Promise<string>
onAuthStateChange: (callback: (session: unknown) => void) => void
}
}
}
```
---
## Renderer Process (React)
**Auth Client (`src/lib/auth-client.ts`):**
```typescript
import { createAuthClient } from "better-auth/react"
// Get API URL from main process
const getAuthClient = async () => {
const baseURL = await window.authApi.getApiUrl()
return createAuthClient({
baseURL,
// Custom fetch to handle Electron environment
fetchOptions: {
credentials: "include",
},
})
}
// Export singleton
let authClientPromise: ReturnType<typeof getAuthClient> | null = null
export const getClient = () => {
if (!authClientPromise) {
authClientPromise = getAuthClient()
}
return authClientPromise
}
// Hook for components
export function useAuthClient() {
const [client, setClient] = useState<Awaited<ReturnType<typeof getAuthClient>> | null>(null)
useEffect(() => {
getClient().then(setClient)
}, [])
return client
}
```
**Simplified Client (if API URL is static):**
```typescript
import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient({
baseURL: "http://localhost:3000", // Your auth server
})
export const { signIn, signUp, signOut, useSession } = authClient
```
---
## Sign In Component
```typescript
import { useState } from "react"
import { authClient } from "@/lib/auth-client"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
export function SignIn() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleSignIn = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError(null)
const { data, error } = await authClient.signIn.email({
email,
password,
})
if (error) {
setError(error.message)
} else if (data?.session) {
// Persist session to main process store
await window.authApi.storeSession(data.session)
}
setLoading(false)
}
return (
<Card className="w-full max-w-md mx-auto">
<CardHeader>
<CardTitle>Sign In</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSignIn} className="space-y-4">
<Input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
{error && <p className="text-sm text-destructive">{error}</p>}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? "Signing in..." : "Sign In"}
</Button>
</form>
</CardContent>
</Card>
)
}
```
---
## Session Management
```typescript
import { useEffect } from "react"
import { authClient } from "@/lib/auth-client"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function UserMenu() {
const { data: session, isPending } = authClient.useSession()
// Sync session changes to main process
useEffect(() => {
if (session) {
window.authApi.storeSession(session)
}
}, [session])
const handleSignOut = async () => {
await authClient.signOut()
await window.authApi.clearSession()
}
if (isPending) {
return <div className="h-8 w-8 animate-pulse rounded-full bg-muted" />
}
if (!session) {
return <Button variant="outline">Sign In</Button>
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src={session.user.image || ""} />
<AvatarFallback>
{session.user.name?.[0]?.toUpperCase()}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem className="font-medium">
{session.user.name}
</DropdownMenuItem>
<DropdownMenuItem className="text-muted-foreground">
{session.user.email}
</DropdownMenuItem>
<DropdownMenuItem onClick={handleSignOut}>
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
```
---
## Restore Session on App Launch
```typescript
// App.tsx - Restore session when app starts
import { useEffect, useState } from "react"
import { authClient } from "@/lib/auth-client"
export function App() {
const [initialized, setInitialized] = useState(false)
useEffect(() => {
async function restoreSession() {
// Check for stored session from previous launch
const storedSession = await window.authApi.getStoredSession()
if (storedSession) {
// Validate session with server
const { data: currentSession } = await authClient.getSession()
if (!currentSession) {
// Session expired, clear stored data
await window.authApi.clearSession()
}
}
setInitialized(true)
}
restoreSession()
}, [])
if (!initialized) {
return <div>Loading...</div>
}
return <MainApp />
}
```
---
## OAuth in Electron
For OAuth flows, open the auth URL in the system browser and handle the callback:
```typescript
import { shell } from "electron" // Main process only
// In main process - handle OAuth callback
app.setAsDefaultProtocolClient("myapp") // Register custom protocol
app.on("open-url", (event, url) => {
// Handle OAuth callback: myapp://auth/callback?code=...
if (url.includes("/auth/callback")) {
mainWindow.webContents.send("auth:oauth-callback", url)
}
})
```
---
## Security Best Practices
1. **Always use contextIsolation** - Never expose Node.js to renderer
2. **Encrypt stored sessions** - Use electron-store with encryption
3. **Validate sessions on startup** - Check with server before trusting local state
4. **Handle OAuth via system browser** - More secure than in-app browser
5. **Use sandbox mode** - Additional security layer
6. **Clear sensitive data on sign out** - Both in-memory and persistent storage
**Documentation**: https://better-auth.com/docsRelated Skills
better-auth
The ultimate authentication and authorization skill. Implement login, signin, signup, registration, OAuth, 2FA, MFA, passkeys, and user session management. Secure your application with RBAC and access control.
better-auth-specialist
Expert implementation of user authentication and authorization using Better Auth library for Next.js 15+/React 18+ frontends and Node.js/FastAPI backends with SQL and NoSQL databases. Use when implementing authentication systems, user login/signup, session management, protected routes, role-based access control (RBAC), OAuth integration, or any auth-related tasks including email/password authentication, JWT tokens, permissions, and user management.
better-auth-patterns
Better Auth authentication patterns for TypeScript applications. Use when implementing authentication with Better Auth, configuring OAuth providers, setting up session management, integrating with Next.js/Astro/Hono/Express/TanStack Start, or configuring Drizzle/Prisma adapters.
better-auth-best-practices
Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.
authoring-excalidraw-files
Generate architecture diagrams as .excalidraw files. Use when the user asks to create architecture diagrams, system diagrams, visualize codebase structure, infrastructure diagrams, or generate excalidraw files.
authentication
Auth flows, session management, OAuth integration, domain-restricted access, and role-based access control for TopNetworks properties. Primary implementation is Better Auth 1.x with Google OAuth in route-genius. Use when implementing login, session checks, protected routes, or any access control logic.
auth0-quickstart
Use when starting Auth0 integration in any framework - detects your stack (React, Next.js, Vue, Angular, Express, Fastify, React Native) and routes to correct SDK setup workflow
auth0-nextjs
Use when adding authentication to Next.js applications with both server and client-side auth - supports App Router and Pages Router with @auth0/nextjs-auth0 SDK
auth0-fastify
Use when adding authentication to Fastify server-rendered web applications with session management - integrates @auth0/auth0-fastify for high-performance web apps
auth0-express
Use when adding authentication to Express.js server-rendered web applications with session management - integrates express-openid-connect for traditional web apps
auth-web-cloudbase
Complete guide for CloudBase Auth v2 using Web SDK (@cloudbase/js-sdk@2.x) - all login flows, user management, captcha handling, and best practices in one file.
auth-tool-cloudbase
Use CloudBase Auth tool to configure and manage authentication providers for web applications - enable/disable login methods (SMS, Email, WeChat Open Platform, Google, Anonymous, Username/password, OAuth, SAML, CAS, Dingding, etc.) and configure provider settings via MCP tools.