Skip to content

001: Authentication Strategy using NextAuth v4 with Custom OIDC Provider

Date: 2025-10-14 Status: Accepted Authors: Ivo van der Knaap Context: Full-stack TypeScript app using Next.js 15 and a custom OIDC identity provider for authentication.

1. Decision Summary

We decided to:

  1. Use next-auth version 4 as our authentication library.
  2. Integrate a custom OIDC provider for user login.
  3. Use stateless JWT-based sessions (no database).
  4. Omit OIDC refresh token rotation, as we only depend on user profile data available in the existing session.
  5. Leverage NextAuth’s built-in JWT cookie rotation (session.updateAge) to keep sessions active without requiring re-authentication.

2. Context and Problem Statement

Our application requires secure user authentication against an existing OIDC identity provider. We need:

  • A production-ready and maintained authentication library.
  • Support for OIDC login without maintaining a session database.
  • Server-side session access for SSR and API routes (via getServerSession).
  • Minimal custom infrastructure (no extra token stores or sync jobs).

The challenge is that the newer authentication ecosystem is still maturing:

  • NextAuth v5 is in beta and not yet stable.
  • Better-Auth, the spiritual successor to NextAuth, currently lacks support for database-less OIDC flows.

3. Considered Options

OptionProsCons
NextAuth v4Mature, stable, feature-complete, supports OIDC and JWT sessionsMaintenance phase; some breaking changes expected in v5
NextAuth v5 (beta)Modernized API and internal improvementsBeta, breaking changes possible, limited production documentation
Better-AuthModern architecture and performanceNo support yet for OIDC without database persistence
Custom OIDC clientFull control, minimal dependenciesHigh complexity, security risk, no built-in cookie/session handling

4. Decision

We are using NextAuth v4 with:

  • session.strategy = "jwt" (stateless sessions, no DB).
  • A custom OIDC provider configuration (using our issuer, client ID, and secret).
  • No OIDC refresh token rotation, since we do not consume or store access tokens for external APIs — the session only holds user identity data.
  • NextAuth’s internal session cookie rotation (session.updateAge, default 24h) to keep users logged in seamlessly without hitting the OIDC provider frequently.

5. Implications

✅ Advantages

  • Simplicity: No database or token store required.
  • Low coupling: The app only depends on OIDC for login, not for ongoing token refresh.
  • Performance: Stateless JWTs avoid DB lookups on every request.
  • Security: NextAuth’s signed and HTTP-only cookies mitigate XSS risk.
  • Compatibility: Fully supported by getServerSession() and useSession() APIs.

⚠️ Trade-offs

  • No long-lived external access: If we later need to call protected APIs using OIDC access tokens, we’ll need to implement token rotation or use NextAuth’s jwt callback to handle refresh logic.
  • Session size: User data is embedded directly in the JWT cookie.
  • Future migration: We’ll need to revisit this decision once NextAuth v5 or Better-Auth fully stabilizes and supports OIDC without a database.

6. Implementation Overview

Frontend flow:

  • The navigation bar contains a Login button calling signIn('oidc') from next-auth/react.
  • After authentication, NextAuth sets an HTTP-only JWT cookie (next-auth.session-token).
  • useSession() (CSR) and getServerSession() (SSR) read from this cookie to access the logged-in user.

Session rotation:

  • NextAuth automatically rotates the cookie every session.updateAge seconds by issuing a new JWT with an extended expiry.
  • When the total session.maxAge expires, users must reauthenticate.

Logout flow:

  • Triggered by signOut() in the client; this clears the session cookie and optionally redirects to a logout endpoint.

7. Diagram

Simplified login + session lifecycle (JWT strategy, no OIDC refresh):

8. Future Considerations

  • Reevaluate when:
    • NextAuth v5 reaches stable release.
    • Better-Auth supports database-less OIDC flows.
    • The application needs API access tokens requiring refresh rotation.
  • Potential migration path:
    • Replace next-auth with better-auth while retaining OIDC provider config.
    • Introduce Redis or another persistence layer if token storage becomes necessary.

Decision: ✅ Adopt next-auth@4 with stateless JWT sessions and no OIDC token refresh. Rationale: Stable, minimal, secure, and perfectly suited for our current needs.