搜索

PythonReactSEOPDF
GitHub Skill

nextauth

Implements authentication with Auth.js/NextAuth.js v5 including OAuth providers, credentials, sessions, and route protection. Use when adding authentication to Next.js, configuring OAuth providers, or protecting routes.

mgd34msu☆ 3 仓库 Stars0 仓库 Forks

安装与使用

请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。

查看来源 ↗使用指南

技能说明

NextAuth.js / Auth.js

Flexible authentication library for Next.js with 80+ OAuth providers and database adapters.

Quick Start

Install:

npm install next-auth@beta

Generate secret:

npx auth secret

This adds AUTH_SECRET to .env.local.

Configuration

Auth Config
// auth.ts
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';

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    GitHub,
    Google,
    Credentials({
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' },
      },
      authorize: async (credentials) => {
        // Validate credentials against database
        const user = await getUserFromDb(
          credentials.email as string,
          credentials.password as string
        );

        if (!user) return null;

        return {
          id: user.id,
          email: user.email,
          name: user.name,
        };
      },
    }),
  ],
});
Route Handler
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';

export const { GET, POST } = handlers;
Environment Variables
# .env.local
AUTH_SECRET=your-generated-secret

# OAuth Providers
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret

AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret

OAuth Providers

GitHub
import GitHub from 'next-auth/providers/github';

export const { handlers, auth } = NextAuth({
  providers: [
    GitHub({
      clientId: process.env.AUTH_GITHUB_ID,
      clientSecret: process.env.AUTH_GITHUB_SECRET,
    }),
  ],
});
Google
import Google from 'next-auth/providers/google';

export const { handlers, auth } = NextAuth({
  providers: [
    Google({
      clientId: process.env.AUTH_GOOGLE_ID,
      clientSecret: process.env.AUTH_GOOGLE_SECRET,
      authorization: {
        params: {
          prompt: 'consent',
          access_type: 'offline',
          response_type: 'code',
        },
      },
    }),
  ],
});
Discord
import Discord from 'next-auth/providers/discord';

export const { handlers, auth } = NextAuth({
  providers: [Discord],
});

Credentials Provider

import Credentials from 'next-auth/providers/credentials';
import { compare } from 'bcryptjs';

export const { handlers, auth } = NextAuth({
  providers: [
    Credentials({
      name: 'credentials',
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' },
      },
      authorize: async (credentials) => {
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        const user = await prisma.user.findUnique({
          where: { email: credentials.email as string },
        });

        if (!user || !user.password) {
          return null;
        }

        const isValid = await compare(
          credentials.password as string,
          user.password
        );

        if (!isValid) {
          return null;
        }

        return {
          id: user.id,
          email: user.email,
          name: user.name,
          image: user.image,
        };
      },
    }),
  ],
  pages: {
    signIn: '/login',
  },
});

Session Management

Get Session (Server)
// In Server Components
import { auth } from '@/auth';

export default async function Page() {
  const session = await auth();

  if (!session) {
    return <div>Please sign in</div>;
  }

  return (
    <div>
      <p>Welcome, {session.user?.name}</p>
      <p>Email: {session.user?.email}</p>
    </div>
  );
}
Get Session (Client)
'use client';

import { useSession } from 'next-auth/react';

export function UserInfo() {
  const { data: session, status } = useSession();

  if (status === 'loading') {
    return <div>Loading...</div>;
  }

  if (status === 'unauthenticated') {
    return <div>Not signed in</div>;
  }

  return (
    <div>
      <p>Signed in as {session?.user?.name}</p>
    </div>
  );
}
Session Provider
// app/layout.tsx
import { SessionProvider } from 'next-auth/react';
import { auth } from '@/auth';

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await auth();

  return (
    <html lang="en">
      <body>
        <SessionProvider session={session}>
          {children}
        </SessionProvider>
      </body>
    </html>
  );
}

Sign In / Sign Out

Server Actions
// app/actions.ts
'use server';

import { signIn, signOut } from '@/auth';

export async function handleSignIn(provider: string) {
  await signIn(provider, { redirectTo: '/dashboard' });
}

export async function handleSignOut() {
  await signOut({ redirectTo: '/' });
}
UI Components
'use client';

import { signIn, signOut } from 'next-auth/react';

export function SignInButton() {
  return (
    <button onClick={() => signIn('github')}>
      Sign in with GitHub
    </button>
  );
}

export function SignOutButton() {
  return (
    <button onClick={() => signOut()}>
      Sign out
    </button>
  );
}

// Or using server actions
import { handleSignIn, handleSignOut } from './actions';

export function AuthButtons() {
  return (
    <div>
      <form action={() => handleSignIn('github')}>
        <button type="submit">Sign in with GitHub</button>
      </form>
      <form action={handleSignOut}>
        <button type="submit">Sign out</button>
      </form>
    </div>
  );
}

Route Protection

Middleware
// middleware.ts
export { auth as middleware } from '@/auth';

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
With Custom Logic
// middleware.ts
import { auth } from '@/auth';
import { NextResponse } from 'next/server';

export default auth((req) => {
  const isLoggedIn = !!req.auth;
  const isOnDashboard = req.nextUrl.pathname.startsWith('/dashboard');
  const isOnAuth = req.nextUrl.pathname.startsWith('/login');

  if (isOnDashboard && !isLoggedIn) {
    return NextResponse.redirect(new URL('/login', req.url));
  }

  if (isOnAuth && isLoggedIn) {
    return NextResponse.redirect(new URL('/dashboard', req.url));
  }

  return NextResponse.next();
});

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
Authorized Callback
// auth.ts
export const { handlers, auth } = NextAuth({
  providers: [...],
  callbacks: {
    authorized: async ({ auth, request }) => {
      const isLoggedIn = !!auth?.user;
      const isProtected = request.nextUrl.pathname.startsWith('/dashboard');

      if (isProtected && !isLoggedIn) {
        return false; // Redirects to signIn page
      }

      return true;
    },
  },
});
API Route Protection
// app/api/protected/route.ts
import { auth } from '@/auth';
import { NextResponse } from 'next/server';

export const GET = auth(function GET(req) {
  if (!req.auth) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }

  return NextResponse.json({
    user: req.auth.user,
    message: 'Protected data',
  });
});

Callbacks

JWT & Session Callbacks
// auth.ts
export const { handlers, auth } = NextAuth({
  providers: [...],
  callbacks: {
    jwt: async ({ token, user, account }) => {
      // Add user data to token on sign in
      if (user) {
        token.id = user.id;
        token.role = user.role;
      }

      // Add access token from OAuth
      if (account) {
        token.accessToken = account.access_token;
      }

      return token;
    },
    session: async ({ session, token }) => {
      // Add token data to session
      if (token) {
        session.user.id = token.id as string;
        session.user.role = token.role as string;
        session.accessToken = token.accessToken as string;
      }

      return session;
    },
  },
});
Sign In Callback
callbacks: {
  signIn: async ({ user, account, profile }) => {
    // Allow OAuth sign in
    if (account?.provider !== 'credentials') {
      return true;
    }

    // Check if user is verified
    const existingUser = await getUserById(user.id);
    if (!existingUser?.emailVerified) {
      return false;
    }

    return true;
  },
}

Database Adapters

Prisma Adapter
npm install @auth/prisma-adapter
// auth.ts
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from '@/lib/prisma';

export const { handlers, auth } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [...],
  session: {
    strategy: 'database', // or 'jwt'
  },
});
Drizzle Adapter
npm install @auth/drizzle-adapter
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import { db } from '@/db';

export const { handlers, auth } = NextAuth({
  adapter: DrizzleAdapter(db),
  providers: [...],
});

TypeScript Extensions

// types/next-auth.d.ts
import { DefaultSession } from 'next-auth';

declare module 'next-auth' {
  interface Session {
    user: {
      id: string;
      role: string;
    } & DefaultSession['user'];
    accessToken?: string;
  }

  interface User {
    role: string;
  }
}

declare module 'next-auth/jwt' {
  interface JWT {
    id: string;
    role: string;
    accessToken?: string;
  }
}

Custom Pages

// auth.ts
export const { handlers, auth } = NextAuth({
  providers: [...],
  pages: {
    signIn: '/login',
    signOut: '/logout',
    error: '/auth/error',
    verifyRequest: '/auth/verify',
    newUser: '/onboarding',
  },
});
// app/login/page.tsx
import { signIn } from '@/auth';

export default function LoginPage() {
  return (
    <div className="flex flex-col gap-4 max-w-md mx-auto mt-20">
      <h1 className="text-2xl font-bold">Sign In</h1>

      <form
        action={async () => {
          'use server';
          await signIn('github', { redirectTo: '/dashboard' });
        }}
      >
        <button className="w-full p-2 bg-gray-900 text-white rounded">
          Sign in with GitHub
        </button>
      </form>

      <form
        action={async () => {
          'use server';
          await signIn('google', { redirectTo: '/dashboard' });
        }}
      >
        <button className="w-full p-2 bg-blue-600 text-white rounded">
          Sign in with Google
        </button>
      </form>
    </div>
  );
}

Best Practices

  1. Use middleware for protection - Centralized auth checks
  2. Verify near data layer - Don't rely only on middleware
  3. Extend session types - Add custom user properties
  4. Use database sessions for sensitive apps - More secure than JWT
  5. Handle errors gracefully - Custom error pages

Common Mistakes

Mistake Fix
Missing AUTH_SECRET Run npx auth secret
Exposed credentials Use environment variables
JWT-only with credentials Consider database sessions
Missing session provider Wrap app with SessionProvider
No redirect after auth Set redirectTo option

Reference Files

使用示例

查看来源仓库中的使用示例与配置说明。

查看来源 ↗

来源历史

收录时间
2026-01-20
索引更新时间
2026-01-28
Branch
main

收录与同步日期属于本站记录。源码修改历史请查看来源仓库。

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!