- Next.js Authentication: 5 Strategies & When to Use Them
Next.js Authentication: 5 Strategies & When to Use Them
Map five auth contexts—Payload CMS, Shopify OAuth, service-to-service refresh tokens, MCP/PKCE, and security…

In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
The most common authentication mistake I see in Next.js projects isn't a security flaw. It's picking the wrong tool for the wrong context — and not realizing it until you're deep into an implementation that doesn't quite fit.
Authentication in a modern Next.js stack isn't one problem. It's at least five distinct problems that share a name. Adding social login for CMS editors is a different problem from securing an API integration with a third-party service. Authenticating Shopify customers in a headless storefront is architecturally different from protecting an MCP server that AI agents will interact with. Getting these confused early leads to implementations that technically work but cause real pain later — wrong token lifecycle, wrong session model, wrong trust boundary.
This guide maps the territory. It explains which authentication pattern you're actually dealing with, what makes each one architecturally distinct, and routes you to the right implementation. The spokes in this series cover each pattern in full implementation detail. This article is the map to those guides.
Before deciding on a tool, identify which authentication context you're working in. Each has a different answer to three questions: who is authenticating, what are they accessing, and where does the trust come from?
First-party user auth — users authenticating to access your application. The trust comes from your own system. Your database holds the credential and vouches for the identity. In a Payload CMS stack, this means an auth: true collection with HTTP-only cookies, JWT tokens, and server-side sessions. This is the default case for content editors, admins, customers, and members.
Headless commerce auth — customers authenticating through a third-party identity provider (Shopify) to access their order history, account data, and checkout state. The trust is federated: Shopify vouches for the customer, and your Next.js app receives a token it can use to query Shopify's APIs on that customer's behalf. Your app never holds the credential — it only holds the token.
Service-to-service auth — your Next.js app authenticating to third-party APIs to perform background operations. Google Calendar sync, analytics APIs, data enrichment services. The trust model is machine-to-machine: a pre-authorized credential (API key or OAuth refresh token) grants access to a resource owned by a specific user. The user authenticated once; the credential persists across sessions.
AI agent auth — tools you expose to AI agents (Claude, ChatGPT) via MCP servers. The trust model is dynamic delegation: an AI client acts on behalf of a user, and each tool invocation carries a bearer token that proves that delegation. There's no browser session — just a token-verified API surface that stateless agents can call.
Security hardening — not authentication itself, but the layer that sits in front of it: rate limiting, IP intelligence, request validation. This protects your auth endpoints and downstream systems from abuse before any identity check happens.
If your users are employees, editors, members, or customers who register directly in your system, Payload's built-in authentication is the right starting point. You get email/password auth, HTTP-only cookies, JWT tokens, server-side sessions, CSRF protection, rate limiting, and field-level access control — without any additional dependencies.
The mechanism is straightforward: Payload generates a JWT, sets it as an HTTP-only cookie, and verifies it on every request via its CSRF whitelist. Because the cookie is HTTP-only, JavaScript can't access it, which eliminates an entire class of token theft via XSS. Server-side sessions (enabled by default) mean logout actually invalidates the session across all devices, not just clears the client-side cookie.
Payload's native auth works on any collection — not just the default users collection. A customers collection with auth: true gets its own login, logout, me, and refresh endpoints at /api/customers/login and so on. This matters for applications where editors and customers are different entities with different access control rules.
The limitation is what Payload doesn't include out of the box: social login, SAML/SSO, and passkeys require plugins. Five community plugins cover these gaps, each with different tradeoffs.
For the full Payload cookie auth implementation — including the CSRF configuration, cookie domain settings for subdomain setups, the production failure modes that only appear behind a reverse proxy, and the exact debugging checklist when 401s appear after a successful login — the Payload CMS Cookie Auth guide covers everything.
For the plugin landscape — when to use payload-authjs for quick Next.js social login integration, when authsmith is worth its setup complexity for enterprise SSO/SAML, what Better Auth adds over Auth.js, and when payload-totp is the right scoped addition — the Payload CMS Auth Plugins guide has the full comparison and decision framework.
For authenticating programmatic API access — how Payload issues tokens via REST, when to use the Local API vs HTTP calls, and how to query data with authentication context — the Payload CMS API: Authentication & Queries guide covers that pattern.
When you're building a headless Shopify storefront, customer authentication is Shopify's responsibility, not yours. The Customer Account API uses standard OAuth 2.0: your Next.js app redirects to Shopify's hosted login, Shopify authenticates the customer, and your app receives an authorization code it exchanges for an access token.
The architecture shift from the deprecated Storefront API customer auth is significant. The old approach had your app managing customer credentials directly. The new approach is fully federated: Shopify holds the identity, handles 2FA and fraud detection, and your app receives only the token it needs to query Shopify's APIs for that customer's data.
The token must not touch localStorage. The correct pattern is an HTTP-only session cookie managed by your Next.js server — the server holds the Shopify access token, the browser holds a session identifier, and the token never surfaces client-side.
The full OAuth flow implementation, token storage pattern, and how to fetch and update customer data across account pages and order history is in the Shopify Customer Account API guide and the Customer Data in Shopify Headless guide.
Some authentication doesn't involve active users at all. When you're syncing Google Calendar for a user who's offline, pulling analytics data in a background job, or reading from a third-party API as part of an automation workflow, you need a credential that persists beyond the user's active session.
The pattern here is OAuth with long-lived refresh tokens. The user authorizes access once — Google, for example, issues a refresh token alongside the short-lived access token. Your application stores the refresh token securely, and every time it needs to make an API call, it uses the refresh token to obtain a fresh access token without any user interaction.
The critical implementation question is where to store the refresh token. It must survive process restarts and be accessible across serverless function invocations, which rules out in-memory storage. Persisting it in the database works for single-user contexts. Redis with Upstash is the practical choice for multi-user applications where you need fast, distributed access to credentials keyed by user ID.
The full implementation — including why access tokens expire and refresh tokens don't, the Upstash Redis storage pattern, handling the edge case where a user revokes access mid-session, and the Next.js server action that refreshes transparently — is in the Persist Google OAuth Refresh Tokens guide.
When you expose tools to AI agents via an MCP server, the authentication model is different from anything in a standard web app. There's no browser, no session cookie, no login form. An AI agent — Claude, ChatGPT, or any MCP-compatible client — needs to call your server's tools on behalf of a user, and it needs a token to prove that authorization.
MCP servers use OAuth 2.1 with PKCE (Proof Key for Code Exchange). PKCE is mandatory because AI agents are public clients — they can't safely hold a client secret. The PKCE flow means the agent generates a code verifier, sends a hash of it with the authorization request, and proves possession of the original verifier when exchanging the code for a token. Even if someone intercepts the authorization code, they can't exchange it without the verifier.
The architectural implication is that you're building a resource server: a stateless API surface that verifies a bearer token on every tool invocation. No sessions, no cookies. Just token-verified requests from an agent acting on a user's behalf.
Claude and OpenAI handle this differently. Claude initiates the full OAuth flow and stores the token; ChatGPT connectors work similarly but with different discovery requirements. The Claude vs OpenAI Authentication Gap guide covers that comparison in detail.
The OAuth 2.1 + PKCE implementation — including the discovery endpoint, Dynamic Client Registration, token verification middleware, and the specific issues that surface when testing locally vs. in production — is in the OAuth for MCP Server guide.
Authentication checks only work if the request actually reaches your auth logic. Two hardening patterns protect the entry points before any identity verification happens.
IP intelligence at the signup or subscription layer prevents bad-faith submissions before they become database entries. The pattern is a risk score check on the incoming IP address — proxies, known bad actors, data center ranges — combined with a decision to allow, challenge, or reject the request. This is particularly valuable for newsletter signups and free tier registration where you can't require a logged-in session as a prerequisite. The IP Intelligence Firewall guide covers the implementation.
Securing email sending pipelines matters because SMTP credentials in server-side code are a common leak vector. The pattern is to never expose credentials to client components, use environment variables only in server-side contexts, and route all email operations through server actions or route handlers. The Secure Email Pipeline in Next.js guide covers this.
The decision is simpler than it looks once you've named the context:
If your users authenticate directly into your app (editors, customers, members) → Payload native auth, with plugins for social/SSO. Start with the cookie auth guide and add the plugin comparison if you need OAuth providers.
If you're building a headless Shopify storefront → Shopify Customer Account API. The flow is standard OAuth but Shopify is the identity provider, not you.
If you need to call third-party APIs on behalf of users in background jobs → OAuth refresh tokens persisted in Redis. The user authenticates once; your system uses the refresh token independently from there.
If you're building tools for AI agents → OAuth 2.1 with PKCE on an MCP server. Stateless, token-verified, no sessions.
If you need to protect entry points before any auth check → IP intelligence and credential hygiene first.
The spokes in this series each cover one of these contexts in full implementation depth. The right move is to identify your context here, then go directly to the guide that matches it.
Let me know in the comments if you're dealing with a hybrid case — applications that need more than one of these patterns simultaneously are where the architecture decisions get interesting.
Thanks, Matija