
create-auth-skill
by kriegcloud
SKILL.md
name: Create Auth Skill description: A skill to create auth service for new applications.
Create Auth Skill
A skill to create authentication and authorization layers for TypeScript/JavaScript applications using Better Auth.
Overview
This skill guides you through creating authentication and authorization layers for TypeScript/JavaScript applications using Better Auth. It covers new projects, existing projects without auth, and improving existing auth implementations.
Decision Tree: Choosing Your Approach
User Request → Is this a new/empty project?
│
├─ YES → Create new project with auth scaffolding
│ ├─ 1. Identify framework (Next.js, Express, SvelteKit, etc.)
│ ├─ 2. Choose database (PostgreSQL, SQLite, MongoDB, etc.)
│ ├─ 3. Install dependencies
│ ├─ 4. Create auth.ts and auth-client.ts
│ ├─ 5. Set up API route handler
│ ├─ 6. Run CLI to generate/migrate schema
│ └─ 7. Add auth features (OAuth, 2FA, organizations, etc.)
│
└─ NO → Does the project already have authentication?
│
├─ YES → Review and enhance existing implementation
│ ├─ Audit current auth for security gaps
│ ├─ Identify missing features
│ ├─ Plan migration path if switching to Better Auth
│ └─ Implement improvements incrementally
│
└─ NO → Add auth to existing project
├─ 1. Analyze project structure and framework
├─ 2. Install better-auth
├─ 3. Create auth configuration
├─ 4. Set up route handler
├─ 5. Run schema migrations
└─ 6. Integrate auth into existing pages/components
Step-by-Step Implementation
Step 1: Install Dependencies
# Core package (always required)
npm install better-auth
# Optional: Scoped packages for specific features
npm install @better-auth/stripe # Stripe payments/subscriptions
npm install @better-auth/passkey # WebAuthn/Passkey authentication
npm install @better-auth/sso # SAML/OIDC enterprise SSO
npm install @better-auth/scim # SCIM user provisioning
npm install @better-auth/expo # Expo/React Native support
Step 2: Environment Variables
Create or update .env:
# Required
BETTER_AUTH_SECRET=your-secret-key-at-least-32-characters-long
BETTER_AUTH_URL=http://localhost:3000
# Database (choose one)
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
# or for SQLite
# DATABASE_URL=file:./auth.db
# OAuth Providers (add as needed)
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
Generate a secure secret:
openssl rand -base64 32
Step 3: Create Server-Side Auth Configuration
Create lib/auth.ts (or src/lib/auth.ts):
Minimal Setup (SQLite, email/password only)
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
export const auth = betterAuth({
database: new Database("./auth.db"),
emailAndPassword: {
enabled: true,
},
});
Standard Setup (PostgreSQL, OAuth providers)
import { betterAuth } from "better-auth";
import { Pool } from "pg";
export const auth = betterAuth({
database: new Pool({
connectionString: process.env.DATABASE_URL,
}),
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url }) => {
// Implement email sending
console.log(`Reset password for ${user.email}: ${url}`);
},
},
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
// Implement email sending
console.log(`Verify email for ${user.email}: ${url}`);
},
sendOnSignUp: true,
},
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!,
},
},
});
// Export types for client-side usage
export type Session = typeof auth.$Infer.Session;
Full-Featured Setup (with plugins)
import { betterAuth } from "better-auth";
import { Pool } from "pg";
import {
admin,
organization,
twoFactor,
bearer,
openAPI,
} from "better-auth/plugins";
import { passkey } from "@better-auth/passkey";
export const auth = betterAuth({
database: new Pool({
connectionString: process.env.DATABASE_URL,
}),
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url }) => {
// Send password reset email
},
},
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
// Send verification email
},
sendOnSignUp: true,
autoSignInAfterVerification: true,
},
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!,
},
},
plugins: [
// Two-factor authentication
twoFactor({
otpOptions: {
async sendOTP({ user, otp }) {
// Send OTP via email/SMS
},
},
}),
// Passkey/WebAuthn
passkey(),
// Organization/team management
organization({
async sendInvitationEmail(data) {
// Send invitation email
},
}),
// Admin panel
admin(),
// Bearer token auth for APIs
bearer(),
// OpenAPI documentation
openAPI(),
],
// Session configuration
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update session every 24 hours
cookieCache: {
enabled: true,
maxAge: 60 * 5, // 5 minutes
},
},
// Account linking
account: {
accountLinking: {
enabled: true,
trustedProviders: ["google", "github"],
},
},
// Rate limiting
rateLimit: {
window: 10,
max: 100,
},
});
export type Session = typeof auth.$Infer.Session;
export type ActiveOrganization = typeof auth.$Infer.ActiveOrganization;
Step 4: Create Client-Side Auth
Create lib/auth-client.ts:
React/Next.js Client
import { createAuthClient } from "better-auth/react";
import type { auth } from "./auth";
export const authClient = createAuthClient({
// baseURL is optional if on same domain
// baseURL: "http://localhost:3000",
});
// Export commonly used functions
export const {
signIn,
signUp,
signOut,
useSession,
getSession,
} = authClient;
Client with Plugins
import { createAuthClient } from "better-auth/react";
import {
adminClient,
organizationClient,
twoFactorClient,
} from "better-auth/client/plugins";
import { passkeyClient } from "@better-auth/passkey/client";
import type { auth } from "./auth";
export const authClient = createAuthClient({
plugins: [
organizationClient(),
twoFactorClient({
onTwoFactorRedirect() {
window.location.href = "/two-factor";
},
}),
passkeyClient(),
adminClient(),
],
fetchOptions: {
onError(e) {
if (e.error.status === 429) {
// Handle rate limiting
console.error("Too many requests");
}
},
},
});
export const {
signIn,
signUp,
signOut,
useSession,
} = authClient;
Other Framework Clients
// Vue
import { createAuthClient } from "better-auth/vue";
// Svelte
import { createAuthClient } from "better-auth/svelte";
// Solid
import { createAuthClient } from "better-auth/solid";
// Vanilla JS
import { createAuthClient } from "better-auth/client";
Step 5: Set Up API Route Handler
Next.js (App Router)
Create app/api/auth/[...all]/route.ts:
import { toNextJsHandler } from "better-auth/next-js";
import { auth } from "@/lib/auth";
export const { GET, POST } = toNextJsHandler(auth);
For Server Components cookie support, add nextCookies() plugin to auth config:
import { nextCookies } from "better-auth/next-js";
export const auth = betterAuth({
// ... other config
plugins: [
nextCookies(),
// ... other plugins
],
});
Next.js (Pages Router)
Create pages/api/auth/[...all].ts:
import { toNextJsHandler } from "better-auth/next-js";
import { auth } from "@/lib/auth";
export default toNextJsHandler(auth);
Express
import express from "express";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./auth";
const app = express();
app.all("/api/auth/*", toNodeHandler(auth));
app.listen(3000);
SvelteKit
Create src/hooks.server.ts:
import { svelteKitHandler } from "better-auth/svelte-kit";
import { auth } from "$lib/auth";
export const handle = svelteKitHandler(auth);
SolidStart
import { solidStartHandler } from "better-auth/solid-start";
import { auth } from "./auth";
export const { GET, POST } = solidStartHandler(auth);
Step 6: Run Database Migrations
# For built-in Kysely adapter (applies directly)
npx @better-auth/cli@latest migrate
# For Prisma (generates schema, apply with prisma migrate)
npx @better-auth/cli@latest generate --output prisma/schema.prisma
npx prisma migrate dev
# For Drizzle (generates schema, apply with drizzle-kit)
npx @better-auth/cli@latest generate --output src/db/auth-schema.ts
npx drizzle-kit push
Step 7: Implement Auth UI Components
Sign In Page Example (React)
"use client";
import { useState } from "react";
import { signIn } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
export function SignInForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const router = useRouter();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError("");
const { error } = await signIn.email({
email,
password,
});
if (error) {
setError(error.message);
setLoading(false);
} else {
router.push("/dashboard");
}
};
const handleOAuthSignIn = async (provider: "github" | "google") => {
await signIn.social({
provider,
callbackURL: "/dashboard",
});
};
return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? "Signing in..." : "Sign In"}
</button>
<div className="oauth-buttons">
<button type="button" onClick={() => handleOAuthSignIn("github")}>
Sign in with GitHub
</button>
<button type="button" onClick={() => handleOAuthSignIn("google")}>
Sign in with Google
</button>
</div>
</form>
);
}
Protected Route Example
"use client";
import { useSession } from "@/lib/auth-client";
import { redirect } from "next/navigation";
export function ProtectedPage() {
const { data: session, isPending } = useSession();
if (isPending) {
return <div>Loading...</div>;
}
if (!session) {
redirect("/sign-in");
}
return (
<div>
<h1>Welcome, {session.user.name}!</h1>
<p>Email: {session.user.email}</p>
</div>
);
}
Server-Side Session Check (Next.js)
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
export default async function DashboardPage() {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session) {
redirect("/sign-in");
}
return (
<div>
<h1>Dashboard</h1>
<p>Welcome, {session.user.name}!</p>
</div>
);
}
Common Auth Features
Two-Factor Authentication
// Server: Add plugin
import { twoFactor } from "better-auth/plugins";
plugins: [
twoFactor({
otpOptions: {
async sendOTP({ user, otp }) {
await sendEmail({
to: user.email,
subject: "Your verification code",
body: `Your code is: ${otp}`,
});
},
},
}),
]
// Client: Enable 2FA
const { data } = await authClient.twoFactor.enable({
password: "user-password",
});
// data.totpURI - QR code URI for authenticator apps
// data.backupCodes - Save these for recovery
Organization/Team Management
// Server: Add plugin
import { organization } from "better-auth/plugins";
plugins: [
organization({
async sendInvitationEmail(data) {
await sendEmail({
to: data.email,
subject: `Join ${data.organization.name}`,
body: `You've been invited by ${data.inviter.user.name}`,
});
},
}),
]
// Client
import { organizationClient } from "better-auth/client/plugins";
// Create organization
await authClient.organization.create({
name: "My Team",
slug: "my-team",
});
// Invite member
await authClient.organization.inviteMember({
email: "member@example.com",
role: "member",
});
Admin Panel
// Server
import { admin } from "better-auth/plugins";
plugins: [
admin({
adminUserIds: ["admin-user-id"],
}),
]
// Client
import { adminClient } from "better-auth/client/plugins";
// List all users (admin only)
const users = await authClient.admin.listUsers({
limit: 10,
offset: 0,
});
// Ban user
await authClient.admin.banUser({
userId: "user-id",
});
Passkey/WebAuthn
// Server
import { passkey } from "@better-auth/passkey";
plugins: [passkey()]
// Client
import { passkeyClient } from "@better-auth/passkey/client";
// Register passkey
await authClient.passkey.addPasskey();
// Sign in with passkey
await authClient.signIn.passkey();
Database Adapters
Built-in Kysely Adapter (Recommended for quick setup)
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
import { Pool } from "pg";
// SQLite
const auth = betterAuth({
database: new Database("./auth.db"),
});
// PostgreSQL
const auth = betterAuth({
database: new Pool({ connectionString: process.env.DATABASE_URL }),
});
Prisma Adapter
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql", // or "mysql", "sqlite"
}),
});
Drizzle Adapter
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db";
const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg", // or "mysql", "sqlite"
}),
});
MongoDB Adapter
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
const db = client.db("myapp");
const auth = betterAuth({
database: mongodbAdapter(db),
});
Security Checklist
- Set
BETTER_AUTH_SECRETto a strong, unique value (32+ characters) - Enable HTTPS in production (
advanced.useSecureCookies: true) - Configure
trustedOriginsfor cross-origin requests - Set appropriate rate limits for auth endpoints
- Enable email verification for new accounts
- Implement password reset flow
- Consider 2FA for sensitive applications
- Review
account.accountLinkingsettings - Never disable CSRF protection in production
- Use
databaseHooksfor audit logging
CLI Reference
# Generate database schema
npx @better-auth/cli@latest generate
# Apply migrations (Kysely adapter only)
npx @better-auth/cli@latest migrate
# Specify config file location
npx @better-auth/cli@latest migrate --config ./src/lib/auth.ts
# Generate for specific output
npx @better-auth/cli@latest generate --output ./prisma/schema.prisma
Resources
Troubleshooting
Common Issues
- "Secret not set": Add
BETTER_AUTH_SECRETto environment variables - "Invalid Origin": Add your domain to
trustedOrigins - Cookies not setting: Check
baseURLmatches your domain, enable secure cookies in production - OAuth callback errors: Verify redirect URIs in provider dashboard match your auth config
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon



