xiaohongshu
xiaohongshu
backend-auth-js
Authentication library for Next.js applications (NextAuth.js v5). Use when building Next.js 14+ apps that need OAuth providers (GitHub, Google, etc.), credentials login, or session management. Provides adapters for Prisma, Drizzle, and other databases. Choose Auth.js over Passport.js for Next.js ...
Full skill instructions
Auth.js (NextAuth.js v5) is the standard authentication solution for Next.js. It handles OAuth, credentials, JWT/database sessions, and integrates natively with App Router.
Version: [email protected] (Auth.js v5)
Requirements: Next.js 14.0+
Key Benefit: Minimal config for OAuth providers, built-in CSRF protection, serverless-ready.
✅ Use Auth.js when:
❌ Use Passport.js instead when:
npm install next-auth@beta @auth/prisma-adapter
// auth.ts (root level)
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from '@/lib/prisma';
import { verify } from 'argon2';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: 'jwt' },
providers: [
GitHub,
Google,
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
const user = await prisma.user.findUnique({
where: { email: credentials.email as string },
});
if (!user?.password) return null;
const valid = await verify(user.password, credentials.password as string);
if (!valid) return null;
return { id: user.id, email: user.email, name: user.name, role: user.role };
},
}),
],
callbacks: {
jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
session({ session, token }) {
session.user.id = token.id as string;
session.user.role = token.role as string;
return session;
},
},
});
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
AUTH_SECRET=your-secret-key-here # openssl rand -base64 32
AUTH_GITHUB_ID=xxx
AUTH_GITHUB_SECRET=xxx
AUTH_GOOGLE_ID=xxx
AUTH_GOOGLE_SECRET=xxx
Extend session/JWT types for custom fields:
// types/next-auth.d.ts
import { DefaultSession } from 'next-auth';
declare module 'next-auth' {
interface Session {
user: {
id: string;
role: string;
} & DefaultSession['user'];
}
interface User {
role: string;
}
}
declare module 'next-auth/jwt' {
interface JWT {
id: string;
role: string;
}
}
model User {
id String @id @default(cuid())
name String?
email String @unique
emailVerified DateTime?
image String?
password String? // For credentials provider
role String @default("user")
accounts Account[]
sessions Session[]
}
model Account {
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)
@@id([provider, providerAccountId])
}
model Session {
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {
identifier String
token String
expires DateTime
@@unique([identifier, token])
}
// app/dashboard/page.tsx
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const session = await auth();
if (!session) {
redirect('/login');
}
return <div>Welcome, {session.user.name}</div>;
}
'use client';
import { useSession } from 'next-auth/react';
export function UserButton() {
const { data: session, status } = useSession();
if (status === 'loading') return <Skeleton />;
if (!session) return <SignInButton />;
return <span>{session.user.name}</span>;
}
// middleware.ts
import { auth } from '@/auth';
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isOnDashboard = req.nextUrl.pathname.startsWith('/dashboard');
if (isOnDashboard && !isLoggedIn) {
return Response.redirect(new URL('/login', req.nextUrl));
}
});
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'],
};
// app/login/page.tsx
import { signIn, signOut } from '@/auth';
export default function LoginPage() {
return (
<div>
<form action={async () => {
'use server';
await signIn('github');
}}>
<button>Sign in with GitHub</button>
</form>
<form action={async () => {
'use server';
await signIn('credentials', { email, password });
}}>
{/* credentials form */}
</form>
</div>
);
}
// src/server/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { auth } from '@/auth';
export const createContext = async () => {
const session = await auth();
return { session, prisma };
};
const t = initTRPC.context<Context>().create();
const isAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session?.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({ ctx: { user: ctx.session.user } });
});
export const protectedProcedure = t.procedure.use(isAuthed);
// Role-based
const hasRole = (role: string) => t.middleware(({ ctx, next }) => {
if (ctx.session?.user?.role !== role) {
throw new TRPCError({ code: 'FORBIDDEN' });
}
return next();
});
export const adminProcedure = protectedProcedure.use(hasRole('admin'));
| Strategy | Storage | Use Case |
|---|---|---|
jwt | Cookie | Serverless, Edge, stateless |
database | Prisma/DB | Need to revoke sessions |
// JWT (default, recommended for most cases)
session: { strategy: 'jwt' }
// Database sessions
session: { strategy: 'database' }
AUTH_SECRET environment variable (auto-detected)next-auth.d.ts for custom fields"Session is null in server component":
→ Ensure auth.ts exports are correct
→ Check AUTH_SECRET is set
→ Verify cookies are being sent
"OAuth callback error":
→ Check provider credentials in env
→ Verify callback URL in provider dashboard
→ Match AUTH_URL with actual URL
"Type errors on session.user":
→ Create types/next-auth.d.ts
→ Extend Session and JWT interfaces
→ Restart TypeScript server
"Credentials provider not working":
→ Must use JWT strategy with credentials
→ Check authorize function returns user object
→ Verify password comparison
app/
├── api/auth/[...nextauth]/route.ts # Auth handlers
├── login/page.tsx # Login page
└── dashboard/page.tsx # Protected page
auth.ts # Auth configuration
middleware.ts # Route protection
types/next-auth.d.ts # Type extensions
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience