You should never save authorization tokens in local storage or cookies, as they can be accessed by any third-party JavaScript code running in the user's browser. By simply using a code like this localStorage.getItem('auth_token')
, tokens can be stolen and misused. By saving tokens in local storage or insecure cookies, you are jeopardizing the security of your user's account.
Laravel Sanctum is a package made by Taylor Otwell which solves this issue by using a special kind of cookies called HttpOnly
cookies. These cookies are set by the server, and cannot be read by the JavaScript code running on the client-side aka browser. When these cookies are set by the server, the browser automatically sends these cookies back to the server with every request, and thus the server knows it an authorized request.
Laravel Sanctum also takes care of CSRF protection by including CSRF cookie in each request. And also protects your site against XSS based attacks.
Introduction
In this tutorial, we will build a sample Nuxt.js application which will demo the authentication flow using Laravel sanctum. We are going to build our Nuxt application in the SPA (Single page application) mode. If you want to deploy your application in universal (SSR - Server-side rendered) mode, you can still do it.
Authentication in the Nuxt using Laravel sanctum does work in SSR mode. But it doesn't make much sense if your application running SSR mode if the application requires login to access and search engine can access your site without a login.
SPA and Backend domains
To work with Sanctum, we should be familiar with a few things first. We must use our SPA and API backend on the same domain, like frontend on domain.com
and API on api.domain.com
. We cannot set frontend on domain.com and backend (API) on another-domain.com
. The client must be able to include cookies with each request being sent to the backend.
Please check out Mozilla developer documentation on withCredentials & article by Mohamed Said on Authentication and Laravel Sanctum for information on session using cookies.
Here is my little advice, when you are developing the Nuxt site in local, use
php artisan serve
command to serve Laravel backend athttp://localhost:8000
instead of using valet likesanctum-nuxt.test
.
Setting up Laravel Sanctum
Enough of theory! Let's begin by setting up Laravel sanctum for your Laravel application. Just follow these steps carefully to configure your app.
In your Laravel 7 app, install the sanctum package using composer:
composer require laravel/sanctum
Next, publish sanctum configuration & database migration files.
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Then, we will need to run our migration to create personal_access_tokens
table, which will be used by Sanctum to save access tokens for the users.
php artisan migrate
Since we are using Sanctum for our SPA, we need to make sure that our HTTP request pass through Sanctum middleware. Configure api
middleware group in app/Http/Kernel.php
to use Sanctum middleware.
// FILE: app/Http/Kernel.php
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
protected $middlewareGroups = [
...
'api' => [
EnsureFrontendRequestsAreStateful::class, // Add & import this class
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Next, let's configure domains for our SPA. To make sure our SPA works with Sanctum, set the appropriate values for SESSION_DOMAIN
and SANCTUM_STATEFUL_DOMAINS
inside .env
file at the root of our application.
SESSION_DOMAIN="localhost"
SANCTUM_STATEFUL_DOMAINS="localhost"
Also, make sure our SPA domain is configured and have set supports_credentials
to true
in CORS config/cors.php
.
// FILE: config/cors.php
return [
...
'paths' => ['*'],
'allowed_origins' => ['http://localhost:3000'],
'supports_credentials' => true,
...
];
And our last step should be to use Sanctum auth middleware auth:sanctum
to protect our routes like this in routes/api.php
.
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
And we are done! Now that we have finished setting up our API backend with Sanctum. Let's proceed for setting up our Nuxt SPA app to use our API.
Nuxt application setup
Let's begin by setting up the Nuxt.js app first, and then the Laravel-based API backend using Sanctum. Before that, let me provide you a little information on how to set up your domains to work with the Sanctum's SPA authentication.
Create a new Nuxt.js project by entering the following command in your terminal.
npx create-nuxt-app your-project-name
Next, setup will ask you a series of questions, answers them as your preferences.
? Project name: sanctum-nuxt
? Project description: My fantastic Nuxt.js project
? Author name: Swapnil Bhavsar
? Choose the package manager: Npm
? Choose UI framework: Tailwind CSS
? Choose custom server framework: None (Recommended)
...
When setup asks for choosing Nuxt.js modules, be sure toggle axios
the option, We will need it, so that our Nuxt app can make HTTP requests.
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
- Axios
In the final step, choose the Single Page App
when setup asks for, choose rendering mode. Of course, you can also choose to render your app in the universal
mode. But for the scope of this tutorial, we will deploy our app in the spa
mode.
? Choose rendering mode (Use arrow keys)
- Single Page App
Auth module
The Auth module is an official package provided by the Nuxt.js community which adds authentication support for the Nuxt.js app. It provides helper objects like $auth
, which can be used to log in a user or access an authenticated user.
To allow our app to authenticate with our API backend, we will need to create an auth strategy scheme in the auth section of nuxt.config.js
. But before that, we need to install the Auth Module.
Install the Auth module by running the following command in terminal:
npm install @nuxtjs/auth
Then, register the auth module in nuxt.config.js
like this:
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth'
],
auth: {
// Options
}
Before proceeding to configuring the auth module, set baseURL
and credentials
options for the axios
section in nuxt.config.js
. Setting credentials: true
will include cookies in the HTTP request made to the server.
axios: {
baseURL: "http://localhost:8000",
credentials: true
},
Next, configure the auth module in nuxt.config.js
to authenticate with our Laravel application endpoints like this:
auth: {
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
},
strategies: {
local: {
endpoints: {
login: { url: '/login', method: 'post', propertyName: false },
user: { url: '/api/user', method: 'get', propertyName: false }
},
tokenRequired: false,
tokenType: false
}
},
localStorage: false
},
After adding the local
strategy in the auth section, let's proceed to creating a login page for our Nuxt app.
Creating a login page
Create a file called login.vue
under the pages
directory in your Nuxt.js project with the following content. Before loading, it sends a request to /sanctum/csrf-cookie
path so that the server could initialize CSRF protection for the application. It also has a login form which uses $auth.loginWith
a function provided by the auth module to submit the login form.
<template>
<div class="flex h-screen items-center justify-center">
<form ref="loginform" @submit.prevent="login()" class="w-1/4 mx-auto p-4">
<h1 class="font-semibold mb-2 text-xl">
Login
</h1>
<div class="mb-4">
<label for="email" class="block mb-1 text-sm">Email</label>
<input
type="email"
name="email"
class="w-full border rounded px-3 py-2"
required
/>
</div>
<div class="mb-4">
<label for="password" class="block mb-1 text-sm">Password</label>
<input
type="password"
name="password"
class="w-full border rounded px-3 py-2"
required
/>
</div>
<button
type="submit"
class="bg-blue-500 text-white font-semibold py-2 px-10 w-full rounded"
>
Login
</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
error: {},
};
},
mounted() {
// Before loading login page, obtain csrf cookie from the server.
this.$axios.$get('/sanctum/csrf-cookie');
},
methods: {
async login() {
this.error = {};
try {
// Prepare form data
const formData = new FormData(this.$refs.loginform);
// Pass form data to `loginWith` function
await this.$auth.loginWith('local', { data: formData });
// Redirect user after login
this.$router.push({
path: '/',
});
} catch (err) {
this.error = err;
// do something with error
}
},
},
};
</script>
Access authenticated user
You can access the authenticated user data by using this.$auth.user
. For example, create an account.vue
the page which will show the user's information like this in the /page
directory.
<template>
<div>
<p>Name: {{ $auth.user.name }}</p>
<p>Name: {{ $auth.user.email }}</p>
</div>
</template>
<script>
export default {
middleware: 'auth',
};
</script>
You can use auth middleware to make sure that your pages are only accessible by authenticated users. You can find more information on using middleware here: https://auth.nuxtjs.org/guide/middleware.html
Conclusion
This is a bare minimum example for you to get started with authentication in Nuxt.js using Laravel Sanctum. I am pretty amazed by the simplicity provided by the Sanctum package over Laravel Passport when implementing API authentication for your applications.
This was my first article on my blog, hope you will find it useful. You can ask me questions or send feedback about the article on Twitter @swapnil_bhavsar.