TalentSync

Home Projects TalentSync Frontend Application Authentication System

Authentication System

Purpose and Scope

This document covers the comprehensive authentication system implemented in TalentSync using NextAuth. The system provides multiple authentication methods (email/password, Google OAuth, GitHub OAuth), email verification, password reset functionality, role-based access control, and middleware-enforced route protection.

For database schema details related to user authentication, see Database & Data Models. For role selection UI and middleware implementation specifically, see subsections Role Selection & Management and Middleware & Route Protection.


System Architecture

The authentication system is built on NextAuth with custom extensions for email verification, role management, and OAuth integration. It consists of frontend UI components, API routes, middleware for route protection, and database-backed token management.

Authentication Component Architecture

Architecture Diagram

Sources: frontend/app/auth/page.tsx1-928 frontend/middleware.ts1-75 frontend/app/api/auth/register/route.ts1-176


Authentication Providers

The system supports three authentication providers configured in NextAuth.

Provider Configuration

Provider Implementation User Data Handling
Credentials Email/password with bcrypt hashing Creates user with passwordHash field
Google OAuth NextAuth GoogleProvider Auto-creates user without passwordHash
GitHub OAuth NextAuth GithubProvider Auto-creates user without passwordHash

Credentials Provider Flow

Architecture Diagram

Sources: frontend/app/auth/page.tsx151-186 frontend/lib/auth-options.ts (referenced but not provided)

OAuth Provider Flow

OAuth providers (Google, GitHub) bypass password verification and email verification requirements. The isVerified field is automatically set to true for OAuth users.

Implementation:

Sources: frontend/app/auth/page.tsx145-149 frontend/app/auth/page.tsx424-467


Registration & Email Verification

Registration Flow

The registration process creates a user account with an unverified status and sends a verification email containing a unique token.

Registration State Machine

Architecture Diagram

Sources: frontend/app/auth/page.tsx188-274 frontend/app/api/auth/register/route.ts68-176

Email Verification Token System

The system uses cryptographically secure tokens stored in the EmailVerificationToken table.

Token Generation: frontend/app/api/auth/register/route.ts103-104

const verificationToken = randomBytes(32).toString("hex");
const tokenExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours

Token Storage: frontend/app/api/auth/register/route.ts132-138

  • Creates EmailVerificationToken record linked to userId
  • Token expires after 24 hours

Email Template: frontend/app/api/auth/register/route.ts35-66

  • Sends HTML email via Nodemailer
  • Contains verification URL: ${NEXTAUTH_URL}/auth/verify-email?token=${token}
  • Styled with inline CSS for email client compatibility

Email Verification Process

Architecture Diagram

Sources: frontend/app/auth/verify-email/page.tsx18-222 frontend/app/api/auth/verify-email/route.ts (referenced but not provided)

Resend Verification Email

Users can request a new verification email if the original expires or is lost.

UI Implementation: frontend/app/auth/resend-verification/page.tsx20-293

API Endpoint: frontend/app/api/auth/resend-verification/route.ts (referenced but not provided)

  • Deletes old verification tokens for the user
  • Generates new token with fresh 24-hour expiry
  • Sends new verification email

Sources: frontend/app/auth/resend-verification/page.tsx1-293 frontend/app/auth/page.tsx128-143


Login Flow

Credentials Login Process

The credentials login validates email/password and enforces email verification before granting access.

Login Handler: frontend/app/auth/page.tsx151-186

Architecture Diagram

Email Verification Error Handling: frontend/app/auth/page.tsx163-177

  • Displays inline error message
  • Provides clickable link to resend verification email
  • Pre-fills email in resend form

Sources: frontend/app/auth/page.tsx151-186

OAuth Login Process

OAuth logins bypass email verification and password checks entirely.

Flow:

  1. User clicks "Continue with Google/GitHub"
  2. Calls signIn(provider, {callbackUrl: "/dashboard"}) at frontend/app/auth/page.tsx145-149
  3. NextAuth redirects to OAuth provider
  4. Provider authenticates user and returns profile
  5. NextAuth creates or updates user record with isVerified: true
  6. Session created, user redirected to /dashboard or /select-role if no role assigned

OAuth User Characteristics:

  • passwordHash is null (cannot use password reset)
  • isVerified is true by default
  • image field populated from OAuth provider avatar

Sources: frontend/app/auth/page.tsx145-149 frontend/app/auth/page.tsx424-467


Password Reset Flow

Forgot Password Request

Users without OAuth accounts can request a password reset via email.

Password Reset Request Flow

Architecture Diagram

Sources: frontend/app/auth/forgot-password/page.tsx1-237 frontend/app/api/auth/reset-password/route.ts59-134

Password Reset Token Management

Token Generation: frontend/app/api/auth/reset-password/route.ts91-92

const token = randomBytes(32).toString("hex");
const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour

Token Security:

Email Template: frontend/app/api/auth/reset-password/route.ts25-57

  • Reset URL: ${NEXTAUTH_URL}/auth/reset-password?token=${token}
  • Clear expiration notice: "This link will expire in 1 hour"

Password Reset Confirmation

Architecture Diagram

Sources: frontend/app/auth/reset-password/page.tsx27-324 frontend/app/api/auth/confirm-reset/route.ts11-88

Key Security Features:


Role Selection & Management

Role Assignment Flow

Users authenticating via OAuth do not select a role during registration. They must select a role before accessing the dashboard.

Role Selection State Machine

Architecture Diagram

Sources: frontend/app/select-role/page.tsx1-156 frontend/app/api/auth/update-role/route.ts1-65

Update Role API

Endpoint: /api/auth/update-role

Role Mapping: frontend/app/api/auth/update-role/route.ts26-31

// Frontend sends "user" or "admin"
// Backend maps to database Role.name: "User" or "Admin"
const roleName = roleId === "user" ? "User" : "Admin";

Database Update: frontend/app/api/auth/update-role/route.ts41-45

  • Updates User.roleId field
  • Includes role relation in response

Session Refresh: frontend/app/select-role/page.tsx52-56

const updated = await update(); // Refresh NextAuth session
if (updated?.user && (updated.user as any).role) {
  router.replace("/dashboard");
}

Sources: frontend/app/select-role/page.tsx33-67 frontend/app/api/auth/update-role/route.ts6-64


Middleware & Route Protection

The authentication middleware enforces access control on all routes, ensuring users are authenticated and have assigned roles before accessing protected pages.

Middleware Logic Flow

Architecture Diagram

Sources: frontend/middleware.ts1-75

Middleware Implementation Details

Public Paths: frontend/middleware.ts42-55

  • Landing page: /
  • About page: /about
  • Auth pages: /auth, /auth/verify-email, /auth/resend-verification, /auth/forgot-password, /auth/reset-password
  • API routes: /api/*
  • Next.js internals: /_next/*
  • Static assets: *.{css,js,png,jpg,...}
  • PostHog analytics proxy: /ph/*

Role Enforcement Logic: frontend/middleware.ts14-26

// If authenticated but no role, redirect to role selection
if (token && !token.role && pathname !== "/select-role" && ...) {
  return NextResponse.redirect(new URL("/select-role", req.url));
}

// If has role but on select-role page, redirect to dashboard
if (token && token.role && pathname === "/select-role") {
  return NextResponse.redirect(new URL("/dashboard", req.url));
}

Authorization Callback: frontend/middleware.ts36-64

  • Returns true for public paths
  • Returns true for /select-role if token exists (even without role)
  • Returns true for protected pages only if token exists

Matcher Configuration: frontend/middleware.ts69-74

matcher: ["/((?!api/auth|_next/static|_next/image|favicon.ico|ph).*)"]

Excludes NextAuth internal routes, static assets, and PostHog proxy from middleware processing.

Sources: frontend/middleware.ts1-75


Session Management

NextAuth Session Structure

NextAuth sessions are stored in the database and managed server-side with JWT tokens for client access.

Session Token Contents:

  • user.id: User database ID
  • user.email: User email address
  • user.name: User display name
  • user.image: Avatar URL (from OAuth or registration)
  • user.role: Role name ("User" or "Admin") - custom field

Session Retrieval:

Client-side: frontend/app/auth/page.tsx40

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

Server-side (API routes): frontend/app/api/auth/update-role/route.ts8

const session = await getServerSession(authOptions);

Session Update: After role assignment, session must be refreshed: frontend/app/select-role/page.tsx52-53

const updated = await update(); // Force session refresh

Token Structure & Callbacks

The JWT token is customized via callbacks in authOptions to include role information.

Expected Callback Structure (in authOptions):

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.role = user.role?.name; // Add role to token
    }
    return token;
  },
  async session({ session, token }) {
    if (session.user) {
      session.user.role = token.role; // Add role to session
    }
    return session;
  }
}

Sources: frontend/app/api/auth/[...nextauth]/route.ts:1-7, frontend/lib/auth-options.ts (referenced but not provided), frontend/app/auth/page.tsx40 frontend/app/api/auth/update-role/route.ts8


Authentication Data Models

User Model Fields

Field Type Purpose Set By
id String Primary key (UUID) Auto-generated
email String Unique email address User input / OAuth
name String Display name User input / OAuth
passwordHash String? Bcrypt hashed password Registration (null for OAuth)
image String? Avatar URL User input / OAuth provider
isVerified Boolean Email verification status false on registration, true after verification or OAuth
roleId String? Foreign key to Role Selected during registration or after OAuth
createdAt DateTime Account creation timestamp Auto-generated

Email Verification Token Model

Field Type Purpose
id String Primary key (UUID)
token String Unique 64-char hex token
userId String Foreign key to User
expiresAt DateTime Token expiration (24 hours from creation)
createdAt DateTime Token creation timestamp

Password Reset Token Model

Field Type Purpose
id String Primary key (UUID)
token String Unique 64-char hex token
userId String Foreign key to User
expiresAt DateTime Token expiration (1 hour from creation)
usedAt DateTime? Timestamp when token was used (null if unused)
createdAt DateTime Token creation timestamp

Role Model

Field Type Values
id String Primary key (UUID)
name String "User" or "Admin"

Role Semantics:

  • "User" (role ID "user" in frontend): Job seeker - access to resume analysis, cold mail, hiring assistant, LinkedIn posts
  • "Admin" (role ID "admin" in frontend): Recruiter - access to candidate dashboard, bulk processing, ATS evaluation

Sources: frontend/app/api/auth/register/route.ts109-129 frontend/app/api/auth/update-role/route.ts26-38 frontend/prisma/schema.prisma (referenced but not provided)


Error Handling & User Feedback

Error Display Patterns

All authentication pages use consistent error handling with inline error messages and helpful recovery actions.

Error Display Component: frontend/app/auth/page.tsx393-409

  • Red background for errors (bg-red-500/10)
  • Green background for success (bg-green-500/10)
  • AlertCircle icon for visual feedback
  • Actionable links embedded in error messages

Common Error Scenarios:

Scenario Error Message Recovery Action
Unverified email on login "Please verify your email before signing in." Link to /auth/resend-verification?email=...
Invalid credentials "Invalid email or password" User can retry or use forgot password
Password reset for OAuth user "This account was created with OAuth..." User must sign in with OAuth provider
Expired verification token "Invalid or expired reset token" Link to request new token
Password mismatch "Passwords do not match" Client-side validation, immediate feedback
Weak password "Password must be at least 6 characters" Client-side validation
Missing role "Please select a role" Inline form validation

Loading States

All authentication operations display full-screen loading overlays during async operations.

Loading Overlay Implementation: frontend/app/auth/page.tsx311-342

  • Backdrop blur effect
  • Animated pulse loader
  • Status message ("Processing Authentication...", "Sending Verification...", etc.)
  • Three animated dots for visual feedback

Specific Loading Messages:

  • Registration: "Creating Account..."
  • Login: "Signing in..."
  • Email verification: "Verifying your email, please wait..."
  • Password reset request: "Sending Reset Link"
  • Password reset confirmation: "Resetting Password"

Sources: frontend/app/auth/page.tsx311-342 frontend/app/auth/verify-email/page.tsx79-93 frontend/app/auth/resend-verification/page.tsx83-127


Security Considerations

Password Security

Token Security

Email Verification Tokens:

Password Reset Tokens:

Email Security

Anti-Enumeration: Password reset returns generic success message regardless of whether user exists: frontend/app/api/auth/reset-password/route.ts70-75

// Don't reveal whether the email exists or not for security
return NextResponse.json(
  { message: "If an account with this email exists, a password reset link has been sent." }
);

Session Security

  • Server-side session storage in database
  • JWT tokens with short expiration
  • HTTPOnly cookies (NextAuth default)
  • CSRF protection via NextAuth

Sources: frontend/app/api/auth/register/route.ts100-104 frontend/app/api/auth/reset-password/route.ts86-92 frontend/app/api/auth/confirm-reset/route.ts43-63