TL;DR: Throughout this article, you'll use Django, Django REST framework, and Vue.js to develop an application with a REST backend API and a Vue.js frontend. You will make the Vue.js frontend application consume the backend API with the help of Axios (an HTTP client library). Then, you will handle user authentication with Auth0.
You are going to start by installing all the project's requirements, then you will bootstrap Django and Vue.js as sub-projects.
You can find the final source code of the demo project that you will create in this GitHub repository.
Introduction to Python and Django
Python is a general-purpose programming language and it's among the most popular programming languages in the world nowadays. It's readable, efficient, and easy to learn. Python is also a portable language available for major operating systems such as Linux, Windows, and Mac.
For web developers, Python has many great tools and frameworks that make developers more productive and able to build prototypes in no time. The most popular web framework for development in the Python landscape is Django. Django is advertised as the framework for perfectionists with tight deadlines because of its ability to allow developers to quickly build prototypes.
Django uses the power of Python that offers Django developers a great set of features such as class-based views and a powerful ORM that you can use to model your database requirements without writing any single line of SQL. The Django ORM abstracts away all the complexities of working with databases and, most importantly, doesn't intimidate you when your client has not decided yet on the database system to use. You can start developing using an SQLite database (which doesn't need any special installation) then you can switch to the right database system later on when your client has settled on the right RDBMS (Relational Database Management System) to use.
Django also comes with a powerful migration system that allows you to migrate your database safely and without losing your data when you make changes to the database structure later on.
Another advantage of using the Django web framework is that it has a big and an evolving community. This community has created open source packages for common web development problems, so you don't need to reinvent the wheel when building your project.
Django has an excellent and complete documentation of all the features of the framework supplemented by a set of tutorials created by the community.
Django is also suitable for beginners and you don't have to be an expert in every feature of the framework to start building your web application.
"Django is also suitable for beginners and you don't have to be an expert in every feature of the framework to start building your web application."
Tweet This
Introduction to Vue.js and Vue.js Features
Vue.js is a progressive framework for building user interfaces with JavaScript. You can use Vue.js in the view layer of your application or you can use it to build Single Page Applications (SPAs) by combining it with some other frontend tools.
Vue.js took the best of both Angular.js and React into one library. For example, just like React, Vue.js uses a virtual DOM and a component-based approach. It also uses similar syntax to the Angular.js directives (e.g. v-if
and v-for
)
Vue.js has many features, such as:
- virtual DOM;
- reactive and composable view components;
- great performance;
- and native rendering on iOS and Android thanks to Weex and NativeScript;
Bootstrapping the backend Project
Before you can create the backend project, you need to install some requirements on your development machine. For Django, you need to have Python 3, PIP, and venv
installed.
Please note that you don't need to install a full-fledged database management system to develop with Django. You can use an SQLite database which allows you to have a file-based database that doesn't require any special installation.
Installing the Requirements
Let's start with Python 3. Chances are that you already have Python 3 installed on your machine. If not, then the process is simple. You just need to head over to the official downloads page and pick the installer for your operating system.
You can check if you have Python 3 installed by running the following command from your terminal or command prompt:
python3 --version
The venv
module (part of the Python 3 standard library) allows you to create lightweight virtual environments for your projects. This allows you to have an isolated environments for each Python project (i.e. dependencies are isolated for each project and from the system-wide packages).
Setting up an isolated environment provides you with more control over the installed Python packages. You can have different versions of the same package without having to worry about any conflicts, which allows you to work with different Python projects with different package versions. Also, since each environment can have its own Python binary, this allows you to create various environments with different Python versions.
Once you create a virtual environment, venv
will take care of installing the latest Python binary.
Next, you need to install pip
, a package manager for Python that, by default, installs packages from the Python Package Index (PyPI).
You can verify if pip
is installed on your development machine by running the following command:
# pip or pip3, depending on the environment
pip --version
You should have pip
installed if you have installed Python using the official python.org installer (or via Homebrew in MAC). Also, if you are inside a venv
environment, pip is already installed. For Linux, you may need to install it separately. If that's your environment, take a look at this guide and this page.
Creating a Virtual Environment
Once you have all the backend requirements installed, you will have to create a virtual environment for installing your project's dependencies. Head back to your terminal and run the following commands:
# create a directory to your project
mkdir django-vue
# move the cursor to it
cd django-vue
# initialize a virtual env on it
python3 -m venv ./
Next, you need to activate the virtual environment using source
:
source ./bin/activate
Bootstrapping Django
You can now install Django using pip
:
pip install django
The next step is to create the Django project using this command:
django-admin startproject djangovue .
Now, execute the following commands to organize your Django project with catalog
:
python manage.py startapp catalog
You also need to add this app to the list of installed apps. Open your settings.py
file and add the app:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'catalog'
]
You can then migrate your database and start the development server:
python manage.py migrate
python manage.py runserver
If you navigate to http://127.0.0.1:8000 in a web browser, you will see the following homepage:
Bootstrapping the frontend Project
Vue.js has a CLI utility that allows developers to quickly generate SPAs. The CLI offers pre-configured build setups for a modern frontend workflow and takes only a few minutes to scaffold a basic project boilerplate. This boilerplate is shipped with features such as hot-reloading, lint-on-save, and production-ready builds.
Before you can install the Vue.js CLI, you need to have a development environment with Node.js 6.x or newer and NPM version 3 or newer. You can install both of them by heading to the official Node.js website and download the right installer for your operating system. For development machines, it's recommended to use NVM.
Next, head over to your terminal or command prompt and run the following command:
npm install -g vue-cli
You may need to add
sudo
to the NPM command to install commands globally depending on your NPM configuration.
After installing the Vue.js CLI, you will use it to generate a new Vue.js application based on the Webpack template. Inside the Django project's root, run the following command:
# initialize Vue.js with the Webpack template
vue init webpack frontend
Vue.js will ask a bunch of questions while creating your project. You can answer them as follows:
- Project name: frontend
- Project description: A Vue.js project
- Author: Your Name your.name@somewhere.com
- Vue build: Runtime + Compiler: recommended for most users
- Install vue-router? Yes
- Use ESLint to lint your code? Yes
- Pick an ESLint preset: Standard
- Set up unit tests: No
- Setup e2e tests with Nightwatch? No
- Should we run
npm install
for you after the project has been created? (recommended) Yes, use NPM
Then, to run the initial Vue.js project, you can run the following code:
# change working directory to the frontend
cd frontend
# run the NPM dev script
npm run dev
After that, you should be able to visit the Vue.js application in your browser by navigating to http://127.0.0.1:8080.
On the next sections, you will learn how to secure your backend and your frontend with Vue.js. Then, you will learn how to make your Vue.js app securely consume your Django API.
Creating an Auth0 API
Before you can use secure your app with Auth0, you first need to create an Auth0 account. If you haven't so yet, sign up for a free account now.
As part of the sign up process, you'll be asked to create a Auth0 domain. For example, django-vue-js.auth0.com
. If you'd like to read more about this, you can checkout our learn the basics documentation, that has some great info on Auth0 domains and how to use them.
After that, head over to the API section of the Auth0 dashboard and click on the CREATE API button.
You'll be presented with a form to fill in your API details: the name, the identifier, and the signing algorithm.
You can use the following properties while filling this form:
- Name: Django Vue.js API
- Identifier:
https://django-vuejs-api
- Signing Algorithm: RS256
After that, click on the Create button. You'll be taken to a page where you can further customize your API settings.
That's it! You are now ready to integrate your Django application with Auth0
Integrating Django with Auth0
In this section, you will see how to secure the Django REST API with Auth0.
In the next part, you will build the API, but before that, you will add JWT authentication to your backend using Auth0.
For this reason, you will need to install the Django REST framework. You will also need to install the djangorestframework-jwt
package for handling JWT authentication. You will set up djangorestframework-jwt
to use Auth0 for signing the JWT tokens.
To do that, head back to your terminal, make sure the virtual environment we previously created is activated, and then run the following commands in the project root to install all packages using pip
:
pip install djangorestframework \
djangorestframework-jwt \
cryptography \
python-jose
You may need to install
python3-dev
if you get problems while installing the cryptography package.
Next, you'll need to add these packages to the list of installed apps in settings.py
INSTALLED_APPS = [
#...
'rest_framework',
'rest_framework_jwt'
]
You will also need to add the REST_FRAMEWORK
application definition to settings.py
:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}
Next, make sure to import the following dependencies in your settings.py
file:
import json
from six.moves.urllib import request
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
Then, add the following code at the end of this file:
AUTH0_DOMAIN = '<YOUR_AUTH0_DOMAIN>'
API_IDENTIFIER = '<YOUR_API_IDENTIFIER>'
PUBLIC_KEY = None
JWT_ISSUER = None
if AUTH0_DOMAIN:
jsonurl = request.urlopen('https://' + AUTH0_DOMAIN + '/.well-known/jwks.json')
jwks = json.loads(jsonurl.read().decode('utf-8'))
cert = '-----BEGIN CERTIFICATE-----\n' + jwks['keys'][0]['x5c'][0] + '\n-----END CERTIFICATE-----'
certificate = load_pem_x509_certificate(cert.encode('utf-8'), default_backend())
PUBLIC_KEY = certificate.public_key()
JWT_ISSUER = 'https://' + AUTH0_DOMAIN + '/'
def jwt_get_username_from_payload_handler(payload):
return 'auth0user'
JWT_AUTH = {
'JWT_PAYLOAD_GET_USERNAME_HANDLER': jwt_get_username_from_payload_handler,
'JWT_PUBLIC_KEY': PUBLIC_KEY,
'JWT_ALGORITHM': 'RS256',
'JWT_AUDIENCE': API_IDENTIFIER,
'JWT_ISSUER': JWT_ISSUER,
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
Make sure to replace <YOUR_AUTH0_DOMAIN>
with your own Auth0 domain and <YOUR_API_IDENTIFIER>
with the identifier used while creating the Auth0 API.
The public key is retrieved from https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json
using the request.urlopen()
method. This key is then assigned to PUBLIC_KEY
in JWT_AUTH
(the settings object for djangorestframework-jwt
).
JWT_ISSUER
is your Auth0 domain name prefixed by https.
JWT_ALGORITHM
needs to be set to RS256. That means that you also need to make sure your Auth0 API is using RS256.
The JWT_AUTH_HEADER_PREFIX
is set to Bearer. This is the default prefix used by most frameworks (djangorestframework-jwt
uses a JWT prefix by default).
You also have set JWT_PAYLOAD_GET_USERNAME_HANDLER
to a custom method. This tells djangorestframework-jwt
to use your custom method in order to map the username from the access_token payload to the Django user.
For most cases, you don't need to store users in your database since Auth0 handles all of that for you including advanced features such as profiles. You can use a custom function that checks if a general (can be fake) user that you create exists and map it to all Auth0 users.
To do that, head over to your application and create a user with any valid username such as auth0user. You can do that through Django admin interface or in a migration by adding the following code:
# create an empty data migration
python manage.py makemigrations --empty catalog
This will generate the catalog/migrations/0001_initial.py
file. In this file, you can replace the content with the following code:
from django.db import migrations
from django.conf import settings
def create_data(apps, schema_editor):
User = apps.get_model(settings.AUTH_USER_MODEL)
user = User(pk=1, username="auth0user", is_active=True , email="admin@techiediaries.com")
user.save()
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.RunPython(create_data),
]
For more information about migrations, see the official docs.
Next, just run migrate to create your initial data:
python manage.py migrate
This is the custom method that maps the JWT payload with the auth0user user:
def jwt_get_username_from_payload_handler(payload):
return 'auth0user'
Please, note that you can return the username of any existing user in the database because you are not going to use this for anything besides the sole purpose of letting djangorestframework-jwt
successfully authenticate the user.
Adding Django Views
To make sure the JWT authentication via Auth0 is working, you can add these two view functions in catalog/views.py
:
from rest_framework.decorators import api_view
from django.http import HttpResponse
def public(request):
return HttpResponse("You don't need to be authenticated to see this")
@api_view(['GET'])
def private(request):
return HttpResponse("You should not see this message if not authenticated!")
Also, make sure to create the corresponding URLs in urls.py
. You can replace the whole contents of this file with this:
from django.conf.urls import url
from catalog import views
urlpatterns = [
url(r'^api/public/', views.public),
url(r'^api/private/', views.private)
]
If you rerun your Django project now (python manage.py runserver
), you will have access only to the http://127.0.0.1:8000/api/public/ URL. The other one (/api/private/
) will need an access token to be accessed.
Enabling CORS on Django
As you are going to create a Vue.js application to consume the Django endpoints, you will need to enable CORS on your Django project. To do that, you can install the django-cors-headers
utility as follows:
pip install django-cors-headers
After that, you have to add this utility to your installed apps in the settings.py
file:
INSTALLED_APPS = [
# ...
'corsheaders',
]
Then, you have to add the CorsMiddleware
in this same file:
MIDDLEWARE = [
# ...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
The last thing you will need to do is to configure what origins will be accepted in your Django application:
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
)
You can add this configuration as the last item in the settings.py
file.
Integrating Auth0 with The Vue.js frontend
In this section, you will see how you can add Auth0 authentication to your frontend Vue.js application. You will also add a button to use the access token, retrieved from Auth0, to allow users to fetch the message from the /api/private/
endpoint.
Creating an Auth0 Client
To represent your Vue.js frontend application on Auth0, you will need to create an Auth0 client. To do that, go to your Auth0 dashboard and click on the New Client button.
You will be presented with a page where you can enter the name of the client and the type of the client. You can fill the name of the application as Django Vue.js App and choose the Single Page Web Applications type.
After filling this form, click on the Create button. This will redirect you to a page where you can find the Settings tab.
Configuring the Callback URL
You will need to whitelist the callback URL for your app (http://localhost:8080/
) in the Allowed Callback URLs field of your new Auth0 client. This can be done in the Settings tab. In this tab, find the field mentioned and add http://localhost:8080/
to it. Then click on the Save button.
Creating the Authentication Service
Now. you will focus on integrating Auth0 in your Vue.js application. To do that, you will have to install the auth0.js
and the eventemitter3
libraries:
# change directory to frontend
cd frontend
# install dependencies
npm install --save auth0-js eventemitter3
You also need to install Axios to send AJAX requests to the Django backend:
npm install --save axios
After installing these three dependencies, create a new file under ./frontend/src/auth/
(you will need to create the auth
directory). You will call this file as AuthService.js
and add the following code:
import auth0 from 'auth0-js'
import EventEmitter from 'eventemitter3'
import router from './../router'
export default class AuthService {
authenticated = this.isAuthenticated()
authNotifier = new EventEmitter()
constructor () {
this.login = this.login.bind(this)
this.setSession = this.setSession.bind(this)
this.logout = this.logout.bind(this)
this.isAuthenticated = this.isAuthenticated.bind(this)
this.handleAuthentication = this.handleAuthentication.bind(this)
}
// create an instance of auth0.WebAuth with your
// API and Client credentials
auth0 = new auth0.WebAuth({
domain: '<YOUR_AUTH0_DOMAIN>',
clientID: '<YOUR_CLIENT_ID>',
redirectUri: '<YOUR_CALLBACK_URL>',
audience: '<YOUR_AUDIENCE>',
responseType: 'token id_token',
scope: 'openid profile'
})
// this method calls the authorize() method
// which triggers the Auth0 login page
login () {
this.auth0.authorize()
}
// this method calls the parseHash() method of Auth0
// to get authentication information from the callback URL
handleAuthentication () {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
} else if (err) {
console.log(err)
alert(`Error: ${err.error}. Check the console for further details.`)
}
router.replace('/')
})
}
// stores the user's access_token, id_token, and a time at
// which the access_token will expire in the local storage
setSession (authResult) {
this.accessToken = authResult.accessToken
this.idToken = authResult.idToken
this.profile = authResult.idTokenPayload
this.expiresAt = authResult.expiresIn * 1000 + new Date().getTime()
this.authNotifier.emit('authChange', {authenticated: true})
}
// remove the access and ID tokens from the
// local storage and emits the authChange event
logout () {
delete this.accessToken
delete this.idToken
delete this.expiresAt
this.authNotifier.emit('authChange', false)
// navigate to the home route
router.replace('/')
}
// checks if the user is authenticated
isAuthenticated () {
// Check whether the current time is past the
// access token's expiry time
return new Date().getTime() < this.expiresAt
}
// a static method to get the access token
getAuthToken () {
return this.accessToken
}
// a method to get the User profile
getUserProfile (cb) {
return this.profile
}
}
You will need to replace <YOUR_AUTH0_DOMAIN>
, <YOUR_CLIENT_ID>
, <YOUR_CALLBACK_URL>
, and <YOUR_AUDIENCE>
with the values from your client and API settings. The audience property refers to the identifier of your Auth0 API (i.e. if you followed the instructions, it will be https://django-vuejs-api
).
Creating the Homepage
Now, you are going to refactor your Vue.js homepage to allow users to authenticate via Auth0 and to fetch public and private messages. To do that, open ./frontend/src/App.vue
and replace the <template>
code with this:
<template>
<div>
<button
class="btn btn-primary btn-margin"
v-if="!authenticated"
@click="login()">
Log In
</button>
<button
class="btn btn-primary btn-margin"
v-if="authenticated"
@click="privateMessage()">
Call Private
</button>
<button
class="btn btn-primary btn-margin"
v-if="authenticated"
@click="logout()">
Log Out
</button>
{{"{{ "}}{{ "message" }} }}
<br>
</div>
</template>
Creating the Methods
Still in your ./frontend/src/App.vue
, replace the <script>
tag with this:
<script>
import AuthService from './auth/AuthService'
import axios from 'axios'
const API_URL = 'http://localhost:8000'
</script>
This imports the AuthService
from ./auth/AuthService
and the axios
library. After that, it declares the API_URL constant which holds the URL of the backend server.
After the API_URL
constant, create an instance of AuthService
:
const auth = new AuthService()
Then, below the auth
definition, start defining the app component:
export default {
name: 'app',
data () {
this.handleAuthentication()
this.authenticated = false
auth.authNotifier.on('authChange', authState => {
this.authenticated = authState.authenticated
})
return {
authenticated: false,
message: ''
}
},
}
In this code, you are listening for the authChange
event emitted by AuthService
when the authentication state changes. You then assign the result to the authenticated
variable. You also set the message
variable to be empty. This variable will hold the response from your protected endpoint.
The last thing you need to do is to define the methods
property of the app
component:
export default {
// name: 'app',
// data () { ... },
methods: {
// this method calls the AuthService login() method
login () {
auth.login()
},
handleAuthentication () {
auth.handleAuthentication()
},
logout () {
auth.logout()
},
privateMessage () {
const url = `${API_URL}/api/private/`
return axios.get(url, {headers: {Authorization: `Bearer ${auth.getAuthToken()}`}}).then((response) => {
console.log(response.data)
this.message = response.data || ''
})
}
}
}
The login()
, handleAuthentication()
, and logout()
methods are simply wrappers for the corresponding methods in AuthService.
In the private()
method, you are now using the axios.get()
method to send a GET request to http://localhost:8000/api/private/
. Since this endpoint is protected by Auth0, you also added an Authorization
header. The access token is retrieved from the local storage using AuthService.getAuthToken()
method.
Testing the Django, Vue.js, and Auth0 Integration
Now that everything is correctly configured, you can test the integration among Django, Vue.js, and Auth0. To do that, go back to the root directory of your project and run the following commands:
# run Django in the background
python manage.py runserver &
# move to the frontend directory
cd frontend
# run Vue.js in the background &
npm run dev &
After running both the backend and the frontend projects, you can head to the Vue.js homepage (http://localhost:8080/) to test it.
In the homepage, you will see the Log In button. Clicking on it will redirect you to the Auth0 login page.
After authenticating, you will be redirected to your Vue.js application, where the Call Private and the Log Out buttons will appear. Clicking on the Call Private button will issue a GET request to the Django framework, alongside with an access token, and will fetch the secured message.
"I just learned how easy it is to integrate Django and Vue.js"
Tweet This
Silently Authentication on Page Refresh
One thing you might notice is that, if you refresh the page in your browser, your user will be unauthenticated again. The problem here is that you are persisting user data (profile and tokens) in memory. To solve that, developers used to use localStorage
. However, as this approach is not secure, you will be better off using another approach.
Luckily, Auth0 provides (through its auth0-js
library) a method called checkSession
that verifies if the current user has a valid session at Auth0. If they do, Auth0 then respond with the tokens you need in your app.
To use this method, you will first need to update a configuration property of your Auth0 Application. So, open the Auth0 dashboard, click in your app, and search for the Allowed Web Origins field. On this field, add the http://localhost:8080
. Adding it will make Auth0 allow apps running on the localhost:8080
domain to use the checkSession
method.
After inserting updating this setting, save the changes in your Auth0 Application and head back to your project. There, open the ./frontend/src/auth/AuthService.js
file and update it as follows:
// ... import statements ...
export default class AuthService {
// ... authenticated, authNotifier, constructor, auth0 = new auth0.WebAuth, login ...
handleAuthentication () {
// users returning from Auth0 (after authentication) will have params on the hash
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
} else if (err) {
console.log(err)
alert(`Error: ${err.error}. Check the console for further details.`)
} else {
// no authResult and no error? lets try silent auth
this.silentAuth()
.then(() => {
console.log('user logged in through silent auth')
})
.catch((err) => {
console.log(err)
alert(`Error: ${err.error}. Check the console for further details.`)
})
}
router.replace('/')
})
}
// ... setSession, logout, isAuthenticated, getAuthToken, getUserProfile ...
silentAuth () {
return new Promise((resolve, reject) => {
this.auth0.checkSession({}, (err, authResult) => {
if (err) return reject(err)
this.setSession(authResult)
resolve()
})
})
}
}
After adding the new silentAuth
method to the AuthService
and making handleAuthentication
use it, your app will be ready. So, if you refresh the Vue.js app in your web browser, you will see that your user will be authenticated again and that you can consume the private API.
Recap
In this article, you have bootstrapped both the Django backend project and the Vue.js frontend application. You have also secured your application with Auth0. If you are interested in learning more about how to create REST APIs using Django REST framework and then how to consume it from the Vue.js frontend using Axios, leave a message on the comments so we can know how popular the topic is.
Have fun!