Best for
- Implementing authentication in TypeScript/JavaScript applications
- Adding email/password or social OAuth authentication
- Setting up 2FA, passkeys, magic links, or other advanced auth features
einverne/dotfiles/claude/skills/better-auth/SKILL.md
Guide for implementing Better Auth - a framework-agnostic authentication and authorization framework for TypeScript. Use when adding authentication features like email/password, OAuth, 2FA, passkeys, or advanced auth functionality to applications.
Decision brief
Better Auth is a comprehensive, framework-agnostic authentication and authorization framework for TypeScript that provides built-in support for email/password authentication, social sign-on, and a powerful plugin ecosystem for advanced features.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/einverne/dotfiles --skill "claude/skills/better-auth"Inspect the Agent Skill "better-auth" from https://github.com/einverne/dotfiles/blob/7d18cf4fefdeec05853c7420cc0499dde1e88b27/claude/skills/better-auth/SKILL.md at commit 7d18cf4fefdeec05853c7420cc0499dde1e88b27. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
bash npm install better-auth
bash npm install better-auth
Generate secret: Use openssl or a random string generator (min 32 characters).
Create auth.ts in project root, lib/, utils/, or nested under src/, app/, or server/:
Choose your database setup:
Permission review
The documentation asks the agent to run terminal commands or scripts.
npm install better-authThe documentation asks the agent to run terminal commands or scripts.
pnpm add better-authThe documentation asks the agent to create, modify, or delete local files.
Create `.env` file:The documentation includes network, browsing, or remote request actions.
BETTER_AUTH_URL=http://localhost:3000The documentation includes network, browsing, or remote request actions.
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 90/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 119 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Better Auth is a comprehensive, framework-agnostic authentication and authorization framework for TypeScript that provides built-in support for email/password authentication, social sign-on, and a powerful plugin ecosystem for advanced features.
Use this skill when:
Better Auth follows a client-server architecture:
better-auth): Handles auth logic, database operations, and API routesbetter-auth/client): Provides hooks and methods for authenticationnpm install better-auth
# or
pnpm add better-auth
# or
yarn add better-auth
# or
bun add better-auth
Create .env file:
BETTER_AUTH_SECRET=<generated-secret-key>
BETTER_AUTH_URL=http://localhost:3000
Generate secret: Use openssl or a random string generator (min 32 characters).
Create auth.ts in project root, lib/, utils/, or nested under src/, app/, or server/:
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: {
// Database configuration
},
emailAndPassword: {
enabled: true,
autoSignIn: true // Users auto sign-in after signup
},
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}
}
});
Choose your database setup:
Direct Database Connection:
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
// or import { Pool } from "pg";
// or import { createPool } from "mysql2/promise";
export const auth = betterAuth({
database: new Database("./sqlite.db"),
// or: new Pool({ connectionString: process.env.DATABASE_URL })
// or: createPool({ host: "localhost", user: "root", ... })
});
ORM Adapter:
// Drizzle
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg", // or "mysql", "sqlite"
}),
});
// Prisma
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
});
// MongoDB
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { client } from "@/db";
export const auth = betterAuth({
database: mongodbAdapter(client),
});
Use Better Auth CLI:
# Generate schema/migration files
npx @better-auth/cli generate
# Or migrate directly (Kysely adapter only)
npx @better-auth/cli migrate
Create catch-all route for /api/auth/*:
Next.js (App Router):
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);
Nuxt:
// server/api/auth/[...all].ts
import { auth } from "~/utils/auth";
export default defineEventHandler((event) => {
return auth.handler(toWebRequest(event));
});
SvelteKit:
// hooks.server.ts
import { auth } from "$lib/auth";
import { svelteKitHandler } from "better-auth/svelte-kit";
export async function handle({ event, resolve }) {
return svelteKitHandler({ event, resolve, auth });
}
Hono:
import { Hono } from "hono";
import { auth } from "./auth";
const app = new Hono();
app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw));
Express:
import express from "express";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./auth";
const app = express();
app.all("/api/auth/*", toNodeHandler(auth));
Create auth-client.ts:
import { createAuthClient } from "better-auth/client";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"
});
Server Configuration:
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
autoSignIn: true, // default: true
}
});
Client Usage:
// Sign Up
const { data, error } = await authClient.signUp.email({
email: "[email protected]",
password: "securePassword123",
name: "John Doe",
image: "https://example.com/avatar.jpg", // optional
callbackURL: "/dashboard" // optional
}, {
onSuccess: (ctx) => {
// redirect or show success
},
onError: (ctx) => {
alert(ctx.error.message);
}
});
// Sign In
const { data, error } = await authClient.signIn.email({
email: "[email protected]",
password: "securePassword123",
callbackURL: "/dashboard",
rememberMe: true // default: true
});
Server Configuration:
export const auth = betterAuth({
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
// Other providers: apple, discord, facebook, etc.
}
});
Client Usage:
await authClient.signIn.social({
provider: "github",
callbackURL: "/dashboard",
errorCallbackURL: "/error",
newUserCallbackURL: "/welcome",
});
await authClient.signOut({
fetchOptions: {
onSuccess: () => {
router.push("/login");
}
}
});
Using Hooks (React/Vue/Svelte/Solid):
// React
import { authClient } from "@/lib/auth-client";
export function UserProfile() {
const { data: session, isPending, error } = authClient.useSession();
if (isPending) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Welcome, {session?.user.name}!</div>;
}
// Vue
<script setup>
import { authClient } from "~/lib/auth-client";
const session = authClient.useSession();
</script>
<template>
<div v-if="session.data">{{ session.data.user.email }}</div>
</template>
// Svelte
<script>
import { authClient } from "$lib/auth-client";
const session = authClient.useSession();
</script>
<p>{$session.data?.user.email}</p>
Using getSession:
const { data: session, error } = await authClient.getSession();
// Next.js
import { auth } from "./auth";
import { headers } from "next/headers";
const session = await auth.api.getSession({
headers: await headers()
});
// Hono
app.get("/protected", async (c) => {
const session = await auth.api.getSession({
headers: c.req.raw.headers
});
if (!session) {
return c.json({ error: "Unauthorized" }, 401);
}
return c.json({ user: session.user });
});
Better Auth's plugin system allows adding advanced features easily.
Server-Side:
import { betterAuth } from "better-auth";
import { twoFactor, organization, username } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
twoFactor(),
organization(),
username(),
]
});
Client-Side:
import { createAuthClient } from "better-auth/client";
import {
twoFactorClient,
organizationClient,
usernameClient
} from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
twoFactorClient({
twoFactorPage: "/two-factor"
}),
organizationClient(),
usernameClient()
]
});
After Adding Plugins:
# Regenerate schema
npx @better-auth/cli generate
# Apply migration
npx @better-auth/cli migrate
// Server
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [twoFactor()]
});
// Client
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
twoFactorClient({ twoFactorPage: "/two-factor" })
]
});
// Usage
await authClient.twoFactor.enable({ password: "userPassword" });
await authClient.twoFactor.verifyTOTP({
code: "123456",
trustDevice: true
});
// Server
import { username } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [username()]
});
// Client
import { usernameClient } from "better-auth/client/plugins";
// Sign up with username
await authClient.signUp.username({
username: "johndoe",
password: "securePassword123",
name: "John Doe"
});
import { magicLink } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
magicLink({
sendMagicLink: async ({ email, url }) => {
// Send email with magic link
await sendEmail(email, url);
}
})
]
});
import { passkey } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [passkey()]
});
// Client
await authClient.passkey.register();
await authClient.passkey.signIn();
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [organization()]
});
// Client
await authClient.organization.create({
name: "Acme Corp",
slug: "acme"
});
await authClient.organization.inviteMember({
organizationId: "org-id",
email: "[email protected]",
role: "member"
});
export const auth = betterAuth({
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
await sendEmail(user.email, url);
},
sendOnSignUp: true
}
});
export const auth = betterAuth({
rateLimit: {
enabled: true,
window: 60, // seconds
max: 10 // requests
}
});
export const auth = betterAuth({
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days in seconds
updateAge: 60 * 60 * 24 // Update every 24 hours
}
});
export const auth = betterAuth({
advanced: {
corsOptions: {
origin: ["https://example.com"],
credentials: true
}
}
});
Better Auth requires these core tables:
user: User accountssession: Active sessionsaccount: OAuth provider connectionsverification: Email verification tokensAuto-generate with CLI:
npx @better-auth/cli generate
Manual schema available in docs: Check /docs/concepts/database#core-schema
BETTER_AUTH_URL to HTTPS URL// Next.js middleware
import { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
export async function middleware(request: NextRequest) {
const session = await auth.api.getSession({
headers: request.headers
});
if (!session) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"]
};
await authClient.updateUser({
name: "New Name",
image: "https://example.com/new-avatar.jpg"
});
// Change password
await authClient.changePassword({
currentPassword: "oldPassword",
newPassword: "newPassword"
});
// Reset password (forgot password)
await authClient.forgetPassword({
email: "[email protected]",
redirectTo: "/reset-password"
});
await authClient.resetPassword({
token: "reset-token",
password: "newPassword"
});
"Unable to find auth instance"
auth.ts is in correct location (root, lib/, utils/)auth or default exportDatabase connection errors
CORS errors
corsOptions in advanced settingsPlugin not working
When implementing Better Auth:
better-auth packageAlternatives
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
alirezarezvani/claude-skills
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
dotnet/skills
Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing
JasonColapietro/suede-creator-skills
Suede-owned experimentation discipline for hypotheses, sample sizing, test duration, significance, and repeatable experiment programs. Use when comparing variants, deciding whether a result is reliable, or building an experiment backlog and cadence. NOT FOR: analytics instrumentation (use suede-analytics), post-click conversion diagnosis (use suede-site-alchemy), or writing the variant copy itself (use suede-copy).