Here's a simplified way to implement cookie-based authentication with Laravel Sanctum and NextJS's new app directory (app router) that can work with a Laravel Valet setup.
I'll go into the details later, but here's the short version:
I'll go into the details later, but here's the short version:
Install a package to enable making requests from middleware:
npm i isomorphic-unfetch
Create or update the .env.local file in the NextJS root:
NEXT_PUBLIC_BACKEND_URL="http://localhost:8000"
NEXT_PUBLIC_BACKEND_NAMED_URL="http://testdomain.test"
If it doesn't exist already, create a middleware.ts file with the following content:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import fetch from 'isomorphic-unfetch';
export async function middleware(request: NextRequest) {
const authCookie = request.cookies.get('laravel_session');
const base = `${request.nextUrl.protocol}//${request.nextUrl.hostname}:${request.nextUrl.port}`;
const csrfResponse = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_NAMED_URL}/sanctum/csrf-cookie`, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
if (!csrfResponse.ok || ! authCookie?.value) {
return NextResponse.redirect(`${base}/login`);
}
const response = await fetch(`${base}/api/user`, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': authCookie.value,
},
credentials: 'include',
});
if (response.status === 401) {
return NextResponse.redirect(`${base}/login`);
}
}
export const config = {
matcher: ["/((?!register|api|login|manifest|_next|styles|scripts).*)"],
}
What this middleware does:
- If the response to api/user is unsuccessful, it redirects to /.
- If a user is already logged in, it redirects from /login to /.
When developing locally with Laravel Sanctum and an SPA like NextJS or Vue, some problems may arise. All requests must originate from the same domain. Usually, a NextJS app runs on localhost:3000, while our backend is configured to run with testdomain.test. In this case, it's impossible to authenticate with cookies due to the domain difference. This is a security measure.
A workaround for this is to run php artisan serve in the backend directory. You can then access the backend API via the base URL http://localhost:8000.
Another issue is that NextJS server-side requests won't work with localhost due to IPv6 issues when hitting the csrf endpoint. So, we need to get the csrf cookie like this:
const csrfResponse = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_NAMED_URL}/sanctum/csrf-cookie`, {
All other requests will carry the authentication cookie, so they must go through localhost. Hence, we compose the base URL like this:
const base = `${request.nextUrl.protocol}//${request.nextUrl.hostname}:${request.nextUrl.port}`;
To create hooks for login/register, check the official Laravel repo: https://github.com/laravel/breeze-next
Top comments (0)