JWT Authentication With React JS and Django
As a full-stack developer, it is crucial to understand how to build an authentication system using backend and manage the flow of authentication in frontend.
In this blog, we will build an authentication system. To build the backend, we will using the Django Framework and for frontend React JS framework.
What JSON Web Tokens (JWT)?
JSON Web Token is a open standard for transferring authorization details between client and server. In JWT all the information is self contained, we don’t need to store them in server.
For Example: A user comes to our web application and login with username and password. After verifying the credentials, sever provides the two JSON web tokens. One of them is an Access Token and other is Refresh Token. After get the token server stores the token and pass the access token in the authorization header. The server verify the token and identify the user. Access token is valid for short time (depending on the configurations). In such a case if token is invalid or expire, we will use the refresh token to generate the new access token.
Backend — Django
First, We will set up the project.
Create the Virtual environment:
## Create the Virtual Environment.python -m venv <Virtual Environment Name>
Activate the Virtual environment:
# Activate the Virtual Environment.<Virtual Environment Name>\scripts\activate
If you found any error to activate the virtual environment run the command before activate the virtual environment:
Set-ExecutionPolicy Unrestricted -Scope Process
After activate the virtual environment, we install the packages such as Django, Django REST framework, Django REST framework simple JWT and Django CORS headers. The DRF packages is used to create the API, while DRF simple JWT gives as ability to generate the JWT Token and Django CORS headers is used to avoid CORS related issues.
# Python Packagespip install Django
pip install djangorestframework
pip install djangorestframework-simplejwt
pip install django-cors-headers
Once this is done, We will create the Project.
# Create the Project.django-admin startproject backend
Then, we will create the first app inside the project.
# Create the app.python manage.py startapp authentification
Now, we have to change the project setting. Go into settings.py file and make the following modifications:
from datetime import timedelta.....INSTALLED_APPS = [
...........
# Register your app
'corsheaders',
'rest_framework',
'rest_framework_simplejwt.token_blacklist'
].....CORS_ORIGIN_ALLOW_ALL = True.....MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...........
].....REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}.....SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=10),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True
}
Let’s look what we have done. First, we have register our app name (You will write your app name), DRF, DRF simple JWT and CORS in INSTALLED_APPS. Second, we have set the CORS_ORIGIN_ALLOW_ALL = True and added the custom CORS header middleware in MIDDLEWARE to avoid the CORS related issue. Then, we have added the authentication JWT Class in REST_FRAMEWORK and added the SIMPLE_JWT token configurations to implement an access/refresh logic.
After add the configuration in settings.py file, we will create the URL for access token and refresh token in backend/urls.py file.
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt import views as jwt_viewsurlpatterns = [
.....
path('token/',
jwt_views.TokenObtainPairView.as_view(),
name ='token_obtain_pair'),
path('token/refresh/',
jwt_views.TokenRefreshView.as_view(),
name ='token_refresh')
]
Now, we are almost ready to test whether it is works or not. Before run the project we have to apply the migration and create the user. Run this command to apply the migration:
# Apply the migration.python manage.py migrate
Once this is done. we will create the user using this command:
# Create the user.python manage.py createsuperuser
Fill all the fields and now, we will start the development server using this command:
# Runserverpython manage.py runserver
After run the server, we will navigate to this http://localhost:8000/token/, we will see this page which contains the form with username and password field.
Fill this form with your created username and password, you should see the access and refresh token.
We all know, access token is invalid after some time. So by the use of refresh token we will generate the new access token and refresh token as well. To generate the new access token with the help of refresh token, we will navigate to this URL http://localhost:8000/token/refresh/, we will see this page which contains the form with Refresh field.
Fill this form with Refresh Token, you should see the new access and refresh token.
Now, we will create the views for testing. so, we will go in views.py file in our app directory and write the following code:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedclass HomeView(APIView):
permission_classes = (IsAuthenticated, ) def get(self, request): content = {'message': 'Welcome to the JWT
Authentication page using React Js and Django!'} return Response(content)
Let’s look what we have done. First, we have import the modules and created the class based view using simple APIView then, we have set the permission class for authenticated user. Second, we have created the get method and set the response.
Now, we will create the URL for HomeView. To create the URL first, we have to create the urls.py file in our app directory.
from django.urls import path
from . import viewsurlpatterns = [
path('home/', views.HomeView.as_view(), name ='home')
]
Second, we have to register the urls.py file path in our project directory urls.py file.
urlpatterns = [
......
path('', include('<app name>.urls')),
]
Now, we will use the POSTMAN to hit this URL http://localhost:8000/home/ with get request and we get authentication credential not provided message because in our HomeView we have set the permission class is IsAuthenticated means this page is only for authenticated user.
So, we have to provide the access token in authorization to get the result. Now again, we will create the get request with access token. To set the access token in POSTMAN, we have to click on Authorization button and set the token type is Bearer Token and paste the access token. After hit the API we will get this output:
Now, we have to add the logout functionality because we all know access token is valid for some time.
By the use of refresh token, we will generate the new access token. But, what happens when user wants to log out. The client can forgot both token. For example we have remove both the access and refresh token from localStorage. But, if refresh token is stolen, then another client use this token and use it. To prevent this, we will create the logout API, by using we can invalid the refresh token.
To create logout API, we will write the LogoutView in our views.py file in app directory:
class LogoutView(APIView): permission_classes = (IsAuthenticated,) def post(self, request):
try: refresh_token = request.data["refresh_token"] token = RefreshToken(refresh_token) token.blacklist() return Response(status=status.HTTP_205_RESET_CONTENT) except Exception as e: return Response(status=status.HTTP_400_BAD_REQUEST)
After create the LogoutView, we have to create the URL for LogoutView in our urls.py file in app directory.
urlpatterns = [
.....
path('logout/', views.LogoutView.as_view(), name ='logout')
]
By using that URL http://localhost:8000/logout/, we can invalid the refresh token and add token into blacklist. By using Postman we can hit this URL with POST request and pass the access token in authorization and refresh token in body.
Now all things is set on backend, We have tested all the API’s.
Frontend — React JS
There are many ways to connect the Django to your frontend but, In this tutorial we design the frontend using React JS Framework. We have already created the API’s for authentication.
Let’s setup the React app using this command:
// Create the react app.npx create-react-app frontend
Install the following packages:
// Install the packages.
npm i bootstrap
npm i axios
npm i react-router-dom
After setup the project run this command to run the react app:
# To run the react app.npm start
Then open http://localhost:3000/ to see your app.
After run the server successfully, we move further to design the application. So first, we will create the component folder inside the src folder.
After create the component folder in src we will create the login.js, logout.js, navigation.js and home.js. Basically, we have created the different file for different use.
So first, we will write the code for navigation bar in navigations.js file.
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import React, { useState, useEffect} from 'react';export function Navigation() { const [isAuth, setIsAuth] = useState(false); useEffect(() => { if (localStorage.getItem('access_token') !== null) { setIsAuth(true);
} }, [isAuth]); return (
<div>
<Navbar bg="dark" variant="dark">
<Navbar.Brand href="/">JWT Authentification</Navbar.Brand>
<Nav className="me-auto">
{isAuth ? <Nav.Link href="/">Home</Nav.Link> : null}
</Nav>
<Nav>
{isAuth ? <Nav.Link href="/logout">Logout</Nav.Link> :
<Nav.Link href="/login">Login</Nav.Link>}
</Nav>
</Navbar>
</div>
);
}
To deign the navigation page. First, we have imported all the packages and then define Navigation function. Inside the navigation function, we have used the useState to store the value in isAuth variable. Initially, we have set the value false. Then, used the useEffect and check access token is present or not in localStoarge. if it is not present than change isAuth variable value from false to true.
Then, we have return the HTML code to design the navigation bar and used the ternary operator condition which check isAuth is true or false. if it is then, it shows the Home and Logout button on navigation bar otherwise it show login button like this:
After written navigation page code, we have to add route condition in our app.js file.
import './App.css';
import {BrowserRouter, Routes, Route} from 'react-router-dom'
import {Login} from "./component/login";
import {Home} from "./component/Home";
import {Navigation} from './component/navigation';
import {Logout} from './component/logout';function App() { return
<BrowserRouter>
<Navigation></Navigation>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/login" element={<Login/>}/>
<Route path="/logout" element={<Logout/>}/>
</Routes>
</BrowserRouter>;
}
export default App;
In App.js file first, we have imported all the packages and files, then, written the route condition for login, logout and home page.
After design the Navigation, we will write the code for login functionality in login.js file.
// Import the react JS packages
import axios from "axios";
import {useState} from "react";// Define the Login function.
export const Login = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); // Create the submit method.
const submit = async e => { e.preventDefault(); const user = {
username: username,
password: password
}; // Create the POST requuest
const {data} = await
axios.post('http://localhost:8000/token/',
user ,{headers:
{'Content-Type': 'application/json'}},
withCredentials: true});
// Initialize the access & refresh token in localstorage.
localStorage.clear(); localStorage.setItem('access_token', data.access); localStorage.setItem('refresh_token', data.refresh); axios.defaults.headers.common['Authorization'] =
`Bearer ${data['access']}`; window.location.href = '/' } return( <div className="Auth-form-container">
<form className="Auth-form" onSubmit={submit}>
<div className="Auth-form-content">
<h3 className="Auth-form-title">Sign In</h3>
<div className="form-group mt-3">
<label>Username</label>
<input className="form-control mt-1"
placeholder="Enter Username"
name='username'
type='text' value={username}
required
onChange={e => setUsername(e.target.value)}/>
</div>
<div className="form-group mt-3">
<label>Password</label>
<input name='password'
type="password"
className="form-control mt-1"
placeholder="Enter password"
value={password}
required
onChange={e => setPassword(e.target.value)}/>
</div>
<div className="d-grid gap-2 mt-3">
<button type="submit"
className="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
)
}
Let’s look what we have done. First, we have initialize the react js packages. Second, we have create the function which name is Login. Inside the login function we have created the submit function. Inside the submit function we have written the axios post method to fetch the access token and refresh token. After fetch the token, we have store this tokens in local storage and navigate to the particular location which you want. For this case, I have route to my home URL. Then, we have design the Login form with username and password fields. Whenever, we click on submit button, the submit function is called and fetch the data from our backend login API. If it is successful than we get the access token and navigate to other page or home page otherwise it gives error in console.
After design the login page our page looks like:
After design the Login page, let’s move on to write the code for home page in our home.js file.
// Import the react JS packages
import {useEffect, useState} from "react";
import axios from "axios";
// Define the Login function.
export const Home = () => { const [message, setMessage] = useState(''); useEffect(() => { if(localStorage.getItem('access_token') === null){ window.location.href = '/login'
}
else{ (async () => { try { const {data} = await axios.get(
'http://localhost:8000/home/', {
headers: {
'Content-Type': 'application/json'
}}
); setMessage(data.message); } catch (e) { console.log('not auth')
} })()}; }, []); return <div className="form-signin mt-5 text-center">
<h3>Hi {message}</h3>
</div>
}
Let’s look what we have done. First, we have initialize the react js packages. Second, we have create the function which name is Home. Inside the Home we have used the useState to store the message data and useEffect which called when every time the component renders. Inside useEffect we have written if else condition to check the access token is present or not. If access token is null in localStorage then, we will redirect to login page otherwise if we have token, we will create the get request to fetch the data from home API and set the message. Then, We have written the HTML code to display the message and our home page looks like:
We have design the home page also but, we all know that our access token is valid for very short time. After short time when we refresh the page, we will get the 401 unauthorized error because our access token is invalid. But we also know that in backend, we have created the refresh token API, which will generate the new access token and refresh token.
But the problem is how we know or after how much time access token is invalid or not. To resolve this problem, we use the interceptor in react js or in other words we can say middleware. Basically, Interceptors are methods which are triggered before or after the main method. So in this case we have using the axios interceptor. There are two types of interceptors:
- Request interceptor: — It allows you to write or execute your code before the request gets sent.
- Response interceptor: — It allows you to write or execute your code before response reaches the calling end.
So in our case, we have using the response interceptor because, we have to check the response. To write the response interceptor code first, we make the interceptor folder. Inside the interceptor folder create the axiox.js file. Inside the axios.js file, we will write the code:
import axios from "axios";let refresh = false;axios.interceptors.response.use(resp => resp, async error => { if (error.response.status === 401 && !refresh) { refresh = true;
console.log(localStorage.getItem('refresh_token'))
const response = await
axios.post('http://localhost:8000/token/refresh/', {
refresh:localStorage.getItem('refresh_token')
}, { headers:
'Content-Type': 'application/json'
}
},{withCredentials: true}); if (response.status === 200) {
axios.defaults.headers.common['Authorization'] = `Bearer
${response.data['access']}`; localStorage.setItem('access_token', response.data.access); localStorage.setItem('refresh_token', response.data.refresh); return axios(error.config);
} }
refresh = false;return error;});
Let’s look what we have done. First, we have import the axios package and then, create the axios response interceptor. Now, Every single time a request is made with the axios instance, if there are no errors, it just works as expected.
When there is error occur first, we will check the error code. If the error code is 401, we need to refresh the token. To refresh the token, we will create the POST request and get the new access token and refresh token. After get the token, we will reinitialize the token in localStorage and send the original request.
After create the response interceptor. We have to the add the interceptor location in our index.js file.
import './interceptors/axios';
Now, we will write the code for logout functionality in logout.js file.
import {useEffect, useState} from "react"
import axios from "axios";export const Logout = () => { useEffect(() => { (async () => { try {
const {data} = await
axios.post('http://localhost:8000/logout/',{
refresh_token:localStorage.getItem('refresh_token')
} ,{headers: {'Content-Type': 'application/json'}},
{withCredentials: true}); localStorage.clear();\
axios.defaults.headers.common['Authorization'] = null;
window.location.href = '/login'
} catch (e) {
console.log('logout not working', e)
}
})();
}, []); return (
<div></div>
)
}
To add the logout functionality, we have declares the packages and use the useEffect hooks. Whenever we click on logout button, it call the backend logout API and remove all the tokens from local storage and navigate to login page.
This is some basic stuff if you need to build an authentication system with React and Django.
Conclusion:
In this article, we have learned to how to build the API’s with Django, designed the frontend part using React JS, call the API using axios and how to use response interceptor.
I hope this article has helped you.
Thanks for coding along with me!
Check the code of the Frontend and Backend on GitHub: https://github.com/Ronakchitlangya1997/Authentication---React-Js-Django