April 10, 2026

Securing B2B SaaS: Why We Use HttpOnly Cookies for JWT Authentication

By Paresh Prajapati • Lead Architect

Securing B2B SaaS: Why We Use HttpOnly Cookies for JWT Authentication

The LocalStorage Vulnerability

When building decoupled B2B SaaS applications at Smart Tech Devs, authentication is the first line of defense. The most common architectural pattern for a Next.js frontend communicating with a Laravel API is using JSON Web Tokens (JWTs). However, the most critical mistake developers make is returning that JWT in the API payload and storing it in the browser's localStorage.

Storing sensitive tokens in localStorage or sessionStorage exposes your entire user base to Cross-Site Scripting (XSS) attacks. If a malicious script is injected into your frontend (perhaps through a compromised third-party NPM package), that script can easily read localStorage, steal the admin's JWT, and hijack the session entirely. For enterprise platforms, this is an unacceptable security risk.

The Enterprise Solution: HttpOnly and SameSite Cookies

To build durable, secure architecture, the frontend JavaScript should never have direct access to the authentication token. Instead, the Laravel backend must attach the JWT to an HttpOnly, Secure cookie attached to the response.

An HttpOnly cookie cannot be read by document.cookie in JavaScript, neutralizing XSS token theft. Furthermore, setting the SameSite=Strict attribute ensures the cookie is only sent in a first-party context, mitigating Cross-Site Request Forgery (CSRF) attacks.

Step 1: The Laravel Authentication Controller

Here is how we securely issue the token upon a successful login, strictly preventing frontend access.


namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthenticationController extends Controller
{
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (!Auth::attempt($credentials)) {
            return response()->json(['message' => 'Invalid credentials'], 401);
        }

        $user = Auth::user();
        // Generate the token (using Sanctum or a JWT package)
        $token = $user->createToken('b2b-dashboard-access')->plainTextToken;

        // Create the secure cookie
        $cookie = cookie(
            name: 'saas_auth_token',
            value: $token,
            minutes: 1440, // 24 hours
            path: '/',
            domain: env('SESSION_DOMAIN'), // e.g., '.smarttechdevs.in'
            secure: true, // MUST be true in production (HTTPS)
            httpOnly: true, // Prevents JavaScript/XSS access
            sameSite: 'strict' // Prevents CSRF
        );

        return response()->json([
            'status' => 'success',
            'user' => [
                'id' => $user->id,
                'name' => $user->name,
                'role' => $user->role
            ]
        ])->withCookie($cookie);
    }
}

Step 2: Frontend API Configuration (Axios)

Because the token is in a cookie, the frontend no longer needs to manually attach an Authorization: Bearer header to every request. We simply tell Axios to automatically include credentials (cookies) in its cross-origin requests to the API.


// lib/api.js
import axios from 'axios';

const api = axios.create({
    baseURL: process.env.NEXT_PUBLIC_API_URL,
    // CRITICAL: This ensures the HttpOnly cookie is sent with every request
    withCredentials: true, 
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }
});

export default api;

Conclusion

Security by design is what separates enterprise architecture from hobbyist projects. By shifting your JWTs out of local storage and into HttpOnly cookies, you close the door on the most common frontend vulnerabilities. It requires a slight shift in how you handle API routing, but the peace of mind and data protection it provides to your B2B clients is invaluable.

Paresh Prajapati
Lead Architect, Smart Tech Devs