Authentication in Nuxt.js using Laravel Sanctum
Swapnil Bhavsar • April 22, 2020
nuxt.js laravelYou should never save authorization tokens in localstorage or cookies, as they can be accessed by any third party Javascript code running in the user's browser. By simply using code like this localStorage.getItem('auth_token')
, tokens can be stolen and miss used. By saving tokens in localstorage 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 special kind of cookies called HttpOnly
cookies. These cookies are set by the server, and can not 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 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 can not 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 checkout 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 passing 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 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 Laravel based API backend using Sanctum. Before that let me give you a little information on how to set up your domains in order 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
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
On the final step, choose the Single Page App
when setup asks for choosing 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
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 login 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 Auth Module.
Install the Auth module by running 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 for 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 for 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 server could initialize CSRF protection for the application. It also has a login form which uses $auth.loginWith
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.