Next.js
I. Next.js Fundamentals
1. Introduction to Next.js
2. Next.js vs React
3. Project Setup
4. Folder Structure
5. Pages and Routing
6. Link and Navigation
7. Static Assets
8. CSS and Styling
II. Routing, Data & Rendering
9. Dynamic Routing
10. API Routes
11. Data Fetching Basics
12. getStaticProps
13. getServerSideProps
14. Incremental Static Regeneration
15. Rendering Strategies
16. Image Optimization
III. Advanced Next.js & Performance
17. Middleware
18. Authentication Basics
19. Authorization
20. Environment Variables
21. Performance Optimization
22. SEO with Next.js
23. Internationalization
24. Error Handling
IV. Projects, Deployment & Best Practices
25. Next.js Best Practices
26. Folder and Code Organization
27. Testing Next.js Apps
28. Security in Next.js
29. Next.js Interview Questions
30. Mini Project – Blog
31. Mini Project – Dashboard
32. Mini Project – Ecommerce
33. Mini Project – API Integration
34. Next.js Case Study
35. Real-World Use Cases
36. Project Planning
37. Final Project
38. Deployment with Vercel
39. Course Review
40. Career Roadmap
LESSON 18
Authentication Basics
Build secure user login and registration for NewsWave using multiple authentication strategies.
Authentication forms the backbone of modern web applications. Without it, you cannot identify users, protect content, or personalize experiences. Think of authentication as checking someone's ID at a club entrance — you need to verify who they are before letting them inside.
Next.js provides multiple ways to handle authentication, each suited for different scenarios. You might use simple email/password forms, third-party providers like Google, or enterprise solutions. The NewsWave platform needs user accounts so readers can save articles, comment, and access premium content.
Authentication differs from authorization — a concept we'll explore in the next lesson. Authentication answers "who are you?" while authorization answers "what can you do?" Like having a driver's license (authentication) versus having permission to drive a company car (authorization).
The complexity varies dramatically based on your needs. A simple blog might only need admin login. An e-commerce site requires user accounts, password reset, and profile management. Enterprise applications often integrate with existing identity systems.
Authentication Strategies
Next.js supports several authentication patterns, each with distinct advantages and use cases. Understanding these patterns helps you choose the right approach for your application's requirements. Session-based authentication works like a hotel key card. When you log in, the server creates a session and gives you a session ID cookie. Every request includes this cookie, and the server looks up your session to verify identity. Sessions live on the server, making them secure but requiring server storage. Token-based authentication uses JSON Web Tokens (JWTs) — like carrying a tamper-proof passport. The server signs a token containing user information, and the client stores it. Each request includes the token, which the server verifies without database lookups. Tokens work well for APIs and distributed systems.// Session-based: Server stores user data
const session = {
userId: 123,
email: 'reader@newswave.com',
expires: new Date('2024-12-31')
}
// Token-based: Client stores signed user data
const jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...'Sessions
Server storage, secure cookies, traditional web apps
JWTs
Stateless, API-friendly, mobile compatible
OAuth
Social login, no password storage, trusted providers
Third-party
Full-featured, managed security, quick integration
NextAuth.js Overview
NextAuth.js stands as the most popular authentication library for Next.js applications. Built specifically for Next.js, it handles the complexity of modern authentication while providing a clean developer experience. The library supports multiple providers, databases, and authentication flows out of the box. NextAuth.js works by creating API routes that handle authentication logic. When users log in, these routes process credentials, verify identity, and manage sessions or tokens. The library provides React hooks to access user data throughout your application. The architecture centers around providers, adapters, and sessions. Providers handle different login methods — email/password, Google OAuth, GitHub, etc. Adapters connect to databases for storing user accounts and sessions. Sessions maintain user state between requests.# Install NextAuth.js for NewsWave authentication
npm install next-auth
# Optional: Install database adapter
npm install @next-auth/prisma-adapterTerminal
$ npm install next-auth
+ next-auth@4.24.5
added 42 packages, and audited 284 packages in 3s
✓ NextAuth.js installed successfully
What just happened?
NextAuth.js provides authentication infrastructure for Next.js applications. It handles multiple login providers, manages user sessions, and implements security best practices. The library reduces authentication complexity from weeks of development to a few configuration files.
Basic Setup Configuration
Setting up NextAuth.js requires creating configuration files and environment variables. The process involves defining authentication providers, configuring session handling, and setting up API routes. Start by creating the main configuration file. NextAuth.js expects this file to export providers, session options, and callback functions. Each provider requires specific configuration — API keys for OAuth providers, database connections for credentials, etc.// lib/auth.js - NextAuth configuration for NewsWave
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import CredentialsProvider from 'next-auth/providers/credentials'
export const authOptions = {
providers: [
// Google OAuth for social login
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
}# .env.local - Authentication secrets for NewsWave
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here
# Google OAuth credentials
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secretTerminal
# Environment variables loaded
✓ NEXTAUTH_URL configured
✓ NEXTAUTH_SECRET set
# Ready for authentication setup
// app/api/auth/[...nextauth]/route.js - Authentication API routes
import NextAuth from 'next-auth'
import { authOptions } from '@/lib/auth'
// Handle all authentication requests
const handler = NextAuth(authOptions)
// Export for GET and POST requests
export { handler as GET, handler as POST }What just happened?
NextAuth.js configuration defines authentication providers, stores sensitive keys in environment variables, and creates API routes for handling login requests. The setup follows Next.js conventions — configuration files in lib/, environment variables in .env.local, and API routes in app/api/.
Session Management
Session management determines how your application tracks authenticated users across requests. NextAuth.js provides two session strategies — JWT tokens stored in cookies or database sessions with session IDs. JWT sessions store user information directly in signed cookies. When users log in, NextAuth.js creates a JWT containing user ID, email, and expiration time. This token travels with each request, and the server verifies the signature to confirm authenticity. JWTs work well for serverless applications since they require no database lookups. Database sessions store minimal session IDs in cookies while keeping user data on the server. This approach provides better security and allows immediate session invalidation. However, it requires database queries for each authenticated request and doesn't work well with serverless functions.// lib/auth.js - Session configuration for NewsWave
export const authOptions = {
providers: [/* providers here */],
// Use JWT sessions for serverless compatibility
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30 days
},
// Customize JWT token content
jwt: {
maxAge: 30 * 24 * 60 * 60, // 30 days
},
}// lib/auth.js - Custom session data for NewsWave users
export const authOptions = {
callbacks: {
// Add custom data to JWT tokens
async jwt({ token, user }) {
if (user) {
token.role = user.role || 'reader' // Default role for NewsWave users
token.subscription = user.subscription || 'free'
}
return token
},
// Shape session data for client components
async session({ session, token }) {
session.user.role = token.role
session.user.subscription = token.subscription
return session
},
},
}useSession hook returns current user information and loading states. The signIn and signOut functions handle authentication actions.
// components/UserProfile.js - Access session data in NewsWave
'use client'
import { useSession, signOut } from 'next-auth/react'
export default function UserProfile() {
const { data: session, status } = useSession()
if (status === 'loading') return <div>Loading...</div>
if (!session) return <div>Please sign in</div>
return (
<div>
<h3>Welcome, {session.user.name}</h3>
<p>Role: {session.user.role}</p>
<button onClick={() => signOut()}>Sign Out</button>
</div>
)
}localhost:3000 — NewsWave
What just happened?
Session management controls user state across requests. JWT sessions store user data in signed cookies, while callbacks customize available data. React hooks provide session access in components, handling loading states and authentication actions automatically.
Provider Configuration
Authentication providers define how users can log into your application. NextAuth.js supports dozens of built-in providers — from social logins like Google and GitHub to enterprise systems like Active Directory. Each provider requires specific configuration and credentials. OAuth providers like Google, GitHub, and Twitter redirect users to their authentication servers. Users grant permission, and the provider sends back an access token. NextAuth.js uses this token to fetch user information and create sessions. OAuth eliminates password storage and leverages trusted identity providers. Setting up OAuth requires registering your application with each provider. They provide client IDs and secrets that authenticate your app to their services. The provider configuration includes these credentials plus optional scope and parameter customizations.// lib/auth.js - Multiple OAuth providers for NewsWave
import GoogleProvider from 'next-auth/providers/google'
import GitHubProvider from 'next-auth/providers/github'
import TwitterProvider from 'next-auth/providers/twitter'
export const authOptions = {
providers: [
// Google OAuth - most popular social login
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
// GitHub OAuth - developer-friendly
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
],
}// lib/auth.js - Email/password authentication for NewsWave
import CredentialsProvider from 'next-auth/providers/credentials'
export const authOptions = {
providers: [
CredentialsProvider({
name: 'Email and Password',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' }
},
// Verify credentials against your database
async authorize(credentials) {
const user = await verifyCredentials(
credentials.email,
credentials.password
)
if (user) {
return {
id: user.id,
name: user.name,
email: user.email,
role: user.role
}
}
return null // Invalid credentials
}
}),
],
}signIn callback controls whether to allow login based on user data or external conditions. The redirect callback determines where users go after authentication.
// lib/auth.js - Custom authentication flow for NewsWave
export const authOptions = {
providers: [/* providers */],
callbacks: {
// Control who can sign in
async signIn({ user, account, profile }) {
// Block users without verified email
if (account.provider === 'google' && !profile.email_verified) {
return false
}
// Only allow specific domains for NewsWave staff
if (user.email.endsWith('@newswave.com')) {
user.role = 'editor'
}
return true // Allow sign in
},
// Customize redirect after authentication
async redirect({ url, baseUrl }) {
// Redirect to dashboard for authenticated users
return url.startsWith(baseUrl) ? url : `${baseUrl}/dashboard`
}
},
}Terminal
# Provider configuration loaded
✓ Google OAuth configured
✓ GitHub OAuth configured
✓ Credentials provider ready
# Multiple login options available
What just happened?
Authentication providers define login methods for your application. OAuth providers handle social login through trusted services, while credentials providers manage email/password authentication. Callbacks customize the authentication flow, controlling access and post-login redirects.
Login and Logout Implementation
Implementing login and logout functionality involves creating user interfaces and connecting them to NextAuth.js functions. The library providessignIn and signOut functions that handle the authentication flow automatically.
Login buttons trigger authentication flows based on the provider type. OAuth providers redirect to external services, while credentials providers submit forms to your API routes. NextAuth.js manages the complexity — CSRF protection, callback handling, and error management.
// components/LoginButton.js - Social login for NewsWave readers
'use client'
import { signIn, signOut, useSession } from 'next-auth/react'
export default function LoginButton() {
const { data: session } = useSession()
// Show logout if user is authenticated
if (session) {
return (
<div>
<span>Welcome, {session.user.name}!</span>
<button onClick={() => signOut()}>
Sign Out
</button>
</div>
)
}
// Show login options for anonymous users
return (
<div>
<button onClick={() => signIn('google')}>
Sign in with Google
</button>
<button onClick={() => signIn('github')}>
Sign in with GitHub
</button>
</div>
)
}localhost:3000 — NewsWave
// components/CustomLoginForm.js - Custom email/password form
'use client'
import { signIn } from 'next-auth/react'
import { useState } from 'react'
export default function CustomLoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
// Submit credentials to NextAuth
const result = await signIn('credentials', {
email,
password,
redirect: false // Handle result manually
})
if (result?.error) {
alert('Login failed: ' + result.error)
}
setLoading(false)
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Signing in...' : 'Sign In'}
</button>
</form>
)
}// app/layout.js - Wrap NewsWave with session provider
'use client'
import { SessionProvider } from 'next-auth/react'
export default function RootLayout({ children, session }) {
return (
<html lang="en">
<body>
{/* Make session available to all components */}
<SessionProvider session={session}>
<header>
<h1>NewsWave</h1>
<LoginButton />
</header>
<main>{children}</main>
</SessionProvider>
</body>
</html>
)
}Security Note
Never store passwords in plain text. Always hash passwords using bcrypt or similar libraries before storing in databases. NextAuth.js handles this automatically for credentials providers when you implement proper password verification.
Route Protection
Route protection prevents unauthorized users from accessing specific pages or API endpoints. Next.js provides multiple ways to implement protection — middleware for global checks, higher-order components for page-level protection, and manual checks within components. Middleware runs before every request, making it perfect for route protection. You can check authentication status and redirect unauthorized users before pages load. This approach works at the edge and provides fast, secure access control.// middleware.js - Protect NewsWave dashboard and admin pages
import { withAuth } from 'next-auth/middleware'
export default withAuth(
function middleware(req) {
// Additional logic can go here
console.log('Protected route accessed:', req.nextUrl.pathname)
},
{
callbacks: {
// Check if user can access the page
authorized: ({ token, req }) => {
const { pathname } = req.nextUrl
// Admin routes require admin role
if (pathname.startsWith('/admin')) {
return token?.role === 'admin'
}
// Dashboard requires any authenticated user
if (pathname.startsWith('/dashboard')) {
return !!token
}
return true // Allow public routes
},
},
}
)
// Specify which routes to protect
export const config = {
matcher: ['/dashboard/:path*', '/admin/:path*']
}Terminal
# Middleware active for route protection
✓ /dashboard/* routes protected
✓ /admin/* routes protected
# Unauthorized users redirected to login
getServerSession to check authentication status on the server.
// app/dashboard/page.js - Server-side protection for NewsWave dashboard
import { getServerSession } from 'next-auth/next'
import { authOptions } from '@/lib/auth'
import { redirect } from 'next/navigation'
export default async function Dashboard() {
// Check authentication on the server
const session = await getServerSession(authOptions)
// Redirect to login if not authenticated
if (!session) {
redirect('/api/auth/signin')
}
return (
<div>
<h1>NewsWave Dashboard</h1>
<p>Welcome back, {session.user.name}!</p>
{/* Dashboard content here */}
</div>
)
}// components/ProtectedPage.js - Client-side route protection
'use client'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function ProtectedPage({ children }) {
const { data: session, status } = useSession()
const router = useRouter()
useEffect(() => {
// Redirect unauthorized users
if (status === 'unauthenticated') {
router.push('/api/auth/signin')
}
}, [status, router])
// Show loading while checking authentication
if (status === 'loading') {
return <div>Checking authentication...</div>
}
// Show login prompt for unauthenticated users
if (!session) {
return <div>Please sign in to access this page</div>
}
// Render protected content
return children
}What just happened?
Route protection controls access to pages and API endpoints. Middleware provides edge-level protection, server-side checks handle SSR authentication, and client-side guards enable dynamic protection. Multiple approaches ensure comprehensive access control across your application.
Common Authentication Pitfalls
Avoid storing sensitive tokens in localStorage — use secure cookies. Don't implement custom password hashing — use established libraries. Never trust client-side authentication checks alone — always verify on the server. Remember that JWTs cannot be invalidated until they expire.
Quiz
1. NewsWave wants to implement stateless authentication for their API. How do JWT sessions work in NextAuth.js?
2. The NewsWave team needs to protect their /dashboard and /admin routes. Which file should contain the route protection logic that runs before every request?
3. NewsWave wants to restrict Google OAuth login to users with verified email addresses. What NextAuth.js callback should they customize?
Up Next: Authorization
Master role-based access control, permissions, and fine-grained authorization patterns to secure NewsWave features by user type and subscription level.