backend-auth-js

📁 petbrains/mvp-builder 📅 7 days ago
1
总安装量
1
周安装量
#49768
全站排名
安装命令
npx skills add https://github.com/petbrains/mvp-builder --skill backend-auth-js

Agent 安装分布

mcpjam 1
claude-code 1
junie 1
windsurf 1
zencoder 1
crush 1

Skill 文档

Auth.js (NextAuth.js v5)

Overview

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: next-auth@5.0.0-beta (Auth.js v5)
Requirements: Next.js 14.0+

Key Benefit: Minimal config for OAuth providers, built-in CSRF protection, serverless-ready.

When to Use This Skill

✅ Use Auth.js when:

  • Building Next.js App Router applications
  • Need OAuth providers (GitHub, Google, Discord, etc.)
  • Want database sessions with Prisma adapter
  • Building serverless/edge-compatible auth
  • Need quick setup with minimal boilerplate

❌ Use Passport.js instead when:

  • Building Express.js APIs
  • Need 500+ provider strategies
  • Require custom auth flows
  • Not using Next.js

Quick Start

Installation

npm install next-auth@beta @auth/prisma-adapter

Basic Configuration

// 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;
    },
  },
});

Route Handler

// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;

Environment Variables

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

Type Augmentation

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;
  }
}

Prisma Schema for Auth.js

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])
}

Usage Patterns

Server Component (App Router)

// 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>;
}

Client Component

'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 Protection

// 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*'],
};

Sign In/Out Actions

// 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>
  );
}

Integration with tRPC

// 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'));

Session Strategies

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' }

Rules

Do ✅

  • Use AUTH_SECRET environment variable (auto-detected)
  • Extend types in next-auth.d.ts for custom fields
  • Use JWT strategy for serverless deployments
  • Hash passwords with argon2 or bcrypt
  • Use middleware for route protection

Avoid ❌

  • Storing sensitive data in JWT (it’s readable)
  • Using credentials provider without password hashing
  • Skipping CSRF protection (enabled by default)
  • Mixing v4 and v5 patterns (breaking changes)

Troubleshooting

"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

File Structure

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

References