auth-flow Skill
Implement secure authentication patterns including OAuth, session management, and protected routes.
Overview#
The auth-flow skill generates production-ready authentication code following security best practices. It supports multiple authentication providers and patterns.
Usage#
Use the auth-flow skill to implement OAuth authentication with Google and GitHub.
Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
provider | string | Yes | Auth provider: clerk, nextauth, custom |
strategies | array | No | Login strategies: oauth, credentials, magic-link |
providers | array | No | OAuth providers: google, github, apple |
sessionType | string | No | Session strategy: jwt, database |
Generated Output#
Server-Side Auth Check (Clerk)#
1// lib/auth.ts
2import { auth } from '@clerk/nextjs/server'
3
4export async function requireAuth() {
5 const { userId } = await auth()
6
7 if (!userId) {
8 throw new Error('UNAUTHORIZED')
9 }
10
11 return userId
12}
13
14// Usage in Server Action
15export async function myAction() {
16 const userId = await requireAuth()
17 // Continue with authenticated user
18}Protected Layout#
1// app/(protected)/layout.tsx
2import { auth } from '@clerk/nextjs/server'
3import { redirect } from 'next/navigation'
4
5export default async function ProtectedLayout({
6 children
7}: {
8 children: React.ReactNode
9}) {
10 const { userId } = await auth()
11
12 if (!userId) {
13 redirect('/sign-in')
14 }
15
16 return <>{children}</>
17}Get Current User with Database Sync#
1// lib/auth.ts
2import { currentUser } from '@clerk/nextjs/server'
3import { prisma } from '@/lib/db'
4
5export async function getCurrentUser() {
6 const user = await currentUser()
7 if (!user) return null
8
9 // Get or create database user
10 const dbUser = await prisma.user.upsert({
11 where: { clerkId: user.id },
12 update: {
13 email: user.emailAddresses[0]?.emailAddress,
14 name: `${user.firstName} ${user.lastName}`.trim()
15 },
16 create: {
17 clerkId: user.id,
18 email: user.emailAddresses[0]?.emailAddress ?? '',
19 name: `${user.firstName} ${user.lastName}`.trim()
20 }
21 })
22
23 return dbUser
24}NextAuth.js Setup#
1// auth.ts
2import NextAuth from 'next-auth'
3import GitHub from 'next-auth/providers/github'
4import Google from 'next-auth/providers/google'
5import { PrismaAdapter } from '@auth/prisma-adapter'
6import { prisma } from '@/lib/db'
7
8export const { handlers, auth, signIn, signOut } = NextAuth({
9 adapter: PrismaAdapter(prisma),
10 session: { strategy: 'jwt' },
11 providers: [
12 GitHub({
13 clientId: process.env.GITHUB_ID!,
14 clientSecret: process.env.GITHUB_SECRET!
15 }),
16 Google({
17 clientId: process.env.GOOGLE_ID!,
18 clientSecret: process.env.GOOGLE_SECRET!
19 })
20 ],
21 callbacks: {
22 async jwt({ token, user }) {
23 if (user) {
24 token.id = user.id
25 }
26 return token
27 },
28 async session({ session, token }) {
29 if (session.user) {
30 session.user.id = token.id as string
31 }
32 return session
33 }
34 },
35 pages: {
36 signIn: '/login',
37 error: '/login'
38 }
39})Custom JWT Session Management#
1// lib/session.ts
2import { SignJWT, jwtVerify } from 'jose'
3import { cookies } from 'next/headers'
4
5const secret = new TextEncoder().encode(process.env.JWT_SECRET)
6const COOKIE_NAME = 'session'
7
8export async function createSession(userId: string) {
9 const token = await new SignJWT({ userId })
10 .setProtectedHeader({ alg: 'HS256' })
11 .setIssuedAt()
12 .setExpirationTime('7d')
13 .sign(secret)
14
15 cookies().set(COOKIE_NAME, token, {
16 httpOnly: true,
17 secure: process.env.NODE_ENV === 'production',
18 sameSite: 'lax',
19 maxAge: 60 * 60 * 24 * 7, // 7 days
20 path: '/'
21 })
22
23 return token
24}
25
26export async function getSession() {
27 const token = cookies().get(COOKIE_NAME)?.value
28
29 if (!token) return null
30
31 try {
32 const { payload } = await jwtVerify(token, secret)
33 return payload as { userId: string }
34 } catch {
35 return null
36 }
37}
38
39export async function destroySession() {
40 cookies().delete(COOKIE_NAME)
41}Middleware Configuration#
1// middleware.ts
2import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
3
4const isProtectedRoute = createRouteMatcher([
5 '/dashboard(.*)',
6 '/settings(.*)',
7 '/api/user(.*)'
8])
9
10const isPublicRoute = createRouteMatcher([
11 '/',
12 '/sign-in(.*)',
13 '/sign-up(.*)',
14 '/api/webhooks(.*)'
15])
16
17export default clerkMiddleware(async (auth, req) => {
18 if (isProtectedRoute(req)) {
19 await auth.protect()
20 }
21})
22
23export const config = {
24 matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)']
25}OAuth Social Buttons#
1// components/auth/SocialButtons.tsx
2'use client'
3
4import { signIn } from 'next-auth/react'
5import { Github } from 'lucide-react'
6
7export function SocialButtons() {
8 return (
9 <div className="space-y-3">
10 <button
11 onClick={() => signIn('google', { callbackUrl: '/dashboard' })}
12 className="flex w-full items-center justify-center gap-2 rounded-lg border py-2 hover:bg-gray-50"
13 >
14 <GoogleIcon className="h-5 w-5" />
15 Continue with Google
16 </button>
17
18 <button
19 onClick={() => signIn('github', { callbackUrl: '/dashboard' })}
20 className="flex w-full items-center justify-center gap-2 rounded-lg border py-2 hover:bg-gray-50"
21 >
22 <Github className="h-5 w-5" />
23 Continue with GitHub
24 </button>
25 </div>
26 )
27}Client-Side Auth Hook#
1// hooks/use-user.ts
2'use client'
3import { useUser } from '@clerk/nextjs'
4
5export function useCurrentUser() {
6 const { user, isLoaded, isSignedIn } = useUser()
7
8 return {
9 user: isSignedIn ? user : null,
10 isLoading: !isLoaded,
11 isAuthenticated: isSignedIn
12 }
13}Features Included#
- Server-side authentication checks
- Protected route layouts
- JWT session management
- OAuth provider integration
- Middleware protection
- Database user sync
- Client-side hooks
Customization Options#
Use the auth-flow skill with:
- provider: "nextauth"
- strategies: ["oauth", "credentials"]
- providers: ["google", "github"]
- sessionType: "jwt"
Best Practices#
Security Considerations#
- Always use
httpOnlycookies for session tokens - Set
secure: truein production - Use
sameSite: 'lax'or'strict'for CSRF protection - Never expose JWT secrets to client-side code
- Implement session refresh for long-lived sessions
Authentication Flow#
- User initiates login
- Validate credentials or OAuth callback
- Create session token
- Store in secure cookie
- Verify token on protected routes
- Refresh token before expiry
Error Handling#
1// app/auth/error/page.tsx
2const errorMessages: Record<string, string> = {
3 OAuthSignin: 'Error starting OAuth flow',
4 OAuthCallback: 'Error in OAuth callback',
5 OAuthAccountNotLinked: 'Email already registered with different provider',
6 CredentialsSignin: 'Invalid credentials',
7 SessionRequired: 'Please sign in to continue',
8 default: 'An error occurred'
9}Related Skills#
- api-endpoint - Protected API routes
- validation - Input validation for login forms
- error-handling - Auth error management