— No reviews yet
0 installs
7 views
0.0% view→install
Install
$ agentstack add skill-allanninal-claude-code-skills-better-auth ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Are you the author of Better Auth? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claimAbout
Better Auth Integration
When to Use This Skill
- Setting up authentication in TypeScript apps
- Implementing OAuth providers (Google, GitHub, etc.)
- Managing user sessions
- Building login/signup flows
- Handling password reset and email verification
Setup
Installation
npm install better-auth
# or
pnpm add better-auth
Basic Configuration
// lib/auth.ts
import { betterAuth } from 'better-auth';
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', // or 'mysql', 'sqlite'
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
});
Database Schema (Prisma)
// prisma/schema.prisma
model User {
id String @id @default(cuid())
email String @unique
emailVerified DateTime?
name String?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
accounts Account[]
}
model Session {
id String @id @default(cuid())
userId String
expiresAt DateTime
token String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model Account {
id String @id @default(cuid())
userId String
accountId String
providerId String
accessToken String?
refreshToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
idToken String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([providerId, accountId])
}
model Verification {
id String @id @default(cuid())
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([identifier, value])
}
OAuth Providers
Configure Providers
// lib/auth.ts
import { betterAuth } from 'better-auth';
export const auth = betterAuth({
// ... database config
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!,
},
discord: {
clientId: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
},
},
});
OAuth Callback URLs
Google: http://localhost:3000/api/auth/callback/google
GitHub: http://localhost:3000/api/auth/callback/github
Discord: http://localhost:3000/api/auth/callback/discord
API Routes
Next.js App Router
// app/api/auth/[...all]/route.ts
import { auth } from '@/lib/auth';
import { toNextJsHandler } from 'better-auth/next-js';
export const { GET, POST } = toNextJsHandler(auth.handler);
Express
// routes/auth.ts
import express from 'express';
import { auth } from '../lib/auth';
import { toNodeHandler } from 'better-auth/node';
const router = express.Router();
router.all('/api/auth/*', toNodeHandler(auth.handler));
export default router;
Client Integration
React Client
// lib/auth-client.ts
import { createAuthClient } from 'better-auth/react';
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
});
export const { signIn, signUp, signOut, useSession } = authClient;
Usage in Components
// components/AuthButtons.tsx
'use client';
import { signIn, signOut, useSession } from '@/lib/auth-client';
export function AuthButtons() {
const { data: session, isPending } = useSession();
if (isPending) {
return Loading...;
}
if (session) {
return (
Welcome, {session.user.name}
signOut()}>Sign Out
);
}
return (
signIn.social({ provider: 'google' })}>
Sign in with Google
signIn.social({ provider: 'github' })}>
Sign in with GitHub
);
}
Email/Password Auth
// components/LoginForm.tsx
'use client';
import { signIn, signUp } from '@/lib/auth-client';
import { useState } from 'react';
export function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isSignUp, setIsSignUp] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (isSignUp) {
const result = await signUp.email({
email,
password,
name: email.split('@')[0],
});
if (result.error) {
console.error(result.error);
}
} else {
const result = await signIn.email({ email, password });
if (result.error) {
console.error(result.error);
}
}
};
return (
setEmail(e.target.value)}
placeholder="Email"
required
/>
setPassword(e.target.value)}
placeholder="Password"
required
/>
{isSignUp ? 'Sign Up' : 'Sign In'}
setIsSignUp(!isSignUp)}>
{isSignUp ? 'Have an account? Sign in' : 'Need an account? Sign up'}
);
}
Server-Side Auth
Get Session in Server Components
// 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: headers(),
});
if (!session) {
redirect('/login');
}
return (
Dashboard
Welcome, {session.user.name}
);
}
Middleware Protection
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { auth } from '@/lib/auth';
export async function middleware(request: NextRequest) {
const session = await auth.api.getSession({
headers: request.headers,
});
if (!session && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};
Advanced Features
Email Verification
// lib/auth.ts
import { betterAuth } from 'better-auth';
import { sendVerificationEmail } from './email';
export const auth = betterAuth({
// ... config
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
sendVerificationEmail: async ({ user, url }) => {
await sendVerificationEmail({
to: user.email,
subject: 'Verify your email',
html: `Click to verify`,
});
},
},
});
Password Reset
// lib/auth.ts
export const auth = betterAuth({
// ... config
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url }) => {
await sendEmail({
to: user.email,
subject: 'Reset your password',
html: `Reset password`,
});
},
},
});
// Client usage
import { authClient } from '@/lib/auth-client';
await authClient.forgetPassword({ email: 'user@example.com' });
await authClient.resetPassword({ token, newPassword });
Two-Factor Authentication
// lib/auth.ts
import { betterAuth } from 'better-auth';
import { twoFactor } from 'better-auth/plugins';
export const auth = betterAuth({
// ... config
plugins: [
twoFactor({
issuer: 'My App',
}),
],
});
// Client
const { twoFactor } = authClient;
// Enable 2FA
const { totpURI } = await twoFactor.enable();
// Verify 2FA
await twoFactor.verify({ code: '123456' });
Best Practices
- [ ] Always use HTTPS in production
- [ ] Store secrets in environment variables
- [ ] Enable email verification for new accounts
- [ ] Implement rate limiting on auth endpoints
- [ ] Use secure session settings
- [ ] Add CSRF protection
- [ ] Log authentication events
- [ ] Implement account lockout after failed attempts
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: allanninal
- Source: allanninal/claude-code-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.