Install
$ agentstack add skill-armaneker-claude-code-skills-auth-setup ✓ 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Auth Setup
You are an expert in web authentication. Set up complete, production-safe auth with protected routes, sessions, and role-based access control.
Workflow
- Identify the framework: Next.js (default), Express, Fastify, other
- Choose the auth provider based on requirements:
- NextAuth.js (Auth.js) — best for Next.js, supports many OAuth providers + credentials
- Supabase Auth — best when already using Supabase DB; built-in email/OAuth/magic links
- Clerk — best when you want a full UI + managed auth with minimal code
- Identify required auth methods: email+password, Google/GitHub OAuth, magic link, SSO
Option A: NextAuth.js (Auth.js v5)
Installation
npm install next-auth@beta
npx auth secret # generates AUTH_SECRET
Configuration (auth.ts)
import NextAuth from 'next-auth';
import { PrismaAdapter } from '@auth/prisma-adapter';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { db } from '@/lib/db';
import bcrypt from 'bcryptjs';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(db),
session: { strategy: 'jwt' }, // use 'database' for server-side sessions
providers: [
GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! }),
Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! }),
Credentials({
async authorize(credentials) {
const { email, password } = credentials as { email: string; password: string };
const user = await db.user.findUnique({ where: { email } });
if (!user?.passwordHash) return null;
const valid = await bcrypt.compare(password, user.passwordHash);
return valid ? user : null;
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) { token.id = user.id; token.role = (user as any).role; }
return token;
},
async session({ session, token }) {
session.user.id = token.id as string;
session.user.role = token.role as string;
return session;
},
},
pages: {
signIn: '/login',
error: '/login',
},
});
Route Handler (app/api/auth/[...nextauth]/route.ts)
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
Middleware — Protect Routes (middleware.ts)
import { auth } from '@/auth';
import { NextResponse } from 'next/server';
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isProtected = req.nextUrl.pathname.startsWith('/dashboard') ||
req.nextUrl.pathname.startsWith('/settings');
if (isProtected && !isLoggedIn) {
return NextResponse.redirect(new URL('/login', req.url));
}
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
Server Component Auth Check
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const session = await auth();
if (!session) redirect('/login');
return Welcome, {session.user.name};
}
Role-Based Access
// Type augmentation (types/next-auth.d.ts)
declare module 'next-auth' {
interface User { role: string }
interface Session { user: { id: string; role: string } & DefaultSession['user'] }
}
// In a server component or API route
const session = await auth();
if (session?.user.role !== 'admin') {
redirect('/unauthorized');
}
Option B: Supabase Auth
Installation
npm install @supabase/supabase-js @supabase/ssr
Client Setup
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export function createClient() {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll(); },
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options));
},
},
}
);
}
Sign Up / Sign In
// app/actions/auth.ts
'use server';
import { createClient } from '@/lib/supabase/server';
import { redirect } from 'next/navigation';
export async function signUp(email: string, password: string) {
const supabase = createClient();
const { error } = await supabase.auth.signUp({ email, password,
options: { emailRedirectTo: `${process.env.APP_URL}/auth/callback` }
});
if (error) throw error;
redirect('/check-email');
}
export async function signIn(email: string, password: string) {
const supabase = createClient();
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) throw error;
redirect('/dashboard');
}
Middleware
// middleware.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
let response = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { getAll: () => request.cookies.getAll(),
setAll: (c) => { c.forEach(({ name, value, options }) => response.cookies.set(name, value, options)); } } }
);
const { data: { user } } = await supabase.auth.getUser();
const isProtected = request.nextUrl.pathname.startsWith('/dashboard');
if (isProtected && !user) {
return NextResponse.redirect(new URL('/login', request.url));
}
return response;
}
Option C: Clerk
Installation
npm install @clerk/nextjs
Provider (app/layout.tsx)
import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
Middleware
// middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtected = createRouteMatcher(['/dashboard(.*)', '/settings(.*)']);
export default clerkMiddleware((auth, req) => {
if (isProtected(req)) auth().protect();
});
export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'] };
Access User in Server Components
import { currentUser } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';
export default async function Page() {
const user = await currentUser();
if (!user) redirect('/sign-in');
return Hello {user.firstName};
}
Database Schema for Auth (NextAuth Prisma Adapter)
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
role String @default("user")
passwordHash String?
accounts Account[]
sessions Session[]
}
Common Pitfalls — Avoid These
- Storing passwords in plain text — always
bcrypt.hash(password, 12), never less than 10 rounds - Relying on client-side route guards only — always validate on the server (middleware or server component)
- Not setting
httpOnly: trueon session cookies — prevents XSS token theft - Missing CSRF protection on credentials endpoints — NextAuth handles this; custom implementations must add it
- Exposing JWT secret in client bundle —
AUTH_SECRET/NEXTAUTH_SECRETmust only be in server env - Not invalidating sessions on password change — update session version or force re-login after password reset
- Skipping email verification — at minimum, flag unverified accounts and limit their access
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: armaneker
- Source: armaneker/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.