How to implement JSON Web Token (JWT) authentication

Loading

JWT (JSON Web Token) is a compact, URL-safe means of representing claims to be transferred between two parties. It is widely used for authentication in web applications, particularly in Single Page Applications (SPAs). In a typical JWT authentication flow, a client sends a request to the server with credentials, the server validates the credentials, and then generates a JWT that is sent back to the client. The client then includes this token in the header of subsequent requests to access protected routes.

In this guide, we’ll go through how to implement JWT authentication in a web application, covering both the back-end (server-side) and front-end (client-side) components.


1. Back-End Implementation (Server-Side)

We will use Node.js with Express and jsonwebtoken for the back-end implementation of JWT authentication.

1.1 Install Required Dependencies

First, you need to install some necessary packages. You can do this using npm.

npm init -y  # To initialize a Node.js project
npm install express jsonwebtoken bcryptjs dotenv
  • express: Web framework for building the server.
  • jsonwebtoken: A library to create and verify JWT tokens.
  • bcryptjs: For hashing passwords before storing them.
  • dotenv: To manage environment variables securely.

1.2 Setup Express Server

Create a file server.js and add the following code:

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const dotenv = require('dotenv');

dotenv.config(); // Load environment variables

const app = express();
app.use(express.json()); // Middleware for parsing JSON request bodies

// Simulate a simple user database
const users = [];

const SECRET_KEY = process.env.SECRET_KEY || 'your_jwt_secret_key';

// Route to register a user
app.post('/register', async (req, res) => {
const { username, password } = req.body;

// Hash password before saving it
const hashedPassword = await bcrypt.hash(password, 10);

const newUser = { username, password: hashedPassword };
users.push(newUser); // Add user to our simple database

res.status(201).send('User registered successfully!');
});

// Route to login and generate JWT
app.post('/login', async (req, res) => {
const { username, password } = req.body;

// Find user in the database
const user = users.find(u => u.username === username);

if (!user) {
return res.status(400).send('User not found');
}

// Compare provided password with the hashed password stored in the DB
const isMatch = await bcrypt.compare(password, user.password);

if (!isMatch) {
return res.status(400).send('Invalid credentials');
}

// Create JWT token
const token = jwt.sign({ username: user.username }, SECRET_KEY, { expiresIn: '1h' });

// Send the token to the client
res.json({ token });
});

// Protected route - only accessible with a valid token
app.get('/protected', (req, res) => {
const token = req.headers['authorization']?.split(' ')[1]; // Extract token from Authorization header

if (!token) {
return res.status(403).send('No token provided');
}

// Verify the token
jwt.verify(token, SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(403).send('Invalid or expired token');
}

// Token is valid, send protected data
res.json({ message: 'Protected content', username: decoded.username });
});
});

// Start server
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
  • The /register route registers users, hashing their passwords using bcryptjs.
  • The /login route authenticates users and generates a JWT token using jsonwebtoken.
  • The /protected route is a protected route that requires a valid JWT to access.

1.3 Environment Configuration (Optional)

Create a .env file to store your secret key:

SECRET_KEY=your_jwt_secret_key

Ensure you never expose this secret in your version control system.


2. Front-End Implementation (Client-Side)

Now, let’s set up the front-end to interact with the backend. We will use AngularJS for this example, but you can implement it in any front-end framework like React, Vue, or just vanilla JavaScript.

2.1 Install Axios (for HTTP requests)

npm install axios

Axios is a popular library for making HTTP requests.

2.2 Implement the Client-Side Authentication Flow

Here is an example of how you can implement JWT authentication on the client-side using AngularJS (you can adapt it to your own framework).

app.js
angular.module('jwtApp', [])
.controller('AuthController', function($scope, $http) {
const apiUrl = 'http://localhost:3000'; // Backend API URL

// Register user
$scope.register = function() {
const userData = {
username: $scope.username,
password: $scope.password
};

$http.post(`${apiUrl}/register`, userData)
.then(response => {
console.log('User registered:', response);
alert('User registered successfully!');
})
.catch(error => {
console.error('Registration failed:', error);
alert('Registration failed');
});
};

// Login user and get JWT
$scope.login = function() {
const loginData = {
username: $scope.username,
password: $scope.password
};

$http.post(`${apiUrl}/login`, loginData)
.then(response => {
console.log('JWT received:', response.data.token);
localStorage.setItem('jwt', response.data.token); // Store JWT token in localStorage
alert('Login successful!');
})
.catch(error => {
console.error('Login failed:', error);
alert('Login failed');
});
};

// Access protected route with JWT
$scope.getProtectedData = function() {
const token = localStorage.getItem('jwt');

if (!token) {
alert('No token found, please login');
return;
}

$http.get(`${apiUrl}/protected`, {
headers: {
'Authorization': `Bearer ${token}` // Add the token to the request header
}
})
.then(response => {
console.log('Protected data:', response.data);
alert('Protected data: ' + JSON.stringify(response.data));
})
.catch(error => {
console.error('Access denied:', error);
alert('Access denied: Invalid or expired token');
});
};
});
  • register(): Sends a POST request to register a user.
  • login(): Sends a POST request to authenticate the user and receive a JWT token.
  • getProtectedData(): Sends a GET request to a protected route, passing the JWT token in the Authorization header.

3. Secure the JWT Token

The JWT token should be kept secure on the client side. Here are a few recommendations:

  • Store the JWT Token in localStorage or sessionStorage: This allows the client to send the token with each request. You can also use cookies if you prefer.
  • Use HTTPS: Always serve your application over HTTPS to protect the token during transmission.
  • Token Expiry: JWT tokens usually have an expiration time. After expiry, the user will need to authenticate again. This can be handled by checking the expiration in the backend and sending an appropriate error response (401 Unauthorized).

Leave a Reply

Your email address will not be published. Required fields are marked *