Agent skill
auth-integration
Use when implementing authentication - login/signup forms, session management, protected routes, or role-based access control. NOT when non-auth UI, plain data fetching, or unrelated backend logic. Triggers: "login page", "signup form", "auth setup", "protected route", "role-based access", "Better Auth", "NextAuth".
Install this agent skill to your Project
npx add-skill https://github.com/aiskillstore/marketplace/tree/main/skills/awais68/auth-integration
SKILL.md
Better Auth Integration Skill
Overview
Expert guidance for authentication implementation using Better Auth/NextAuth v5, including login/signup forms, session management with Zustand, protected routes via middleware, and role-based access control for ERP systems.
When This Skill Applies
This skill triggers when users request:
- Auth Setup: "Setup Better Auth", "Configure authentication", "Auth providers"
- Auth Forms: "Login page", "Signup form", "Forgot password", "Email verification"
- Session Management: "Auth store", "Session handling", "Zustand auth"
- Protected Routes: "Protected dashboard", "Auth middleware", "Route guards"
- Role-Based Access: "Role guard", "Permission check", "Admin only", "Teacher access"
Core Rules
1. Provider Setup
// app/auth/[...auth]/route.ts
import { auth } from '@/lib/auth';
import { toNextJsHandler } from 'better-auth/next-js';
export const { GET, POST } = toNextJsHandler(auth);
// lib/auth.ts
import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
export const auth = betterAuth({
database: prismaAdapter(prisma),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 24 hours
},
});
Requirements:
- Use Better Auth v5 for Next.js App Router
- Configure providers (Google, Email/Password)
- Enable email verification for new users
- Set appropriate session expiration
- Use environment variables for secrets (never hardcode)
- Prisma adapter for database integration
2. Auth Forms with Validation
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { signIn } from '@/lib/auth/client';
const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type LoginFormData = z.infer<typeof loginSchema>;
export default function LoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<LoginFormData>({
resolver: zodResolver(loginSchema),
});
const onSubmit = async (data: LoginFormData) => {
await signIn.email({
email: data.email,
password: data.password,
});
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2">Email</label>
<input
{...register('email')}
type="email"
className="w-full px-4 py-2 rounded-lg border"
/>
{errors.email && <p className="text-red-500 text-sm mt-1">{errors.email.message}</p>}
</div>
<div>
<label className="block text-sm font-medium mb-2">Password</label>
<input
{...register('password')}
type="password"
className="w-full px-4 py-2 rounded-lg border"
/>
{errors.password && <p className="text-red-500 text-sm mt-1">{errors.password.message}</p>}
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50"
>
{isSubmitting ? 'Signing in...' : 'Sign In'}
</button>
</form>
);
}
Requirements:
- Use React Hook Form + Zod for validation
- shadcn/ui components for form inputs
- Clear error messages with localization support
- Loading states for better UX
- Accessibility: proper labels, ARIA attributes
- Password strength indicator for signup
3. Session Management
import { create } from 'zustand';
import { authClient } from '@/lib/auth/client';
interface AuthState {
user: User | null;
session: Session | null;
isLoading: boolean;
isAuthenticated: boolean;
role: string | null;
hydrateAuth: () => Promise<void>;
signIn: (email: string, password: string) => Promise<void>;
signOut: () => Promise<void>;
refresh: () => Promise<void>;
}
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
session: null,
isLoading: true,
isAuthenticated: false,
role: null,
hydrateAuth: async () => {
set({ isLoading: true });
try {
const session = await authClient.getSession();
set({
user: session.user,
session: session,
isAuthenticated: !!session,
role: session.user?.role || null,
isLoading: false,
});
} catch (error) {
set({ isLoading: false, isAuthenticated: false });
}
},
signIn: async (email: string, password: string) => {
await authClient.signIn.email({ email, password });
await get().hydrateAuth();
},
signOut: async () => {
await authClient.signOut();
set({
user: null,
session: null,
isAuthenticated: false,
role: null,
});
},
refresh: async () => {
await get().hydrateAuth();
},
}));
export const useAuth = () => {
const auth = useAuthStore();
useEffect(() => {
if (!auth.user && !auth.isLoading) {
auth.hydrateAuth();
}
}, []);
return auth;
};
Requirements:
- Use Zustand for client-side auth state
- Sync with Better Auth session
- Type-safe user and session data
- Automatic session refresh on hydration
- Secure cookie storage (httpOnly, secure, sameSite)
- Clear sessions on sign out
4. Protected Routes
import { authMiddleware } from 'better-auth/next-js';
import { NextResponse } from 'next/server';
export default authMiddleware({
pathPrefix: '/dashboard',
callback: async (request) => {
const { user } = request;
// Redirect to login if not authenticated
if (!user) {
return NextResponse.redirect(new URL('/auth/login', request.url));
}
// Role-based access control
const path = request.nextUrl.pathname;
if (path.startsWith('/dashboard/admin') && user.role !== 'admin') {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
if (path.startsWith('/dashboard/teacher') && user.role !== 'teacher' && user.role !== 'admin') {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
return NextResponse.next();
},
});
export const config = {
matcher: ['/dashboard/:path*'],
};
import { useRouter } from 'next/navigation';
import { useAuth } from '@/hooks/useAuth';
export function withAuth<P extends object>(
WrappedComponent: React.ComponentType<P>,
allowedRoles?: string[]
) {
return function AuthGuard(props: P) {
const { isAuthenticated, user, isLoading } = useAuth();
const router = useRouter();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push('/auth/login');
}
if (!isLoading && isAuthenticated && allowedRoles) {
if (!allowedRoles.includes(user?.role || '')) {
router.push('/unauthorized');
}
}
}, [isAuthenticated, isLoading, user, router]);
if (isLoading) {
return <LoadingSkeleton />;
}
if (!isAuthenticated || (allowedRoles && !allowedRoles.includes(user?.role || ''))) {
return null;
}
return <WrappedComponent {...props} />;
};
}
Requirements:
- Middleware for server-side route protection
- Path matchers for protected routes
- Role-based access control in middleware
- Client-side auth guard HOC for extra protection
- Redirect unauthorized users to appropriate pages
- Loading states during auth checks
5. Role-Based Access Control
export const ROLES = {
ADMIN: 'admin',
TEACHER: 'teacher',
STUDENT: 'student',
PARENT: 'parent',
} as const;
export const PERMISSIONS = {
// Admin permissions
MANAGE_USERS: 'manage:users',
MANAGE_COURSES: 'manage:courses',
VIEW_ALL_DATA: 'view:all_data',
// Teacher permissions
VIEW_CLASS: 'view:class',
MANAGE_STUDENTS: 'manage:students',
CREATE_ASSIGNMENTS: 'create:assignments',
// Student permissions
VIEW_OWN_DATA: 'view:own_data',
SUBMIT_ASSIGNMENTS: 'submit:assignments',
} as const;
export const hasRole = (user: User | null, role: string): boolean => {
return user?.role === role || user?.role === ROLES.ADMIN;
};
export const hasPermission = (user: User | null, permission: string): boolean => {
if (!user) return false;
if (user.role === ROLES.ADMIN) return true;
const rolePermissions = {
[ROLES.ADMIN]: Object.values(PERMISSIONS),
[ROLES.TEACHER]: [
PERMISSIONS.VIEW_CLASS,
PERMISSIONS.MANAGE_STUDENTS,
PERMISSIONS.CREATE_ASSIGNMENTS,
],
[ROLES.STUDENT]: [
PERMISSIONS.VIEW_OWN_DATA,
PERMISSIONS.SUBMIT_ASSIGNMENTS,
],
[ROLES.PARENT]: [
PERMISSIONS.VIEW_OWN_DATA,
],
};
return rolePermissions[user.role]?.includes(permission) ?? false;
};
export const PermissionGuard = ({
permission,
fallback = null,
children,
}: {
permission: string;
fallback?: React.ReactNode;
children: React.ReactNode;
}) => {
const { user } = useAuth();
if (!hasPermission(user, permission)) {
return <>{fallback}</>;
}
return <>{children}</>;
};
Requirements:
- Define roles and permissions clearly
- Admin has all permissions by default
- Type-safe permission checking
- Permission-aware components
- Server-side permission verification for sensitive actions
- Audit logs for permission checks
Output Requirements
Code Files
-
Auth Configuration:
lib/auth.ts- Better Auth setupapp/auth/[...auth]/route.ts- Auth API routes
-
Forms:
app/auth/login/page.tsx- Login formapp/auth/signup/page.tsx- Signup formapp/auth/forgot-password/page.tsx- Password reset
-
Session Management:
lib/auth-store.ts- Zustand auth storehooks/useAuth.ts- Auth hook
-
Protected Routes:
middleware.ts- Route protectioncomponents/AuthGuard.tsx- Auth guard HOC
Integration Requirements
- shadcn/ui: Use shadcn form components
- @ui-ux-designer: Ensure responsive, accessible forms
- @nextjs-app-router: Follow Next.js App Router patterns
- @react-component: Use functional components with hooks
Documentation
- PHR: Create Prompt History Record for auth decisions
- ADR: Document auth strategy (JWT vs DB sessions, provider choice)
- Comments: Document security considerations
Workflow
-
Analyze Auth Requirements
- Identify auth providers needed (Google, Email/Password)
- Determine role-based access needs
- Check email verification requirements
-
Setup Auth Provider
- Configure Better Auth with providers
- Setup database adapter (Prisma)
- Configure session settings
-
Create Auth Forms
- Build login form with validation
- Create signup with email verification
- Add password reset flow
-
Implement Session Management
- Create Zustand auth store
- Sync with Better Auth session
- Handle session refresh
-
Protect Routes
- Setup middleware for server-side protection
- Create auth guard HOC for client-side
- Implement role-based access control
-
Validate Security
- Check OWASP compliance
- Verify CSRF protection
- Test session handling
Quality Checklist
Before completing any auth implementation:
- OWASP Secure: CSRF tokens, XSS protection, secure headers
- Password Hash/Verify: bcrypt/argon2, no plain text storage
- Role Guards: Middleware + client-side role checks
- Logout Clear Sessions: Clear cookies, Zustand store, redirect
- Responsive Mobile Login: Mobile-friendly forms, touch targets 44px+
- Email Verification: Required for new accounts
- Session Expiration: Reasonable timeout (7-30 days)
- Secure Cookies: httpOnly, secure, sameSite='strict'
- Rate Limiting: Prevent brute force attacks
- Audit Logging: Log auth events for security monitoring
Common Patterns
Login Page with Google Auth
'use client';
import { useState } from 'react';
import { signIn } from '@/lib/auth/client';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const router = useRouter();
const [error, setError] = useState('');
const handleGoogleSignIn = async () => {
try {
await signIn.social({
provider: 'google',
callbackURL: '/dashboard',
});
} catch (err) {
setError('Failed to sign in with Google');
}
};
const handleEmailSignIn = async (email: string, password: string) => {
try {
await signIn.email({
email,
password,
callbackURL: '/dashboard',
});
router.push('/dashboard');
} catch (err) {
setError('Invalid email or password');
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow-md">
<h1 className="text-3xl font-bold text-center">Sign In</h1>
{error && (
<div className="p-3 bg-red-100 text-red-700 rounded-lg">
{error}
</div>
)}
<button
onClick={handleGoogleSignIn}
className="w-full flex items-center justify-center px-4 py-3 border border-gray-300 rounded-lg hover:bg-gray-50"
>
Sign in with Google
</button>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500">Or continue with email</span>
</div>
</div>
<LoginForm onSubmit={handleEmailSignIn} />
<p className="text-center text-sm text-gray-600">
Don't have an account?{' '}
<a href="/auth/signup" className="text-blue-600 hover:underline">
Sign up
</a>
</p>
</div>
</div>
);
}
Signup with Email Verification
'use client';
import { useState } from 'react';
import { signUp } from '@/lib/auth/client';
export default function SignupPage() {
const [success, setSuccess] = useState(false);
const [error, setError] = useState('');
const handleSignup = async (data: SignupFormData) => {
try {
await signUp.email({
email: data.email,
password: data.password,
name: data.name,
});
setSuccess(true);
} catch (err) {
setError('Failed to create account');
}
};
if (success) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl font-bold mb-4">Check your email</h1>
<p className="text-gray-600">
We've sent a verification link to your email address
</p>
</div>
</div>
);
}
return <SignupForm onSubmit={handleSignup} />;
}
Protected Dashboard with Role Guard
import { withAuth } from '@/components/AuthGuard';
import { ROLES } from '@/lib/permissions';
function AdminDashboard() {
const { user } = useAuth();
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6">Admin Dashboard</h1>
<p>Welcome, {user?.name}</p>
</div>
);
}
export default withAuth(AdminDashboard, [ROLES.ADMIN]);
Password Strength Indicator
export const PasswordStrength = ({ password }: { password: string }) => {
const getStrength = (pwd: string) => {
let strength = 0;
if (pwd.length >= 8) strength++;
if (/[a-z]/.test(pwd)) strength++;
if (/[A-Z]/.test(pwd)) strength++;
if (/[0-9]/.test(pwd)) strength++;
if (/[^a-zA-Z0-9]/.test(pwd)) strength++;
return strength;
};
const strength = getStrength(password);
const colors = ['bg-red-500', 'bg-orange-500', 'bg-yellow-500', 'bg-green-500', 'bg-green-600'];
const labels = ['Weak', 'Fair', 'Good', 'Strong', 'Very Strong'];
return (
<div className="mt-2">
<div className="flex gap-1">
{[1, 2, 3, 4, 5].map((i) => (
<div
key={i}
className={`h-2 flex-1 rounded ${i <= strength ? colors[strength - 1] : 'bg-gray-200'}`}
/>
))}
</div>
{password && (
<p className={`text-sm mt-1 ${colors[strength - 1]?.replace('bg-', 'text-')}`}>
Password strength: {labels[strength - 1]}
</p>
)}
</div>
);
};
Security Best Practices
OWASP Compliance
- CSRF Protection: Use built-in Better Auth CSRF tokens
- XSS Prevention: Sanitize user input, use React's auto-escaping
- SQL Injection: Use Prisma ORM (parameterized queries)
- Password Security: bcrypt/argon2 hashing, minimum 8 characters
- Session Security: httpOnly, secure, sameSite cookies
Rate Limiting
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, '10 s'), // 5 requests per 10 seconds
});
export async function checkRateLimit(identifier: string) {
const { success } = await ratelimit.limit(identifier);
return success;
}
Audit Logging
export async function logAuthEvent(event: {
type: 'login' | 'logout' | 'signup' | 'password_reset';
userId?: string;
ip?: string;
userAgent?: string;
}) {
await prisma.authEvent.create({
data: {
...event,
timestamp: new Date(),
},
});
}
Environment Variables
# .env.local
AUTH_SECRET=your-super-secret-random-string
BETTER_AUTH_URL=http://localhost:3000
# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Database
DATABASE_URL=your-database-url
References
- Better Auth: https://www.better-auth.com
- NextAuth v5: https://authjs.dev
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- shadcn/ui Forms: https://ui.shadcn.com/docs/components/form
- React Hook Form: https://react-hook-form.com
- Zod Validation: https://zod.dev
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
perigon-backend
Perigon ASP.NET Core + EF Core + Aspire conventions
perigon-agent
Pointers for Copilot/agents to apply Perigon conventions
perigon-angular
Angular 21+ standalone/Material/signal conventions for Perigon WebApp
fastapi-mastery
Comprehensive FastAPI development skill covering REST API creation, routing, request/response handling, validation, authentication, database integration, middleware, and deployment. Use when working with FastAPI projects, building APIs, implementing CRUD operations, setting up authentication/authorization, integrating databases (SQL/NoSQL), adding middleware, handling WebSockets, or deploying FastAPI applications. Triggered by requests involving .py files with FastAPI code, API endpoint creation, Pydantic models, or FastAPI-specific features.
context7-efficient
Token-efficient library documentation fetcher using Context7 MCP with 86.8% token savings through intelligent shell pipeline filtering. Fetches code examples, API references, and best practices for JavaScript, Python, Go, Rust, and other libraries. Use when users ask about library documentation, need code examples, want API usage patterns, are learning a new framework, need syntax reference, or troubleshooting with library-specific information. Triggers include questions like "Show me React hooks", "How do I use Prisma", "What's the Next.js routing syntax", or any request for library/framework documentation.
browser-use
Browser automation using Playwright MCP. Navigate websites, fill forms, click elements, take screenshots, and extract data. Use when tasks require web browsing, form submission, web scraping, UI testing, or any browser interaction.
Didn't find tool you were looking for?