Authentication with Django and Nuxt (the frontend)
Introduction
This tutorial part will teach how to configure the frontend and backend together for their communication.
NOTE: I will omit most of the HTML to maintain brevity. You can consult the repository to see the complete files.
Prerequisites
- Familiarity with django-rest-framework
- Knowledge of nuxt-auth: this video will be enough
Setting up the frontend
If you want to integrate authentication to an existing project, add the required modules to
your project using npm or yarn. Just run npm install @nuxtjs/auth @nuxtjs/axios in
the frontend/ directory.
If you’re starting from scratch, you can follow these steps.
$ npx create-nuxt-app frontend # in the root directory `nuxtjs+drf-user-auth/`
Generating Nuxt.js project in frontend
? Project name: frontend
? Programming language: JavaScript
? Package manager: Npm
? UI framework: Vuetify.js
? Nuxt.js modules: Axios
? Linting tools: ESLint, Prettier
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
$ cd frontend/ && npm install @nuxtjs/auth --save
I will be using Vuetify as my UI framework, but you are
free to use whatever else you want. However, be aware that if you use something else (like
Buefy, you will have to use different HTML tags. For example, a
button in Vuetify <v-btn @click="greetPerson()">Click Me!</v-btn> will be written as
<b-button @click="greetPerson()">Click Me!</b-button> in Buefy. The Vue directives and
general API, however, remain the same.
Initial configuration of the auth module
First we’ll configure our settings to use the auth-module.
export default {
// [...other settings...]
modules:{
// [...other stuff...]
'@nuxtjs/axios',
'@nuxtjs/auth',
},
axios: {
baseURL: 'http://127.0.0.1:8000/',
},
auth: {
strategies: {
local: {
endpoints: {
login: {
url: 'token/login/',
method: 'post',
propertyName: 'auth_token',
},
logout: { url: 'token/logout/', method: 'post' },
user: {
url: 'accounts/data/',
method: 'get',
propertyName: false,
},
},
tokenType: 'Token',
tokenName: 'Authorization',
},
redirect: {
login: '/login',
home: '/',
},
},
},
}
Then create a file frontend/store/index.js and save it. Fire up a development server
using by running npm run dev in the frontend/ directory. Visit http://127.0.0.1:3000/
in your browser.
Explanation
Here’s what we did:
- Added the
axiosmodule to our setup. This module can be best compared to therequestspackage that we often use in python. - Added the
authmodule to our setup that will automatically send the required requests to the backend for user authentication. - We store the currently logged-in user’s data in the Vuex
store. So we created
frontend/store/index.jsto activate this module.
We will make a few changes in frontend/layouts/default.vue.
<!-- Add these lines somewhere near the middle so that these buttons are visible on the navbar -->
<template>
<!-- Other stuff -->
<div class="authentication-buttons">
<div v-if="$auth.loggedIn">
{{ $auth.user.email }}
<v-btn icon to="/logout" class="logout-btn">
<v-icon light @click="$auth.logout()">mdi-logout</v-icon>
</v-btn>
</div>
<div v-else>
<v-btn icon to="/login" class="login-btn">
<v-icon>mdi-login</v-icon>
</v-btn>
<v-btn icon to="/register" class="register-btn">
<v-icon>mdi-account-key-outline</v-icon>
</v-btn>
</div>
</div>
<!-- Other stuff -->
</template>
<script>
export default {
// Some more stuff
};
</script>
We used the v-if directive to check if the current user is logged-in. If there is, then
display a logout button, else display login and register buttons using the v-else
directive.
Creating the authentication pages
Next, let’s make pages (routes) for login, logout, and register.
<!-- This contains the login form. You should also add some custom validation yourself. -->
<template>
<div class="login-page">
<v-layout flex align-center justify-center>
<v-flex xs6 sm6 elevation-6>
<v-card>
<v-card-title flex align-center justify-center>
<h1>Login</h1>
</v-card-title>
<v-card-text class="pt-4">
<div>
<v-form ref="form">
<v-text-field
v-model="userData.email"
label="Enter your e-mail address"
counter
required
></v-text-field>
<v-text-field
v-model="userData.password"
label="Enter your password"
:append-icon="
userData.showPassword ? 'mdi-eye-off' : 'mdi-eye'
"
:type="userData.showPassword ? 'text' : 'password'"
required
@click:append="userData.showPassword = !userData.showPassword"
></v-text-field>
<v-layout justify-space-between>
<v-btn @click="logInUser(userData)"> Login </v-btn>
<a href="?">Forgot Password</a>
</v-layout>
</v-form>
</div>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</div>
</template>
<script>
export default {
data: () => ({
userData: { email: '', password: '', showPassword: false },
}),
methods: {
async logInUser(userData) {
try {
await this.$auth.loginWith('local', {
data: userData,
});
console.log('notification successful');
} catch (error) {
console.log('notification unsuccessful');
}
},
},
};
</script>
Explanation
In this page, we created a login form with email and password fields. There is a data
object that stores all properties of the form so that we can perform validation on it, and
send that validated data to the backend. On clicking the button labelled Login, an async
function logInUser() is executed. This uses the Nuxtjs auth module to send a POST
request containing the userData to token/login/ which will then automatically perform
the login and return the login token as auth_token. This auth_token is stored in the
Vuex store for further use later. Furthermore, a new request is sent to accounts/data/
which then returns all the data about the logged in user like email, id, first_name,
etc. The logout button already works. When you click on it, it calls an auth module
function- $auth.logout(). This simply deletes the auth_token from memory and flushes
out the $auth.user object.
So login and logout functionalities are working! Yay!
Let’s get the registration functionality working now. This will be fairly easy.
<template>
<div class="register-page">
<v-container>
<v-layout flex align-center justify-center>
<v-flex xs6 sm6 elevation-6>
<v-card>
<v-card-title flex align-center justify-center>
<h1>Register</h1>
</v-card-title>
<v-card-text class="pt-4">
<div>
<v-form ref="form">
<v-text-field
v-model="userData.email"
label="Enter your e-mail address"
counter
required
></v-text-field>
<v-text-field
v-model="userData.password"
label="Enter your password"
required
:append-icon="
userData.showPassword ? 'mdi-eye' : 'mdi-eye-off'
"
:type="userData.showPassword ? 'text' : 'password'"
@click:append="
userData.showPassword = !userData.showPassword
"
></v-text-field>
<v-text-field
v-model="userData.password2"
label="Confirm password"
:append-icon="
userData.showPassword2 ? 'mdi-eye' : 'mdi-eye-off'
"
:type="userData.showPassword2 ? 'text' : 'password'"
required
@click:append="
userData.showPassword2 = !userData.showPassword2
"
></v-text-field>
<v-layout justify-space-between>
<v-btn @click="signUp(userData)"> Register </v-btn>
<a href="">Forgot Password</a>
</v-layout>
</v-form>
</div>
</v-card-text>
</v-card>
</v-flex>
</v-layout>
</v-container>
</div>
</template>
<script>
export default {
data: () => ({
userData: {
email: '',
password: '',
password2: '',
showPassword: false,
showPassword2: false,
},
}),
methods: {
async signUp(registrationInfo) {
await this.$axios
.$post('accounts/users/', registrationInfo)
.then((response) => {
console.log('Successful');
})
.catch((error) => {
console.log('errors:', error.response);
});
this.$auth.loginWith('local', {
data: registrationInfo,
});
},
},
};
</script>
As soon as the user enters their details in the form, and click the Register button, a
function signUp() is fired. Using the axios module, a POST request is sent to
accounts/users. Assuming the data is valid, the user is created in the database. Next, we
use the auth module to again login using the same logic as we did previously in our login
page login.vue. Logout functionality stays the same as before.
Conclusion
So, now that you are done with authentication, what new feature do you plan to implement?
I thank you for taking the time to follow this tutorial and I hope I can help you again in future. See you!