Introduction
JSON Web Tokens (JWT) are a popular method for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs are commonly used for authentication and authorization in web applications.
Key Concepts
-
JWT Structure: A JWT is composed of three parts:
- Header: Contains metadata about the token, such as the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256).
- Payload: Contains the claims, which are statements about an entity (typically, the user) and additional data.
- Signature: Used to verify the token's integrity and authenticity.
-
Claims: Claims are statements about an entity (typically, the user) and additional data. There are three types of claims:
- Registered claims: Predefined claims that are not mandatory but recommended, such as
iss
(issuer),exp
(expiration time),sub
(subject), andaud
(audience). - Public claims: Custom claims that can be defined by the user.
- Private claims: Custom claims created to share information between parties that agree on using them.
- Registered claims: Predefined claims that are not mandatory but recommended, such as
-
Signing Algorithms: JWTs can be signed using a secret (with HMAC algorithm) or a public/private key pair (with RSA or ECDSA).
Practical Example
Step 1: Setting Up the Project
First, create a new Node.js project and install the necessary dependencies.
mkdir jwt-authentication cd jwt-authentication npm init -y npm install express jsonwebtoken body-parser
Step 2: Creating the Server
Create a file named server.js
and set up a basic Express server.
const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const app = express(); const PORT = 3000; const SECRET_KEY = 'your_secret_key'; app.use(bodyParser.json()); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
Step 3: Creating JWTs
Add a route to generate a JWT when a user logs in.
app.post('/login', (req, res) => { const { username, password } = req.body; // In a real application, you would verify the username and password if (username === 'user' && password === 'password') { const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' }); return res.json({ token }); } res.status(401).json({ message: 'Invalid credentials' }); });
Step 4: Verifying JWTs
Add a middleware function to verify the JWT for protected routes.
const authenticateJWT = (req, res, next) => { const token = req.header('Authorization'); if (token) { jwt.verify(token, SECRET_KEY, (err, user) => { if (err) { return res.sendStatus(403); } req.user = user; next(); }); } else { res.sendStatus(401); } }; app.get('/protected', authenticateJWT, (req, res) => { res.json({ message: 'This is a protected route', user: req.user }); });
Full Code Example
Here is the complete server.js
file:
const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const app = express(); const PORT = 3000; const SECRET_KEY = 'your_secret_key'; app.use(bodyParser.json()); app.post('/login', (req, res) => { const { username, password } = req.body; // In a real application, you would verify the username and password if (username === 'user' && password === 'password') { const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' }); return res.json({ token }); } res.status(401).json({ message: 'Invalid credentials' }); }); const authenticateJWT = (req, res, next) => { const token = req.header('Authorization'); if (token) { jwt.verify(token, SECRET_KEY, (err, user) => { if (err) { return res.sendStatus(403); } req.user = user; next(); }); } else { res.sendStatus(401); } }; app.get('/protected', authenticateJWT, (req, res) => { res.json({ message: 'This is a protected route', user: req.user }); }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
Practical Exercises
Exercise 1: Implementing Logout
Task: Implement a logout route that invalidates the JWT.
Solution: In a stateless JWT implementation, you can't directly invalidate a token. However, you can implement token blacklisting or change the secret key periodically.
Exercise 2: Adding More Claims
Task: Add more claims to the JWT payload, such as role
and email
.
Solution:
Modify the login
route to include additional claims.
app.post('/login', (req, res) => { const { username, password } = req.body; if (username === 'user' && password === 'password') { const token = jwt.sign({ username, role: 'admin', email: '[email protected]' }, SECRET_KEY, { expiresIn: '1h' }); return res.json({ token }); } res.status(401).json({ message: 'Invalid credentials' }); });
Common Mistakes and Tips
- Not using HTTPS: Always use HTTPS to protect the token during transmission.
- Storing JWT in local storage: It's safer to store JWTs in HTTP-only cookies to prevent XSS attacks.
- Not handling token expiration: Ensure your application handles token expiration gracefully and prompts the user to re-authenticate.
Conclusion
In this section, you learned about JWT authentication, how to create and verify JWTs, and how to protect routes using JWTs. This knowledge is crucial for building secure web applications. In the next section, you will learn about role-based access control to further enhance your application's security.
Node.js Course
Module 1: Introduction to Node.js
Module 2: Core Concepts
Module 3: File System and I/O
Module 4: HTTP and Web Servers
Module 5: NPM and Package Management
- Introduction to NPM
- Installing and Using Packages
- Creating and Publishing Packages
- Semantic Versioning
Module 6: Express.js Framework
- Introduction to Express.js
- Setting Up an Express Application
- Middleware
- Routing in Express
- Error Handling
Module 7: Databases and ORMs
- Introduction to Databases
- Using MongoDB with Mongoose
- Using SQL Databases with Sequelize
- CRUD Operations
Module 8: Authentication and Authorization
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with Mocha and Chai
- Integration Testing
- Debugging Node.js Applications
Module 10: Advanced Topics
Module 11: Deployment and DevOps
- Environment Variables
- Using PM2 for Process Management
- Deploying to Heroku
- Continuous Integration and Deployment