Authentication in a headless WooCommerce setup is fundamentally different from traditional WordPress. There’s no wp-login.php, no cookies managed by PHP, and no nonce verification through theme templates. You need to build an auth layer that works across your decoupled frontend and WordPress backend.

The Authentication Challenge

In traditional WooCommerce, WordPress handles everything: login form, session cookies, nonce tokens, and password reset. When you decouple the frontend, you lose all of this. Your Next.js or Nuxt frontend needs to:

  • Authenticate customers against WordPress user accounts
  • Maintain session state across page navigations
  • Authorize API requests to WooCommerce
  • Handle registration, password reset, and account management

Option 1: JWT (JSON Web Tokens)

The most popular approach for headless WordPress authentication.

Setup: Install the [JWT Authentication for WP REST API](https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/) plugin.

Login flow:

// Frontend: Login

async function login(username, password) {

const response = await fetch(${WP_URL}/wp-json/jwt-auth/v1/token, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ username, password })

});

const data = await response.json();

if (data.token) {

// Store token securely

localStorage.setItem('auth_token', data.token);

return { success: true, user: data.user_display_name };

}

return { success: false, error: data.message };

}

// Frontend: Authenticated API call

async function getMyOrders() {

const token = localStorage.getItem('auth_token');

const response = await fetch(${WP_URL}/wp-json/wc/v3/orders?customer=${userId}, {

headers: { 'Authorization': Bearer ${token} }

});

return response.json();

}

Token validation:

// Validate token is still valid

async function validateToken(token) {

const response = await fetch(${WP_URL}/wp-json/jwt-auth/v1/token/validate, {

method: 'POST',

headers: { 'Authorization': Bearer ${token} }

});

return response.ok;

}

Pros: Stateless, scalable, works across domains
Cons: Token storage security (localStorage is vulnerable to XSS), no built-in refresh mechanism in the standard plugin

Option 2: HttpOnly Cookies with Server-Side Proxy

More secure than client-side JWT storage. Your Next.js API routes proxy auth requests and set HttpOnly cookies.

// Next.js API route: /api/auth/login

export async function POST(request) {

const { username, password } = await request.json();

const wpResponse = await fetch(${WP_URL}/wp-json/jwt-auth/v1/token, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify({ username, password })

});

const data = await wpResponse.json();

if (data.token) {

const response = NextResponse.json({ success: true, user: data.user_display_name });

response.cookies.set('auth_token', data.token, {

httpOnly: true,

secure: true,

sameSite: 'strict',

maxAge: 60 60 24 * 7 // 7 days

});

return response;

}

return NextResponse.json({ success: false }, { status: 401 });

}

Pros: Token never exposed to JavaScript, XSS-resistant
Cons: More complex setup, requires server-side rendering or API routes

Option 3: NextAuth.js with WordPress Provider

NextAuth.js provides a complete auth solution with session management, CSRF protection, and multiple provider support.

// app/api/auth/[...nextauth]/route.js

import NextAuth from 'next-auth';

import CredentialsProvider from 'next-auth/providers/credentials';

export const authOptions = {

providers: [

CredentialsProvider({

name: 'WordPress',

credentials: {

username: { label: "Email", type: "email" },

password: { label: "Password", type: "password" }

},

async authorize(credentials) {

const res = await fetch(${WP_URL}/wp-json/jwt-auth/v1/token, {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

body: JSON.stringify(credentials)

});

const user = await res.json();

if (res.ok && user.token) {

return {

id: user.user_email,

name: user.user_display_name,

email: user.user_email,

wpToken: user.token

};

}

return null;

}

})

],

callbacks: {

async jwt({ token, user }) {

if (user) token.wpToken = user.wpToken;

return token;

},

async session({ session, token }) {

session.wpToken = token.wpToken;

return session;

}

}

};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

Pros: Battle-tested, handles session management, supports social login providers
Cons: Additional dependency, slight learning curve

Registration

WooCommerce’s REST API supports customer creation:

async function register(email, password, firstName, lastName) {

const response = await fetch(${WP_URL}/wp-json/wc/v3/customers, {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'Authorization': Basic ${btoa(${CK}:${CS})} // Consumer key/secret

},

body: JSON.stringify({

email,

password,

first_name: firstName,

last_name: lastName

})

});

return response.json();

}

Note: Customer creation requires consumer key/secret auth (server-side), not customer JWT.

Password Reset

WordPress doesn’t expose password reset via REST API by default. Options:

  • Custom endpoint: Build a WordPress plugin that handles reset token generation and password update
  • Email-based: Redirect to WordPress’s native password reset page
  • Plugin: Use a headless-friendly password reset plugin

Security Best Practices

  • Never store JWT in localStorage for production — use HttpOnly cookies or server-side session
  • Implement token refresh — JWTs should have short expiry (1 hour) with a refresh mechanism
  • Rate limit login attempts — prevent brute force attacks
  • Use HTTPS everywhere — tokens in transit must be encrypted
  • Validate tokens on every API request — don’t trust client-side state alone
  • Implement CORS properly — only allow your frontend domain

Conclusion

For most headless WooCommerce projects, we recommend NextAuth.js with a WordPress credentials provider and HttpOnly cookies. It provides the best balance of security, developer experience, and flexibility. JWT stored in localStorage is fine for prototyping but should be upgraded to server-side session management before production.

Leave a Reply

Your email address will not be published. Required fields are marked *

Close Search Window