Laravel 11.x Sanctum SPA authentication with Postman!
Tags: laravel, authentication, postman
Published: 2024-06-14 01:32:37

For those who are unfamiliar with [Laravel](https://laravel.com/), it is a very popular monolithic PHP web framework similar to others like [Ruby on Rails](https://rubyonrails.org/). It is known for its ease of use, rapid development and making PHP development far more enjoyable haha!

![PHP being a bad language meme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iy6ebh4kve352rt6kegy.jpg)

[Sanctum](https://github.com/laravel/sanctum) is an official Laravel package that is designed to provides a featherweight authentication system for SPAs and simple APIs. Sanctum is by far the easiest way to add authentication to a Laravel application.

Sanctum offers an API token approach designed for authenticating APIs and mobile applications, which is fairly easy to setup. On the other hand the cookie bases SPA authentication method can be quite confusing to configure. Which is unfortunate as this is the method most people will be using.

So here I am going to show you how to configure the Sanctum SPA Authentication in Laravel 11.x and test it with Postman. I will also include an Axios configuration to easily authenticate from your SPA also!

**NOTE:** The SPA authentication method only works if the request is coming from the same domain, but can come from different subdomains. For example `website.com` can authenticate to `api.website.com`.

Follow along with the Sanctum documentation [here](https://laravel.com/docs/11.x/sanctum).

## Installation & Configuration of Sanctum

By default Laravel does not include authentication, but provides artisan commands to install and scaffold Sanctum. It will generate a few files you will need, like a database migration and a config file specifically for Sanctum.

``` bash
php artisan install:api
```

### Configuring Your First-Party Domains

In your `.env` file you will want to add the domain which you will be accepting requests from. This will be used to verify the `Referer` header, and if not found the `Origin` header.

``` env
SANCTUM_STATEFUL_DOMAINS=website.com
- or -
SANCTUM_STATEFUL_DOMAINS=spa.website.com
``` 

### Sanctum Middleware

Inside `bootstrap/app.php` you want to add the stateful API middleware to the entire application, this can be done with the following. Without this middleware any route protected by the `auth:sanctum` middleware will fail.

``` PHP
->withMiddleware(function (Middleware $middleware) {
    //
    $middleware->statefulApi();
})
```

### CORS and Cookies

For almost any application you will want CORS enabled, Sanctum requires it to function properly. You can easily scaffold its configuration with the following:

``` bash
php artisan config:publish cors
```

This will generate the `config/cors.php` file. In this file you will simply need to set the `supports_credentials` to `true`. This will prevent the cookies from being blocked by CORS.

Lastly for this section you will to edit the `SESSION_DOMAIN` in your `.env` file. You will want to change it to the domain of your SPA or you can prefix it with a `.` to make it accept all subdomains.

For example `.website.com` will allow any subdomain.

### Logging In

So now we have everything in place to verify out tokens, but we need to create the login route which will verify our email and password and associate that with our session.

You can easily just add the following to your `routes/web.php`

``` PHP
use Illuminate\Support\Facades\Auth;

Route::post('/login', function () {
    $credentials = $request->validate([
        'email' => ['required', 'email'],
        'password' => ['required'],
    ]);

    if (Auth::attempt($credentials)) {
        $request->session()->regenerate();
			
        return Response([
            'message' => 'Successful login!'
        ], 200);
    }

    return Response([
        'message' => 'Mismatch email and password!'
    ], 401);
});
```

You can also follow the [example provided by Laravel](https://laravel.com/docs/11.x/authentication#authenticating-users) however it does not return JSON like above, but a redirect response.

## Configure Postman Collection

Before we configure Postman I want to briefly discuss the operations required for logging in. 

First Sanctum needs us to hit the `/sanctum/csrf-cookie` endpoint with a `GET` request. This will return a 204 request meaning it was successful but has no content, it only contains our session cookies.

Then we can make a `POST` request to our newly created `/login` route with an an `email` and `password` in the body to authenticate and associate the authenticated user with the session matching the issued token in our previous step.

Now we are fully authenticated and can use any sanctum protected route by passing the cookie along with the header `X-XSRF-TOKEN` containing the parsed value of the cookie in the request. This can be tested by making a request to the `/api/user` route that is included by default.

Don't worry Postman and Axios can easily manage the cookies and headers for us!

### The postman collection

You can download this [Laravel 11.x Sanctum SPA Collection](https://github.com/PrismLabsDev/laravel_11.x_sanctum_spa_collection) to follow along.

The key to the postman collection lies in our collection Pre-request script:

``` JavaScript
const jar = pm.cookies.jar();

jar.get("http://localhost:5174", "XSRF-TOKEN", (err, cookie) => {

    pm.request.addHeader({
        key: "X-XSRF-TOKEN",
        value: cookie
    });

    pm.request.addHeader({
        key: "Origin",
        value: "http://localhost:5174"
    });

    pm.request.addHeader({
        key: "Referer",
        value: "http://localhost:5174"
    });

});
```

This script will run before every request, parsing the cookies found for the given origin and adding them to the `X-XSRF-TOKEN` header. It also sets both the `Referer` and `Origin` header which sanctum requires. 

You may notice that I have set the `Referer` and `Origin` header to `localhost:5174` this is simply because that is a common port for SPA applications built with Vite!

NOTE: If this fails at any point it is probably because Postman requires you to give access to the cookies that belong to the origin. You can do it with the following process:

![How to view cookies in postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1h6y4ds97vwxxgqgxh9c.png)

![How to allow cookies in postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hy75835tdn6jvurg3wq7.png)

![How to add cookie origins in postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywtp1m9clnukp3nqn6ii.png)

## Axios configuration

Axios is a promise based HTTP client for JavaScript giving you a really easy way to work with HTTP requests. In our case it offers configuration points that make working with Sanctum extremely easy.

``` JavaScript
import axios from 'axios';

let axiosInstance = axios.create({
    baseURL: 'http://localhost',
    timeout: 1000,
    headers: {
        Accept: 'application/json'
    },
    withCredentials: true,
    withXSRFToken: true,
    xsrfCookieName: "XSRF-TOKEN",
    xsrfHeaderName: "X-XSRF-TOKEN",
});

export default axiosInstance;
```

By examining the above configuration, if you are familiar with Axios, you will see that its a pretty simple Axios configuration except for 4 configurations:

- withCredentials
- withXSRFToken
- xsrfCookieName
- xsrfHeaderName

Lets first discuss the `withCredentials`. This option tells Axios to attach cookies found in the browser batching the origin to the request.

Next is `withXSRFToken`. This option tells Axios to set the XSRF header only for requests of the same origin.

Finally `xsrfCookieName` and `xsrfHeaderName` tell Axios which cookie to parse for the header, and the name of the header to attach. Both options default to the values given in the above configuration which Sanctum expects.

Now any requests made with this Axios instance will be compatible with Laravel Sanctum SPA authentication!