Scrum4Me/docs/adr/0005-iron-session-over-nextauth.md

2.8 KiB

status date decision-makers
accepted 2026-05-03
janpetervisser

ADR-0005: Use iron-session for authentication instead of NextAuth/Clerk/Supabase Auth

Context and Problem Statement

Scrum4Me requires username/password login without email verification, a synchronous demo-user check on every request, and full control over the session cookie shape (including an isDemo flag). Which authentication solution fits these constraints at minimal complexity?

Decision Drivers

  • No email required — username/password only.
  • Demo-user policy (ADR-0006) requires a synchronous isDemo check in both middleware and server actions.
  • No third-party redirect chain — auth must stay in-process.
  • Solo-developer project: minimal external dependencies preferred.

Considered Options

  • NextAuth / Auth.js v5
  • Clerk
  • Supabase Auth
  • iron-session + bcryptjs

Decision Outcome

Chosen option: iron-session + bcryptjs, because it is the only option that gives us full control over cookie contents, has zero external redirect dependency, and lets us embed isDemo directly in the session payload.

Consequences

  • Good, because session structure is fully controlled — we add any field we need.
  • Good, because no external service dependency for auth; works offline and in CI.
  • Good, because synchronous cookie read in proxy.ts middleware is trivial.
  • Bad, because we own the password hashing, session rotation, and CSRF protection.
  • Bad, because no OAuth/social login without building it ourselves.

Confirmation

lib/session.ts defines the session type. docs/patterns/iron-session.md documents the pattern. Any new field on the session object must be added to the type there.

Pros and Cons of the Options

NextAuth / Auth.js v5

  • Good, because OAuth, email magic links, and credentials all in one library.
  • Bad, because credentials provider is discouraged in v5; session shape is opaque.
  • Bad, because adding isDemo to the JWT requires custom callbacks.

Clerk

  • Good, because fully managed, beautiful UI, no session code to maintain.
  • Bad, because requires third-party redirect; adds external dependency.
  • Bad, because demo-user policy would require custom session metadata sync.

Supabase Auth

  • Good, because integrates with Supabase storage (but we use Neon).
  • Bad, because username/password without email is not the primary use case.
  • Bad, because adds a second database dependency just for auth.

iron-session + bcryptjs

  • Good, because minimal, explicit, and TypeScript-native.
  • Good, because session payload is a plain object we fully control.
  • Neutral, because we write our own password logic (bcrypt makes it safe).

More Information

See docs/patterns/iron-session.md for implementation details. Revisit if multi-tenant or SSO requirements emerge.