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:
- Use
next-authversion 4 as our authentication library. - Integrate a custom OIDC provider for user login.
- Use stateless JWT-based sessions (no database).
- Omit OIDC refresh token rotation, as we only depend on user profile data available in the existing session.
- 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
| Option | Pros | Cons |
|---|---|---|
| NextAuth v4 | Mature, stable, feature-complete, supports OIDC and JWT sessions | Maintenance phase; some breaking changes expected in v5 |
| NextAuth v5 (beta) | Modernized API and internal improvements | Beta, breaking changes possible, limited production documentation |
| Better-Auth | Modern architecture and performance | No support yet for OIDC without database persistence |
| Custom OIDC client | Full control, minimal dependencies | High 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()anduseSession()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
jwtcallback 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')fromnext-auth/react. - After authentication, NextAuth sets an HTTP-only JWT cookie (
next-auth.session-token). useSession()(CSR) andgetServerSession()(SSR) read from this cookie to access the logged-in user.
Session rotation:
- NextAuth automatically rotates the cookie every
session.updateAgeseconds by issuing a new JWT with an extended expiry. - When the total
session.maxAgeexpires, 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-authwithbetter-authwhile retaining OIDC provider config. - Introduce Redis or another persistence layer if token storage becomes necessary.
- Replace
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.