Implementing JWT Authentication in Express API
JWT (JSON Web Token) is a popular and efficient method for securing APIs through token-based authentication, ensuring that only authenticated users can access your API endpoints. Unlike traditional session-based methods, JWT is stateless, meaning it doesn't require server-side session storage, making it ideal for scalable and distributed systems. In this guide, we'll walk you through implementing JWT authentication in an Express API, covering everything from generating tokens during user login to protecting your endpoints by validating these tokens, ultimately boosting the security of your application's data and services.
Prerequisites
- Node.js
Setup project
npm install express jsonwebtoken
Project structure
├─ config.js
├─ index.js
├─ middleware
│ └─ authenticate.js
└─ public
├─ index.html
└─ login.html
Project files
config.js
module.exports = {
jwt_secret: 'b0WciedNJvFCqFRbB2A1QhZoCDnutAOen5g1FEDO0HsLTwGINp04GXh2OXVpTqQL'
}
The config.js
file exports a configuration object containing a jwt_secret
property. It is a string used to sign and verify JWT tokens in the application.
authenticate.js
const jwt = require('jsonwebtoken')
const jwtSecret = require('../config').jwt_secret
module.exports = (req, res, next) => {
if (req.originalUrl == '/login') {
next()
}
else {
let header = req.headers['authorization']
if (!header || !header.startsWith('Bearer ')) {
return res.sendStatus(401)
}
else {
let token = header.substr(7)
jwt.verify(token, jwtSecret, (error, user) => {
if (error) {
return res.sendStatus(401)
}
req.user = user
next()
})
}
}
}
The authenticate.js
file is a middleware function that handles JWT authentication for an Express API. It first checks if the incoming request is to the /login
route, which is exempt from authentication. For other routes, the middleware checks the Authorization
header for a valid JWT token prefixed with "Bearer ". If the token is missing or improperly formatted, it returns a 401 Unauthorized response. If the token is present, it is verified using the secret key from the config.js
file. If the token is valid, the decoded user information is attached to the req.user
object, and the request proceeds to the next middleware or route handler. If the token is invalid or expired, a 401 Unauthorized response is sent.
index.js
const express = require('express')
const jwt = require('jsonwebtoken')
const jwtSecret = require('./config').jwt_secret
const authenticate = require('./middleware/authenticate')
let app = express()
app.use(express.json(), express.static('public'), authenticate)
app.get('/user', (req, res) => {
res.send({
name: req.user.name,
})
})
app.post('/login', (req, res) => {
if (req.body.name == 'admin' && req.body.password == '1234') {
let token = jwt.sign({ id: 1, name: 'admin' }, jwtSecret, { expiresIn: '1d' })
return res.send({ token })
}
res.status(400).send()
})
app.listen(8000)
The index.js
file configures an Express server that handles JWT authentication. It uses middleware to parse JSON requests, serve static files from a public
directory, and apply JWT authentication to protected routes. The server defines a login route where users can authenticate and receive a token, and a user route that requires a valid token to access user data. The app listens for incoming requests on port 8000.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col mt-5">
<div class="d-flex justify-content-center">
<div class="card p-0">
<div class="card-header">
<h3>Home</h3>
</div>
<div class="card-body text-center">
<i id="icon" class="fa fa-times-circle fa-5x mt-3 text-danger"></i>
<p id="message" class="mt-3">
You are currently not logged in.
<br/>
Redirecting to login page in seconds..
</p>
<div id="logout" class="col-12 d-none">
<button class="btn btn-primary w-100" onclick="logout()">Logout</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function init() {
fetch('/user', {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('token')
}
}).then(response => {
if (response.ok) {
response.json().then(data => {
document.getElementById('icon').className = 'fa fa-check-circle fa-5x mt-3 text-success'
document.getElementById('message').innerText = 'You are currently logged in as: ' + data.name
document.getElementById('logout').classList.remove('d-none')
})
}
else {
if (response.status === 401) {
setTimeout(() => {
location = '/login.html'
}, 4000)
}
else {
alert(`Error: ${response.status} ${response.statusText}`)
}
}
})
}
function logout() {
localStorage.removeItem('token')
location.reload()
}
init()
</script>
</body>
The index.html
is a simple web page that provides a user interface for displaying the login status of a user. It uses Bootstrap for styling and Font Awesome for icons. On page load, it checks the user's authentication status by sending a request to the server with a JWT token stored in localStorage
. If the user is logged in, it shows a success message with the user's name and a logout button. If not logged in, it shows a message indicating the user is not logged in and redirects them to the login page after a few seconds.
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col mt-5">
<div class="d-flex justify-content-center">
<div class="card p-0" style="width: 300px;">
<div class="card-header">
<h3>Login</h3>
</div>
<div class="card-body">
<i class="fa fa-user-circle fa-5x d-block text-center text-secondary"></i>
<form onsubmit="return login()">
<div class="row">
<div class="mb-3 col-12">
<label class="form-label" for="user_account_name">User Name</label>
<input id="name" class="form-control form-control" value="admin" required/>
</div>
<div class="mb-3 col-12">
<label class="form-label" for="user_account_password">Password</label>
<input id="password" class="form-control form-control" type="password" value="1234" required/>
</div>
<div class="col-12">
<button class="btn btn-primary w-100">Login</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function login() {
fetch('/login', {
method: 'POST',
body: JSON.stringify({
name: document.getElementById('name').value,
password: document.getElementById('password').value
}),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
if (response.ok) {
response.json().then(data => {
localStorage.setItem('token', data.token)
location = '/'
})
}
else {
alert(`Error: ${response.status} ${response.statusText}`)
}
})
return false
}
</script>
</body>
The login.html
page provides a simple login form where users can input their username and password. It uses Bootstrap for styling and Font Awesome for icons. When the user submits the form, a JavaScript function login()
sends a POST
request to the /login
endpoint with the entered credentials. If the login is successful, the server returns a JWT token, which is stored in localStorage
. The page then redirects the user to the home page (/
). If the login fails, an error message is displayed.
Run project
node index.js
Open the web browser and goto http://localhost:8000
You will find this test page.
Testing
After a few seconds, you will be redirected to the login page.
Press the login button, and you will be logged in to the home page, which will display the logged-in user's name.
Try refreshing the browser, and you will see that you're still logged in. Then, press the logout button, the JWT token will be removed, and you will be redirected to the login page again.
Conclusion
In conclusion, implementing JWT authentication in an Express API provides a secure and scalable way to manage user authentication. By using the jsonwebtoken
package, we can easily sign and verify tokens, ensuring that only authorized users can access protected routes. The Express server is configured to handle user login, generate tokens, and secure routes with middleware that checks the validity of the JWT. This setup enhances the API's security by ensuring that sensitive data is only accessible to authenticated users. With JWTs, the system is well-suited for building stateless, scalable applications.
Source code: https://github.com/stackpuz/Example-JWT-Express
Create a CRUD Web App in Minutes: https://stackpuz.com