Courses Curriculum Cities Blog Enroll Now
Next.js Full-Stack · Day 4 of 5 ~40 minutes

Day 4: Authentication with NextAuth.js

Add full authentication to your Next.js app: OAuth with GitHub/Google, credential login, protected routes, and session management.

1
Day 1
2
Day 2
3
Day 3
4
Day 4
5
Day 5
What You'll Build

A Next.js app with working GitHub OAuth login, a protected dashboard page that shows user info, and a database-backed session so users stay logged in across refreshes.

1
Section 1 · 10 min

Install and Configure NextAuth.js

NextAuth.js (now Auth.js) handles OAuth, JWT sessions, database sessions, and credential auth. It integrates directly with Next.js and requires minimal configuration for common providers.

bashterminal
npm install next-auth @auth/prisma-adapter
typescriptsrc/app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    })
  ],
  callbacks: {
    session({ session, user }) {
      session.user.id = user.id  // add user ID to session
      return session
    }
  }
})

export { handler as GET, handler as POST }
2
Section 2 · 10 min

Get Session in Server and Client Components

Use getServerSession() in Server Components and useSession() in Client Components. Wrap your app in SessionProvider to make client-side session available everywhere.

typescriptsrc/app/dashboard/page.tsx (Server Component)
import { getServerSession } from 'next-auth'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const session = await getServerSession()

  if (!session) {
    redirect('/api/auth/signin')
  }

  return (
    <div>
      <h1>Welcome, {session.user?.name}</h1>
      <img src={session.user?.image ?? ''} alt="avatar" />
    </div>
  )
}
typescriptsrc/components/NavUser.tsx (Client Component)
'use client'
import { useSession, signIn, signOut } from 'next-auth/react'

export default function NavUser() {
  const { data: session, status } = useSession()

  if (status === 'loading') return <span>Loading...</span>
  if (session) return <button onClick={() => signOut()}>Sign Out</button>
  return <button onClick={() => signIn('github')}>Sign In</button>
}
3
Section 3 · 10 min

Add Auth Models to Prisma Schema

The Prisma Adapter stores sessions and accounts in your database. Add the required models to your schema.

textprisma/schema.prisma (add these models)
model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  access_token      String?
  expires_at        Int?
  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)
}

// Update User model to add:
// accounts  Account[]
// sessions  Session[]
4
Section 4 · 10 min

Credential Authentication

For email/password login, use the Credentials provider. Always hash passwords — never store them in plaintext.

typescriptauth config (credentials provider)
import CredentialsProvider from 'next-auth/providers/credentials'
import bcrypt from 'bcryptjs'

CredentialsProvider({
  name: 'credentials',
  credentials: {
    email: { label: 'Email', type: 'email' },
    password: { label: 'Password', type: 'password' }
  },
  async authorize(credentials) {
    if (!credentials?.email || !credentials.password) return null

    const user = await prisma.user.findUnique({
      where: { email: credentials.email }
    })

    if (!user?.password) return null

    const valid = await bcrypt.compare(credentials.password, user.password)
    return valid ? user : null
  }
})

What You Learned Today

  • Configured NextAuth.js with GitHub OAuth and the Prisma database adapter
  • Retrieved session data in Server Components with getServerSession and Client Components with useSession
  • Added the Account and Session Prisma models required by the NextAuth adapter
  • Implemented credential authentication with bcrypt password hashing
Your Challenge

Go Further on Your Own

  • Add Google OAuth provider alongside GitHub — users can sign in with either
  • Build a /register page that creates a new user with a hashed password
  • Add role-based authorization: admin users can access /admin, regular users cannot
Day 4 Complete

Nice work. Keep going.

Day 5 is ready when you are.

Continue to Day 5
Course Progress
80%

Want live instruction and hands-on projects? Join the AI bootcamp — 3 days, 5 cities.

Finished this lesson?