Authentication Strategies in Headless WordPress with Faust.js

Authentication Strategies in Headless WordPress with Faust.js

Abstract

When WordPress is decoupled from the frontend, the native cookie-based authentication mechanism breaks due to cross-origin security policies. Faust.js, a framework built on top of Next.js specifically for headless WordPress, provides robust solutions for this challenge. This chapter examines the implementation of authentication using Faust.js, focusing on the “Redirect Strategy” for securing gated content and the underlying token exchange mechanism.

The Headless Auth Challenge

In a standard WordPress site, the backend sets a wordpress_logged_in cookie that the browser automatically sends with every request. In a headless setup, the frontend (Next.js) usually lives on a different domain (e.g., app.com) than the backend (e.g., api.app.com). Browsers block third-party cookies by default, rendering the native auth flow useless. We need a token-based system (JWT or OAuth).

Faust.js: The Specialized Toolkit

Faust.js abstracts the complexity of OAuth integration with WPGraphQL. It introduces a Redirect-Based Authentication flow that mimics the familiar WordPress login experience but keeps the frontend session secure.

The Redirect Flow

  1. Trigger: The user clicks “Login” on the Faust.js app.
  2. Redirect: The user is sent to the WordPress Admin login screen.
  3. Authorization: Upon successful login, WordPress generates an Authorization Code and redirects the user back to the Next.js app.
  4. Exchange: The Next.js server (acting as a backend-for-frontend) intercepts this code and exchanges it for an Access Token and a Refresh Token.
  5. Storage: The Access Token is stored in an encrypted, HTTP-only cookie on the Next.js domain. This is crucial: the token is never exposed to client-side JavaScript, mitigating XSS attacks.

Implementing Gated Content

Faust.js provides the useAuth hook to manage the UI state based on authentication status.

Code Example: Protected Route

import { useAuth, useLogout, getApolloAuthClient } from "@faustwp/core";
import { gql, useQuery } from "@apollo/client";

// Define the query for authenticated data
const VIEWER_QUERY = gql`
  query GetViewer {
    viewer {
      name
      email
    }
  }
`;

export default function GatedPage() {
  const { isAuthenticated, isReady, loginUrl } = useAuth();
  const { logout } = useLogout();

  // 1. Loading State
  if (!isReady) return <p>Loading...</p>;

  // 2. Unauthenticated State
  if (!isAuthenticated) {
    return (
      <div className="login-prompt">
        <p>Access Restricted</p>
        <a href={loginUrl}>Log in via WordPress</a>
      </div>
    );
  }

  // 3. Authenticated View
  return <AuthenticatedContent logout={logout} />;
}

function AuthenticatedContent({ logout }) {
  // getApolloAuthClient automatically attaches the Access Token to the request
  const { data } = useQuery(VIEWER_QUERY, { client: getApolloAuthClient() });

  return (
    <div>
      <h1>Welcome, {data?.viewer?.name}</h1>
      <button onClick={() => logout("/")}>Sign Out</button>
    </div>
  );
}

The getApolloAuthClient() helper is the key component here. It ensures that the GraphQL request includes the Authorization: Bearer <token> header, allowing WPGraphQL to validate the user and return private data.

Local vs. Redirect Strategy

While the redirect strategy is secure and easy to implement, it exposes the WordPress Admin UI to the user. For a fully “White Label” experience, Faust.js supports a Local Strategy. This involves building a custom React login form that sends credentials via a mutation. However, this increases security responsibility, as the developer must handle credential transmission and error states carefully.

Leave a Reply

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

Your Comment
Your Name
Your Email
Your Website