원클릭으로
better-auth
Better-Auth library integration for authentication with social providers, sessions, RBAC, and organization management
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Better-Auth library integration for authentication with social providers, sessions, RBAC, and organization management
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Unified design foundations — design system architecture, tokens, component specs, visual principles, creative vision, figma integration, plus brand design system loader (66 real brands via DESIGN.md). Absorbs design, design-system, design-systems, design-principles, design-router, creative-vision, figma, design-md.
Render, summarize, and present markdown documents and structured content in multiple output modes
Ultra UI skill - combines Google's DESIGN.md spec (machine-readable design tokens) with the ui-ux-pro-max knowledge base (91 styles, 161 palettes, 73 font pairings, 161 products, 104 UX guidelines, 25 chart types). Generates lint-clean DESIGN.md files, validates token references and WCAG contrast, exports Tailwind/DTCG tokens, and diffs design systems version-over-version.
Initialize UltraThink capabilities in the current project directory
Org-Bench Google-bipartite winning mechanism — the 4-section design-doc gate that every non-trivial change passes through. Use when the Director defines new work, when an Integrator reviews a lane (code/quality/devops), when the Director approves, or when a Worker is about to start coding and needs the spec. Tools live in the `design-doc` MCP server. Triggers on phrases like "design doc", "design review", "approve revision", "lane verdict", "what does this issue require", "is this approved yet".
Web scraping with anti-bot bypass (Cloudflare Turnstile etc.), stealth headless browsing, adaptive selectors, and concurrent crawls. Use when the user asks to scrape, crawl, or extract data from websites; the built-in WebFetch fails; the target has anti-bot protections; or the work needs JavaScript rendering. Prefers the registered MCP tools (mcp__scrapling__*) over raw Python so token cost stays low.
| name | better-auth |
| description | Better-Auth library integration for authentication with social providers, sessions, RBAC, and organization management |
| layer | domain |
| category | security |
| triggers | ["better-auth","better auth","betterauth"] |
| inputs | ["application framework","auth requirements","database adapter","social providers"] |
| outputs | ["auth configuration","client setup","middleware","protected routes","plugin configuration"] |
| linksTo | ["authentication","nextjs","drizzle","prisma"] |
| linkedFrom | ["authentication","code-review"] |
| preferredNextSkills | ["authentication","owasp","nextjs"] |
| fallbackSkills | ["authentication"] |
| riskLevel | medium |
| memoryReadPolicy | selective |
| memoryWritePolicy | none |
| sideEffects | ["database migrations","session creation"] |
Integrate Better-Auth as the authentication solution for web applications. This skill covers server and client configuration, social providers, session management, email/password auth, RBAC, organization/team support, two-factor authentication, and database adapter setup.
// lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { twoFactor } from "better-auth/plugins/two-factor";
import { organization } from "better-auth/plugins/organization";
import { admin } from "better-auth/plugins/admin";
import { db } from "@/db";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg", // or "mysql", "sqlite"
}),
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 128,
autoSignIn: true,
requireEmailVerification: true,
sendResetPassword: async ({ user, url }) => {
await sendEmail({
to: user.email,
subject: "Reset your password",
html: `<a href="${url}">Reset Password</a>`,
});
},
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
session: {
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 5 min cache
},
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update session every 24h
},
plugins: [
twoFactor({
issuer: "MyApp",
otpOptions: {
digits: 6,
period: 30,
},
}),
organization({
allowUserToCreateOrganization: true,
}),
admin(),
],
trustedOrigins: [process.env.NEXT_PUBLIC_APP_URL!],
advanced: {
generateId: () => crypto.randomUUID(),
},
});
export type Session = typeof auth.$Infer.Session;
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import { twoFactorClient } from "better-auth/client/plugins";
import { organizationClient } from "better-auth/client/plugins";
import { adminClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL!,
plugins: [
twoFactorClient(),
organizationClient(),
adminClient(),
],
});
export const {
signIn,
signUp,
signOut,
useSession,
getSession,
} = authClient;
// app/dashboard/page.tsx
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("/login");
}
return (
<div>
<h1>Welcome, {session.user.name}</h1>
<p>Email: {session.user.email}</p>
</div>
);
}
"use client";
import { useSession, signIn, signUp, signOut } from "@/lib/auth-client";
import { useState } from "react";
export function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
async function handleLogin(e: React.FormEvent) {
e.preventDefault();
const result = await signIn.email({ email, password });
if (result.error) {
setError(result.error.message);
}
}
async function handleGoogleLogin() {
await signIn.social({ provider: "google" });
}
return (
<form onSubmit={handleLogin}>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
{error && <p>{error}</p>}
<button type="submit">Sign In</button>
<button type="button" onClick={handleGoogleLogin}>Sign in with Google</button>
</form>
);
}
export function UserMenu() {
const { data: session, isPending } = useSession();
if (isPending) return <div>Loading...</div>;
if (!session) return <a href="/login">Sign In</a>;
return (
<div>
<span>{session.user.name}</span>
<button onClick={() => signOut()}>Sign Out</button>
</div>
);
}
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";
const protectedPaths = ["/dashboard", "/settings", "/admin"];
const authPaths = ["/login", "/signup"];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const sessionCookie = getSessionCookie(request);
const isProtected = protectedPaths.some((p) => pathname.startsWith(p));
const isAuthPage = authPaths.some((p) => pathname.startsWith(p));
if (isProtected && !sessionCookie) {
const url = new URL("/login", request.url);
url.searchParams.set("callbackUrl", pathname);
return NextResponse.redirect(url);
}
if (isAuthPage && sessionCookie) {
return NextResponse.redirect(new URL("/dashboard", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|api/auth).*)"],
};
# Generate schema for your database adapter
npx @better-auth/cli generate --config ./lib/auth.ts --output ./db/auth-schema.ts
# Or push schema directly to database
npx @better-auth/cli migrate --config ./lib/auth.ts
trustedOrigins to prevent open redirect attacksrequireEmailVerification for email/password authcookieCache to reduce database lookups for sessionssession.expiresIn based on your security requirementsupdateAge to refresh sessions periodicallyallowUserToCreateOrganization based on your business model| Pitfall | Fix |
|---|---|
Missing trustedOrigins | Set it to your app URL to prevent CSRF |
| Not running migrations | Run npx @better-auth/cli migrate after config changes |
| Client/server plugin mismatch | Add matching client plugins for each server plugin |
| Session not found in Server Components | Pass headers: await headers() to getSession |
| Middleware blocking auth API | Exclude /api/auth from middleware matcher |
| Missing email verification | Enable requireEmailVerification in production |
// Server-side admin check
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
export async function isAdmin(): Promise<boolean> {
const session = await auth.api.getSession({ headers: await headers() });
return session?.user?.role === "admin";
}
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
sendVerificationEmail: async ({ user, url }) => {
await sendEmail({
to: user.email,
subject: "Verify your email",
html: `<a href="${url}">Verify Email</a>`,
});
},
},
import { betterAuth } from "better-auth";
export const auth = betterAuth({
rateLimit: {
window: 60, // 60 seconds
max: 10, // 10 requests per window
customRules: {
"/sign-in/email": { window: 60, max: 5 },
"/sign-up/email": { window: 60, max: 3 },
"/forgot-password": { window: 300, max: 3 },
},
},
// ... rest of config
});