Skip to main content
PensionsPortal.ie uses Auth.js v5 (formerly NextAuth) with the Credentials provider. Users authenticate with email and bcrypt-hashed passwords stored in Neon PostgreSQL. Sessions are issued as signed JWTs, carrying role and tenancy claims.

Architecture

Configuration (src/lib/auth.ts)

The Auth.js configuration lives in src/lib/auth.ts and exports { handlers, auth, signIn, signOut }.

Session Strategy

JWT sessions are used. No database session table is required, reducing attack surface and eliminating session fixation risks tied to server-side session stores.

Custom JWT Claims

The jwt and session callbacks propagate three pension-specific fields from the user record into every token and session:

Password Hashing

Passwords are hashed with bcryptjs before storage. On authentication, bcrypt.compare() is used — timing-safe by design. Plaintext passwords are never logged, stored, or transmitted.

Login Page

Auth.js is configured to redirect unauthenticated requests to /auth/login:

Auth Flow

1

Credential submission

User submits email + password to POST /api/auth/signin (handled by Auth.js route handler at src/app/api/auth/[...nextauth]/route.ts).
2

Database lookup

Drizzle ORM queries the users table by email. If DATABASE_URL is not set, the request fails in production.
3

Password verification

bcrypt.compare(providedPassword, storedHash) — returns false if the hash does not match. Wrong passwords do not fall through to any fallback.
4

JWT issuance

On success, Auth.js issues a signed JWT containing id, role, brokerId, and employerId. The JWT secret is AUTH_SECRET (required environment variable).
5

Session cookie

The JWT is stored in an HttpOnly, Secure, SameSite=Lax cookie. It is not accessible to JavaScript.

Role Mapping

The database schema uses granular role names; Auth.js maps these to application-level UserRole values:

Development vs Production

Auth.js includes a dev-only credential fallback (hardcoded test users). This fallback is explicitly disabled in production via a NODE_ENV === "production" guard. Never deploy without DATABASE_URL set in production.
In production:
  • DATABASE_URL must be set — the DB fallback is the only auth path
  • AUTH_SECRET must be a cryptographically random string (minimum 32 bytes)
  • The dev credential fallback returns null immediately

Environment Variables

TypeScript Augmentation

Custom session fields are typed via module augmentation in src/lib/auth-types.ts, ensuring TypeScript catches any access to undefined session properties at compile time.