Now that we have created an authentication page and can log in the user, we need to protect the routes, both on the front-end and back-end. So let's implement auth:sanctum
Middleware.
We have installed Sanctum earlier with the install:api
Artisan command. Now, we can update the API routes to use the auth:sanctum
Middleware.
routes/api.php:
Route::get('/user', function (Request $request) { return $request->user();})->middleware('auth:sanctum'); Route::group(['middleware' => 'auth:sanctum'], function() { Route::apiResource('posts', PostController::class); Route::get('categories', [CategoryController::class, 'index']); Route::get('/user', function (Request $request) { return $request->user(); });});
Next, we need to redirect the user to the login page, on the front-end. We need to intercept the response with Axios.
resources/js/bootstrap.js:
import axios from 'axios';window.axios = axios; window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; window.axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401 || error.response?.status === 419) { if (JSON.parse(localStorage.getItem('loggedIn'))) { localStorage.setItem('loggedIn', false) location.assign('/login') } } return Promise.reject(error) })
Here, in case of an error, if the status is 401 (unauthorized) or 419 (session expired), then if we have loggedIn
in the local storage, we set it to false
and redirect the user to the login page.
Next, to make Laravel Sanctum work properly we need to change a few things. We must add the statefulApi
Middleware.
bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__)) ->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) { // })->create();
Then for the Axios, we need another header withCredentials
.
resources/js/bootstrap.js:
import axios from 'axios';window.axios = axios; window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';window.axios.defaults.withCredentials = true window.axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401 || error.response?.status === 419) { if (JSON.parse(localStorage.getItem('loggedIn'))) { localStorage.setItem('loggedIn', false) location.assign('/login') } } return Promise.reject(error) })
Notice: for your SPA to work correctly, don't forget to set the correct
APP_URL
in the.env
file.
And lastly, we will protect the Vue.js routes. In the routes, we will add a function auth
which will do the same thing: check the local storage if the user is logged in and redirect to the /login
.
resources/js/routes/index.js:
import { createRouter, createWebHistory } from 'vue-router'; import AuthenticatedLayout from '@/layouts/Authenticated.vue';import GuestLayout from '@/layouts/Guest.vue'; import PostsIndex from '@/components/Posts/Index.vue'import PostsCreate from '@/components/Posts/Create.vue'import PostsEdit from '@/components/Posts/Edit.vue'import Login from '@/components/Auth/Login.vue'; function auth(to, from, next) { if (JSON.parse(localStorage.getItem('loggedIn'))) { next() } next('/login')}// ...
Then we need to add this auth
function for the authenticated routes with the beforeEnter
parameter.
resources/js/routes/index.js:
import { createRouter, createWebHistory } from 'vue-router'; import AuthenticatedLayout from '@/layouts/Authenticated.vue';import GuestLayout from '@/layouts/Guest.vue'; import PostsIndex from '@/components/Posts/Index.vue'import PostsCreate from '@/components/Posts/Create.vue'import PostsEdit from '@/components/Posts/Edit.vue'import Login from '@/components/Auth/Login.vue'; function auth(to, from, next) { if (JSON.parse(localStorage.getItem('loggedIn'))) { next() } next('/login')} const routes = [ { path: '/', component: GuestLayout, children: [ { path: '/login', name: 'login', component: Login }, ] }, { component: AuthenticatedLayout, beforeEnter: auth, children: [ { path: '/posts', name: 'posts.index', component: PostsIndex, meta: { title: 'Posts' } }, { path: '/posts/create', name: 'posts.create', component: PostsCreate, meta: { title: 'Add new post' } }, { path: '/posts/edit/:id', name: 'posts.edit', component: PostsEdit, meta: { title: 'Edit post' } }, ] }] export default createRouter({ history: createWebHistory(), routes})
And finally, let's take care of the home page which would automatically redirect to the login form. In the routes, we need to add the path and where to redirect to.
resources/js/routes/index.js:
// ...const routes = [ { path: '/', redirect: { name: 'login' }, component: GuestLayout, children: [ { path: '/login', name: 'login', component: Login }, ] }, { component: AuthenticatedLayout, beforeEnter: auth, children: [ { path: '/posts', name: 'posts.index', component: PostsIndex, meta: { title: 'Posts' } }, { path: '/posts/create', name: 'posts.create', component: PostsCreate, meta: { title: 'Add new post' } }, { path: '/posts/edit/:id', name: 'posts.edit', component: PostsEdit, meta: { title: 'Edit post' } }, ] }]// ...
And that's it. Now we have protected our routes from both front-end and back-end.