Skip to content

Comprehensive implementation of user authnetication with credentials, OAuth providers, 2FA and email verification using Auth.JS 5 and NextJS (with app directory).

Notifications You must be signed in to change notification settings

mbeps/nextjs-authjs

Repository files navigation

Next.js Authentication System with Auth.js v5

A comprehensive authentication system built with Next.js 15, NextAuth v5, Prisma, and PostgreSQL. This application demonstrates modern authentication patterns including OAuth integration, email verification, two-factor authentication, and role-based access control.

The application implements a complete authentication flow with support for both OAuth providers (Google and GitHub) and credential-based authentication. JWT-based sessions provide stateless authentication whilst Prisma with PostgreSQL ensures persistent storage for users, tokens, and account data. Email verification, password reset, and two-factor authentication are handled through secure token-based flows using Resend for email delivery.

Features

OAuth 2.0 Authentication

The application provides complete OAuth authentication functionality:

  • Google OAuth 2.0 integration with NextAuth
  • GitHub OAuth 2.0 integration with NextAuth
  • Automatic account linking for OAuth providers
  • Automatic email verification for OAuth users
  • Separate user flow for OAuth vs. credentials authentication

Credentials Authentication

Email and password authentication with security features:

  • Email and password registration with validation
  • Password hashing with bcryptjs (10 salt rounds)
  • Email verification required before first login
  • Account validation against database
  • Prevention of OAuth account conflicts

Email Verification

Email verification system for new accounts:

  • Verification email sent on registration via Resend
  • Single-use verification tokens (UUID v4)
  • Token expiration (1 hour)
  • Automatic verification for OAuth users
  • Re-send verification option during login
  • Token validation on protected verification page

Password Reset

Secure password reset functionality:

  • Password reset request via email form
  • Single-use reset tokens sent via email
  • Token expiration (1 hour)
  • New password validation (minimum 8 characters)
  • Automatic token deletion after successful reset
  • Current password verification for password changes

Two-Factor Authentication (2FA)

Optional email-based two-factor authentication:

  • 6-digit random code generation
  • Code delivery via email
  • Token expiration (15 minutes)
  • Per-user 2FA toggle in settings
  • Enforced during credential login flow
  • Not available for OAuth users

Role-Based Access Control (RBAC)

User role management and enforcement:

  • Two roles: ADMIN and USER (default)
  • Server-side role validation via server actions
  • Client-side role-based UI hiding with RoleGate component
  • API route protection with role checks
  • Admin-only endpoints returning 403 for non-admins
  • Role changes via settings page

Session Management

Secure session handling:

  • JWT-based sessions for stateless authentication
  • Session token stored in httpOnly cookies
  • Automatic session refresh on data changes
  • Extended session data (role, 2FA status, OAuth flag)
  • Callback URL preservation for redirect after login
  • Logout with session cleanup

Protected Routes

Route protection with middleware:

  • Authentication-required routes under (protected) group
  • Automatic redirect to login for unauthenticated users
  • Callback URL preservation in redirect
  • Prevention of authenticated users accessing auth pages
  • Public routes accessible without authentication
  • Middleware running on all routes except static assets

User Profile Management

Profile settings and updates:

  • Name, email, and password updates
  • Email change with verification flow
  • Current password validation before password change
  • 2FA toggle in settings
  • Role selection (for demonstration)
  • OAuth user restrictions (cannot change email/password/2FA)

Token Management

Secure token generation and validation:

  • Verification tokens (UUID v4, 1-hour expiry)
  • Password reset tokens (UUID v4, 1-hour expiry)
  • Two-factor tokens (6-digit number, 15-minute expiry)
  • Automatic deletion of expired tokens
  • Single-use token enforcement
  • Token validation before operations

Requirements

These are the requirements needed to run the project:

  • Node.js 20 or higher
  • PostgreSQL database (local or cloud-hosted like Neon)
  • Google OAuth Application credentials (Client ID and Client Secret)
  • GitHub OAuth Application credentials (Client ID and Client Secret)
  • Resend API key for email delivery

Stack

These are the main technologies used in this project:

Language

  • TypeScript: Strongly typed programming language building on JavaScript.

Front-End

  • Next.js: A React framework with server-side rendering, App Router, and Turbopack support.
  • React.js: A JavaScript library for building user interfaces with components.
  • Tailwind CSS: A utility-first CSS framework for rapid UI development.
  • shadcn/ui: Beautifully designed components built with Radix UI and Tailwind CSS.
  • React Hook Form: Performant, flexible forms with easy-to-use validation.
  • Zod: TypeScript-first schema declaration and validation library.
  • Lucide React: Beautiful & consistent icon toolkit.
  • Sonner: An opinionated toast component for React.

Back-End

  • Auth.js (NextAuth.js): Complete authentication solution for Next.js applications with OAuth and credentials support.
  • PostgreSQL: Advanced open-source relational database with strong data integrity.
  • Prisma: Next-generation ORM for Node.js and TypeScript with type safety.
  • bcryptjs: Library for hashing and comparing passwords securely.
  • Resend: Modern email API for developers with high deliverability.

Design

Authentication Strategy

The application uses NextAuth v5 with a JWT session strategy rather than database sessions. This approach enables edge runtime compatibility and reduces database queries. Session tokens are stored in httpOnly cookies to prevent XSS attacks. The JWT contains extended user information including role, two-factor status, and OAuth flag, which is synchronized with the database on every request through the jwt callback.

Database Architecture

PostgreSQL stores six main collections via Prisma:

  • User: Stores user accounts with email, password hash, role, and 2FA preferences
  • Account: Stores OAuth provider data (Google, GitHub) linked to users
  • VerificationToken: Stores email verification tokens with 1-hour expiry
  • PasswordResetToken: Stores password reset tokens with 1-hour expiry
  • TwoFactorToken: Stores 2FA codes with 15-minute expiry
  • TwoFactorConfirmation: Stores 2FA confirmation state per user

All token models use unique constraints on email and token to prevent duplicates. Previous tokens are automatically deleted when generating new ones for the same email.

Database Schema

                     +-----------------------+
                     |         User          |
                     |-----------------------|
                     | id (PK)               |
                     | email (unique)        |
                     | role                  |
                     | password (nullable)   |
                     | isTwoFactorEnabled    |
                     +-----------+-----------+
                                 |
                                 | 1 ────────* accounts
                                 v
                      +----------+-----------+
                      |        Account       |
                      |----------------------|
                      | id (PK)              |
                      | userId (FK → User)   |
                      | provider             |
                      | providerAccountId    |
                      | access/refresh tokens|
                      +----------------------+
                                 ^
                                 | 1 ──────── 0..1 confirmation
                     +-----------+-----------+
                     | TwoFactorConfirmation |
                     |-----------------------|
                     | id (PK)               |
                     | userId (unique FK)    |
                     +-----------------------+

      +-----------------------+  +-----------------------+  +---------------------+
      |   VerificationToken   |  |  PasswordResetToken   |  |    TwoFactorToken   |
      |-----------------------|  |-----------------------|  |---------------------|
      | id (PK)               |  | id (PK)               |  | id (PK)             |
      | email (unique + token)|  | email (unique + token)|  | email (unique token)|
      | token (unique)        |  | token (unique)        |  | token (unique)      |
      | expires               |  | expires               |  | expires             |
      +-----------------------+  +-----------------------+  +---------------------+

The User table sits at the center of the schema with a one-to-many relationship to Account for OAuth providers and a one-to-one relationship with TwoFactorConfirmation to mark successful 2FA challenges. The token tables (VerificationToken, PasswordResetToken, TwoFactorToken) are intentionally decoupled from foreign keys and instead use unique (email, token) pairs so that tokens can be issued before an account exists and can be rotated without referential constraints. Cascading deletes on the Account and TwoFactorConfirmation relations ensure that removing a user automatically cleans up linked records.

Token Structure

Verification and password reset tokens use UUID v4 for cryptographic randomness. Two-factor tokens use 6-digit random numbers (100,000 to 999,999) for user convenience. All tokens include expiration timestamps validated server-side before operations. Tokens are single-use and deleted immediately after successful validation.

Route Protection Strategy

Middleware intercepts all requests to enforce authentication rules. Unauthenticated users accessing protected routes are redirected to login with the original URL preserved as a callback parameter. Authenticated users cannot access authentication pages and are redirected to the settings page. Public routes and NextAuth API endpoints bypass all protection checks.

Authentication Flow

  1. User initiates login (credentials or OAuth)
  2. For credentials: validates email verification and 2FA if enabled
  3. NextAuth processes authentication via signIn callback
  4. JWT token generated with user claims (ID, role, 2FA status, OAuth flag)
  5. Session cookie set as httpOnly with Secure flag
  6. User redirected to callback URL or default protected page
  7. Subsequent requests include session cookie automatically
  8. Middleware validates authentication on protected routes

Email Verification Flow

  1. User registers with email and password
  2. Server generates verification token (UUID, 1-hour expiry)
  3. Verification email sent via Resend with token link
  4. User clicks link to /auth/new-verification?token=xxx
  5. Server validates token exists and not expired
  6. User's emailVerified timestamp updated in database
  7. Token deleted from database
  8. User redirected to login

Password Reset Flow

  1. User requests password reset via email form
  2. Server generates reset token (UUID, 1-hour expiry)
  3. Reset email sent via Resend with token link
  4. User clicks link to /auth/new-password?token=xxx
  5. User enters new password
  6. Server validates token and password strength
  7. Password hashed with bcryptjs and updated in database
  8. Token deleted from database
  9. User redirected to login

Two-Factor Authentication Flow

  1. User enables 2FA in settings page
  2. On next credential login, server detects 2FA enabled
  3. Server generates 6-digit code (15-minute expiry)
  4. Code sent via email
  5. Login form shows 2FA code input
  6. User enters code from email
  7. Server validates code against database
  8. TwoFactorConfirmation record created
  9. Session created and user logged in
  10. TwoFactorConfirmation deleted after successful login

Logout Flow

  1. User clicks logout button
  2. Client calls logout server action
  3. Server calls NextAuth signOut()
  4. Session cookie deleted
  5. User redirected to home page

Setting Up Project

These are simple steps to run the application locally.

1. Clone the Project Locally

git clone https://github.com/mbeps/oauth-nextjs-springboot-backend.git
cd oauth-nextjs-springboot-backend

2. Install Dependencies

yarn install

3. Set Up PostgreSQL Database

Ensure PostgreSQL is running locally or create a serverless PostgreSQL database on Neon. Note your database connection string for the next step.

4. Create OAuth Applications

Google OAuth Application

Create a Google OAuth application with the following settings:

  • Authorized JavaScript origins: http://localhost:3000
  • Authorized redirect URIs: http://localhost:3000/api/auth/callback/google

Note your Client ID and Client Secret.

For Production: Update the origins and redirect URIs to your production domain (e.g., https://yourdomain.com instead of http://localhost:3000).

GitHub OAuth Application

Create a GitHub OAuth application with the following settings:

  • Homepage URL: http://localhost:3000
  • Authorization callback URL: http://localhost:3000/api/auth/callback/github

Note your Client ID and Client Secret.

For Production: Update the Homepage URL and Authorization callback URL to your production domain (e.g., https://yourdomain.com instead of http://localhost:3000).

5. Create Resend Account

Sign up for a Resend account and obtain your API key from the dashboard.

For Production: Verify your domain in Resend to send emails from your own domain instead of onboarding@resend.dev.

6. Configure Environment Variables

Create a .env file in the project root:

# Database
DATABASE_URL="postgresql://user:password@localhost:5432/auth_db?schema=public"

# NextAuth
AUTH_SECRET="your-auth-secret-here"  # Generate with: openssl rand -base64 32
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_TRUST_HOST="true"

# GitHub OAuth
GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"

# Google OAuth
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"

# Resend
RESEND_API_KEY="your-resend-api-key"

# Public App URL for email links
NEXT_PUBLIC_APP_URL="http://localhost:3000"

Environment Variable Descriptions:

DATABASE_URL:

  • PostgreSQL connection string for Prisma
  • Format: postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA
  • Example local: postgresql://postgres:password@localhost:5432/auth_db
  • Example Neon: postgresql://user:pass@ep-xxx.region.aws.neon.tech/dbname?sslmode=require

AUTH_SECRET:

  • Secret key for signing and encrypting JWT tokens
  • Generate with: openssl rand -base64 32
  • Keep this secret and never commit to version control

NEXTAUTH_URL:

  • The canonical URL of your site for OAuth callbacks
  • Local development: http://localhost:3000
  • Production: https://yourdomain.com

GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET:

  • Credentials from GitHub OAuth application
  • Obtained from GitHub Developer Settings > OAuth Apps

GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET:

  • Credentials from Google Cloud Console OAuth application
  • Obtained from Google Cloud Console > Credentials

RESEND_API_KEY:

  • API key for Resend email service
  • Obtained from Resend dashboard

NEXT_PUBLIC_APP_URL:

  • Public URL used in email links for verification and reset
  • Must match your actual site URL
  • Local: http://localhost:3000
  • Production: https://yourdomain.com

For Production:

  • Set NEXTAUTH_URL to your production domain
  • Set NEXT_PUBLIC_APP_URL to your production domain
  • Use a strong, randomly generated AUTH_SECRET
  • Configure PostgreSQL with SSL/TLS
  • Update OAuth callback URLs to production domain

7. Set Up Database Schema

npm run prisma-push

This will push the Prisma schema to your database and generate the Prisma Client.

8. Run the Application

npm run dev

The application should now be running on http://localhost:3000

Usage

Registration Flow

  1. Navigate to /auth/register
  2. Enter name, email, and password (minimum 8 characters)
  3. Click "Create an account"
  4. Verification email sent to provided email address
  5. Click verification link in email
  6. Redirected to login page
  7. Login with credentials

Credential Login

Standard Login:

1. Navigate to /auth/login
2. Enter email and password
3. Click "Sign In"
4. Redirected to callback URL or /settings

Login with 2FA Enabled:

1. Navigate to /auth/login
2. Enter email and password
3. Click "Sign In"
4. 2FA code input appears
5. Check email for 6-digit code
6. Enter code and submit
7. Redirected to callback URL or /settings

Login with Unverified Email:

1. Navigate to /auth/login
2. Enter email and password
3. Click "Sign In"
4. New verification email sent
5. Click verification link in email
6. Return to login and try again

OAuth Login

Google Login:

1. Navigate to /auth/login
2. Click "Continue with Google" button
3. Authenticate with Google
4. Redirected back to application
5. Logged in and redirected to /settings

GitHub Login:

1. Navigate to /auth/login
2. Click "Continue with GitHub" button
3. Authenticate with GitHub
4. Redirected back to application
5. Logged in and redirected to /settings

Password Reset

1. Navigate to /auth/reset
2. Enter email address
3. Click "Send reset email"
4. Check email for reset link
5. Click link to /auth/new-password?token=xxx
6. Enter new password (minimum 8 characters)
7. Click "Reset password"
8. Redirected to login page
9. Login with new password

Enabling Two-Factor Authentication

1. Login to application
2. Navigate to /settings
3. Find "Two Factor Authentication" section
4. Toggle switch to ON
5. Save changes
6. Logout and login again
7. Enter 6-digit code from email
8. Successfully logged in with 2FA

Changing Email

1. Login to application (credentials only, not OAuth)
2. Navigate to /settings
3. Enter new email address
4. Click "Save"
5. Verification email sent to new email address
6. Click verification link in email
7. Email updated in profile

Changing Password

1. Login to application (credentials only, not OAuth)
2. Navigate to /settings
3. Enter current password
4. Enter new password (minimum 8 characters)
5. Click "Save"
6. Password updated successfully

Accessing Protected Routes

Protected routes require authentication:

Settings Page:

Navigate to /settings
- Redirected to login if not authenticated
- Access granted if authenticated

Admin Page:

Navigate to /admin
- Redirected to login if not authenticated
- Access granted if authenticated
- Admin-only content hidden for non-admin users via RoleGate

Server Component Demo:

Navigate to /server
- Demonstrates server-side authentication
- Shows user information from server session

Client Component Demo:

Navigate to /client
- Demonstrates client-side authentication
- Shows user information from client session

Using Admin API Route

Admin-only API endpoint with role validation:

GET /api/admin
Authorization: Session Cookie (automatic)

Response if Admin:

{
  "message": "Access granted"
}

Response if Not Admin:

403 Forbidden

Checking Authentication Status

All protected pages automatically check authentication via middleware. Use the currentUser() server function in server components or useCurrentUser() hook in client components to access user data.

Logging Out

1. Click user avatar in top right
2. Click "Logout" from dropdown menu
3. Session cleared
4. Redirected to home page

References

About

Comprehensive implementation of user authnetication with credentials, OAuth providers, 2FA and email verification using Auth.JS 5 and NextJS (with app directory).

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published