Skip to main content
VulnTrack uses NextAuth.js v4 for authentication, with support for credential-based login and JWT session management.

Configuration Overview

Authentication is configured in src/lib/auth.ts with the following components:
  • Adapter: Prisma Adapter for database session storage
  • Session Strategy: JWT (JSON Web Tokens)
  • Provider: Credentials provider with bcrypt password hashing
  • Custom Pages: Branded login and onboarding flows

Authentication Flow

1

User submits credentials

Email and password are sent to the credentials provider via the login form.
2

Database lookup

NextAuth queries the User table in PostgreSQL to find a matching email.
3

Password verification

Submitted password is compared against the stored bcrypt hash using bcryptjs.compare().
4

JWT creation

On success, a JWT token is created containing:
  • User ID
  • User role (ADMIN, ANALYST, VIEWER)
  • Onboarding status
5

Session callback

The JWT is decoded and user data is attached to the session object for use in the application.

Environment Variables

Required configuration for NextAuth:
NEXTAUTH_URL
string
required
Canonical URL of your deployment.Development: http://localhost:3000Production: https://vulntrack.yourdomain.com
NEXTAUTH_SECRET
string
required
Secret key for signing and encrypting JWT tokens.Generate with: openssl rand -base64 32
This secret must be kept secure. Changing it will invalidate all active sessions.

Session Configuration

VulnTrack uses JWT-based sessions for stateless authentication:
src/lib/auth.ts
session: {
  strategy: "jwt",
}
Benefits:
  • No database queries for session validation
  • Horizontal scaling without session stores
  • Reduced database load
Session Data:
  • user.id - Unique user identifier
  • user.email - User email address
  • user.name - Display name
  • user.role - Access control role (ADMIN, ANALYST, VIEWER)
  • user.isOnboarded - Onboarding completion status

Credentials Provider

VulnTrack implements a custom credentials provider for email/password authentication:

Password Hashing

Passwords are hashed using bcrypt with automatic salt generation:
import { compare } from "bcryptjs"

const isPasswordValid = await compare(credentials.password, user.password)
Bcrypt automatically handles salt generation and storage within the hash string.

Authorization Logic

The authorize() function in src/lib/auth.ts:23 handles:
  1. Validation - Checks that email and password are provided
  2. User Lookup - Queries database for user by email
  3. Password Check - Verifies password against stored hash
  4. Return User Object - Returns user data for JWT encoding

Security Features

  • Constant-time password comparison (bcrypt)
  • No password returned in session data
  • Failed login attempts return generic error messages (prevents email enumeration)

Custom Pages

VulnTrack overrides default NextAuth pages:
signIn
Custom branded login page with VulnTrack styling.
newUser
Post-registration onboarding flow (Note: primarily for OAuth flows).

JWT Callbacks

VulnTrack extends JWT tokens with custom claims:

JWT Callback (src/lib/auth.ts:70)

Executed when a token is created or updated:
async jwt({ token, user }) {
  if (user) {
    token.id = user.id
    token.role = user.role
    token.isOnboarded = user.isOnboarded
  }
  
  // Refresh user data from database
  if (token.id) {
    const freshUser = await prisma.user.findUnique({
      where: { id: token.id },
      select: { role: true, isOnboarded: true }
    })
    if (freshUser) {
      token.role = freshUser.role
      token.isOnboarded = freshUser.isOnboarded
    }
  }
  return token
}
Features:
  • Fetches fresh user data on every token use
  • Ensures role changes are reflected immediately
  • Updates onboarding status dynamically

Session Callback (src/lib/auth.ts:59)

Attaches user data to the session object:
async session({ session, token }) {
  return {
    ...session,
    user: {
      ...session.user,
      id: token.id,
      role: token.role,
      isOnboarded: token.isOnboarded,
    }
  }
}

Role-Based Access Control

VulnTrack implements three user roles:
ADMIN
role
Full system access:
  • Create and manage teams
  • Send invitations
  • Manage all vulnerabilities
  • Access audit logs
  • Configure system settings
ANALYST
role
Operational access:
  • Create and edit vulnerabilities
  • Assign vulnerabilities
  • Add comments and assessments
  • Generate reports
VIEWER
role
Read-only access:
  • View vulnerabilities
  • Read comments
  • Access reports
  • Cannot modify data

Protecting Routes

Server Components

Use getServerSession to protect server components:
import { getServerSession } from "next-auth"
import { authOptions } from "@/lib/auth"
import { redirect } from "next/navigation"

export default async function ProtectedPage() {
  const session = await getServerSession(authOptions)
  
  if (!session) {
    redirect("/login")
  }
  
  // Check role
  if (session.user.role !== "ADMIN") {
    return <div>Access Denied</div>
  }
  
  return <div>Admin Content</div>
}

Server Actions

Protect server actions with session checks:
import { getServerSession } from "next-auth"
import { authOptions } from "@/lib/auth"

export async function createVulnerability(data: FormData) {
  const session = await getServerSession(authOptions)
  
  if (!session) {
    throw new Error("Unauthorized")
  }
  
  if (!["ADMIN", "ANALYST"].includes(session.user.role)) {
    throw new Error("Insufficient permissions")
  }
  
  // Proceed with action
}

Client Components

Use useSession hook for client-side checks:
import { useSession } from "next-auth/react"

export function ClientComponent() {
  const { data: session, status } = useSession()
  
  if (status === "loading") {
    return <div>Loading...</div>
  }
  
  if (status === "unauthenticated") {
    return <div>Please log in</div>
  }
  
  return <div>Welcome, {session.user.name}</div>
}

Invitation System

VulnTrack uses an invitation-only registration system:
1

Admin creates invitation

Admin generates a unique invitation token with expiration (typically 24 hours).
2

Invitation email sent

User receives email with invitation link containing the token.
3

User completes registration

User creates account using the invitation link. Token is validated and consumed.
4

Role assignment

User is automatically assigned the role specified in the invitation.
See Email Configuration for invitation email setup.

Security Best Practices

Secure secrets

Generate strong NEXTAUTH_SECRET values and never commit them to version control.

HTTPS in production

Always use HTTPS in production. NextAuth will reject non-HTTPS URLs.

Rate limiting

Implement rate limiting on login endpoints to prevent brute force attacks.

Password policy

Enforce minimum password requirements during registration (length, complexity).
Security Note: VulnTrack stores password hashes using bcrypt, but you should still implement additional security measures like rate limiting, account lockouts, and 2FA for high-security deployments.

Build docs developers (and LLMs) love