NextJS 13 app dir with Laravel Sanctum, Valet.
Published: 2023-07-24 10:01:57

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](https://github.com/laravel/breeze-next)