In this lesson, we will see an example of how to authenticate users using Laravel Sanctum for a SPA application.
This lesson isn't about how to create an SPA application. The Vue.js part will only be shown partly.
We will change the home page to show categories and products for authenticated users.
Laravel Sanctum comes with the default new Laravel installation. First, you must set the stateful domain. Your application must be on the same domain, but API can be on a subdomain.
Laravel has set some default stateful domains. You can check the domain in the config/sanctum.php
. But, the correct way, in my opinion, would be to set the correct APP_URL
in the .env
, and Laravel will set the stateful domain automatically. Another option is to set SANCTUM_STATEFUL_DOMAINS
in the .env
without the http
part.
Next, we must add the Sanctum EnsureFrontendRequestsAreStateful
Middleware. Laravel has a method called statefulApi
which can be added to enable this Middleware.
bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__)) ->withProviders() ->withRouting( web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->statefulApi(); }) ->withExceptions(function (Exceptions $exceptions) { $exceptions->renderable(function (NotFoundHttpException $e) { return response()->json(['message' => 'Object not found'], 404); }); })->create();
And for the CORS and cookies, we need to set supports_credentials
to true
in the config/cors.php
. Because we are using Axios to make HTTP requests in this example, we must also configure it.
resources/js/bootstrap.js:
import axios from 'axios';window.axios = axios; window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';axios.defaults.withCredentials = true; axios.defaults.withXSRFToken = true; // ...
Before making a POST request to authenticate a user, we must make a GET request to the default sanctum URL /sanctum/csrf-cookie
. After getting a XSRF-TOKEN
token, we can make a POST request to authenticate the user.
<template> // ...</template> <script setup>import { onMounted, ref } from 'vue';import { TailwindPagination } from 'laravel-vue-pagination'; const categories = ref({})const products = ref({})const user = ref(false) const email = ref('')const password = ref('') // ... const login = async () => { await axios.get('/sanctum/csrf-cookie') .then(response => { axios.post('/login', { email: email.value, password: password.value }) .then(response => { user.value = true getCategories() getProducts() }) .catch(error => console.log(error)); // credentials didn't match })} onMounted(() => { if (user.value) { getCategories() getProducts() }})</script>
After successfully authenticating the user, we set the user variable to true in this simple example. In the template, we use v-show
to show login form or categories with products based on the user
variable.
Now, we can protect the API routes.
routes/api.php:
Route::get('/user', function (Request $request) { return $request->user();})->middleware('auth:sanctum'); Route::middleware('auth:sanctum')->group(function () { Route::apiResource('categories', \App\Http\Controllers\Api\CategoryController::class); Route::get('products', [\App\Http\Controllers\Api\ProductController::class, 'index']);});