
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
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です



