- vừa được xem lúc

Blog#204: 🔐JSON Web Tokens (JWT) for Secure Authentication and Authorization in Node.js Express

0 0 4

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

204

Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.

Introduction to JSON Web Tokens (JWT)

JSON Web Tokens (JWT) is an open standard (RFC 7519) that defines a compact and self-contained method for securely transmitting information between parties as a JSON object. JWTs are particularly useful for authentication and authorization, as they allow a server to verify a client's identity and grant access to protected resources based on the client's claims or permissions.

In this article, we will explore the use of JWT for secure authentication and authorization in a Node.js Express application. We will discuss the following topics:

  1. How JWT works
  2. Setting up a Node.js Express application
  3. Implementing JWT-based authentication
  4. Implementing JWT-based authorization
  5. Best practices and security considerations

How JWT Works

Structure of a JWT

A JSON Web Token consists of three parts: the header, the payload, and the signature. These three parts are base64Url encoded, concatenated with a period (.) separator, and form the complete JWT as a string. The structure of a JWT is as follows:

header.payload.signature

Header

The header typically contains two properties:

  • alg: The signing algorithm being used, such as HMAC SHA256 (HS256) or RSA (RS256).
  • typ: The token's type, usually set to "JWT".

A sample header in JSON format:

{ "alg": "HS256", "typ": "JWT"
}

Payload

The payload contains the claims, which are statements about the subject (e.g., user) and additional metadata. There are three types of claims:

  • Registered claims: Predefined claims such as iss (issuer), exp (expiration time), sub (subject), and aud (audience).
  • Public claims: Custom claims agreed upon by both parties. To avoid collisions, they should be registered in the IANA JSON Web Token Registry or use a collision-resistant naming convention.
  • Private claims: Custom claims used between the two parties and not intended for public consumption.

A sample payload in JSON format:

{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022
}

Signature

The signature is used to verify the integrity of the token. It is generated by combining the encoded header, the encoded payload, a secret, and the algorithm specified in the header. For example, with the HMAC SHA256 algorithm:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret
)

Token Verification Process

When a client sends a JWT to the server, the server verifies the token's signature by decoding the JWT and recalculating the signature using the same secret or private key used during token creation. If the recalculated signature matches the one in the JWT, the server can trust the token's contents.

Setting Up a Node.js Express Application

To start, we need to set up a Node.js Express application. First, ensure you have Node.js and npm installed. Then, create a new directory for the project and initialize it with npm init. After answering the prompts, install the required dependencies:

npm install express jsonwebtoken bcryptjs body-parser dotenv

Create an .env file to store sensitive information, such as the JWT secret and the password salt rounds:

JWT_SECRET=my_jwt_secret
SALT_ROUNDS=10

Implementing JWT-based Authentication

User Registration

In this example, we will use a simple in-memory storage for user data. In a production environment, you would typically use a database for persistent storage. First, create a file named users.js with the following content:

const users = []; module.exports = users;

Next, create an authController.js file to handle user registration and authentication. Import the required dependencies and the users array:

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const dotenv = require('dotenv');
const users = require('./users'); dotenv.config();

Now, create a function to handle user registration. This function will hash the user's password using bcryptjs and store the user's information in the users array:

const register = async (req, res) => { const { username, password } = req.body; // Check if the user already exists const userExists = users.find((user) => user.username === username); if (userExists) { return res.status(400).send('User already exists'); } // Hash the password const salt = await bcrypt.genSalt(parseInt(process.env.SALT_ROUNDS)); const hashedPassword = await bcrypt.hash(password, salt); // Store the user const newUser = { username, password: hashedPassword }; users.push(newUser); res.status(201).send('User registered successfully');
};

User Authentication

Create a function to authenticate users by comparing the submitted password with the stored hash. If the password is correct, generate a JWT and return it to the client:

const authenticate = async (req, res) => { const { username, password } = req.body; // Find the user const user = users.find((user) => user.username === username); if (!user) { return res.status(404).send('User not found'); } // Verify the password const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { return res.status(401).send('Invalid credentials'); } // Create a JWT const token = jwt.sign({ username: user.username }, process.env.JWT_SECRET); res.status(200).json({ token });
};

Finally, export the register and authenticate functions:

module.exports = { register, authenticate,
};

Implementing JWT-based Authorization

Create a middleware function in a new file named authMiddleware.js to verify the JWT in incoming requests:

const jwt = require('jsonwebtoken');
const dotenv = require('dotenv'); dotenv.config(); const verifyToken = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).send('Access denied: No token provided'); } const token = authHeader.split(' ')[1]; try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (error) { res.status(400).send('Invalid token'); }
}; module.exports = verifyToken;

Now you can use this middleware to protect your routes. For example, create a protected route in a new file named routes.js:

const express = require('express');
const router = express.Router();
const verifyToken = require('./authMiddleware'); router.get('/protected', verifyToken, (req, res) => { res.send('Access granted: You are authenticated');
}); module.exports = router;

Wiring Up the Application Create an app.js file to wire up the application. Import the required dependencies, the authentication controller, and the routes:

const express = require('express');
const bodyParser = require('body-parser');
const authController = require('./authController');
const routes = require('./routes'); const app = express();
const port = process.env.PORT || 3000;

Set up the Express middleware, register the authentication routes, and use the protected routes:

app.use(bodyParser.json()); // Authentication routes
app.post('/register', authController.register);
app.post('/authenticate', authController.authenticate); // Protected routes
app.use('/', routes);

Start the Express server:

app.listen(port, () => { console.log(`Server running on port ${port}`);
});

Now, you can run the application with node app.js and use an API client like Postman to test the /register, /authenticate, and /protected endpoints.

Best Practices and Security Considerations

  1. Store the JWT secret securely: Use environment variables, a secrets manager, or a configuration management tool to store the JWT secret securely.
  2. Use HTTPS: To protect JWTs from being intercepted during transmission, always use HTTPS for communication between the client and server.
  3. Set an appropriate expiration time: Keep the JWT's lifetime short to reduce the risk of misuse. You can set the exp claim to an appropriate value when creating the JWT.
  4. Handle token revocation: Implement a mechanism to revoke tokens, such as using a token blacklist or implementing a token introspection endpoint.
  5. Validate input: Always validate user input on both the client and server sides to prevent injection attacks and other vulnerabilities.
  6. Implement proper error handling: Properly handle errors and avoid disclosing sensitive information in error messages.

Conclusion

In this article, we explored how to use JSON Web Tokens for secure authentication and authorization in a Node.js Express application. We discussed the structure of JWTs, implemented user registration and authentication, and protected routes using JWT-based authorization middleware. Additionally, we covered best practices and security considerations to ensure the secure use of JWTs in your application.

And Finally

As always, I hope you enjoyed this article and got something new. Thank you and see you in the next articles!

If you liked this article, please give me a like and subscribe to support me. Thank you. 😊

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

Cách mình "hack" được vào hẹ thống của SMAS để xem điểm.

Cách mà mình "hack" được vào hệ thống của SMAS. Thật ra dùng từ hack cũng không đúng lắm, chỉ là một vài trick để lừa hệ thống mà thôi.

0 0 125

- vừa được xem lúc

[NodeJs] Tạo QR Code trong nodeJs với qrcode

Tạo mã QR Code trong nodejs với qrcode. QR Code là gì. Tạo QR code với qrcode. Cài đặt thư viện qrcode.

0 0 21

- vừa được xem lúc

Áp dụng kiến trúc 3 Layer Architecture vào project NodeJS

The problem encountered. Các framework nodejs phổ biết như Express cho phép chúng ta dễ dàng tạo ra Resful API xử lí các request từ phía client một cách nhanh chóng và linh hoạt.

0 0 64

- vừa được xem lúc

Router, Controller trong Express

Mở đầu. Xin chào các bạn mình đã quay trở lại rồi đây, tiếp tục với series Nodejs cơ bản thì hôm nay mình sẽ giới thiệu đến các bạn Express Router và Controller.

0 0 30

- vừa được xem lúc

Xây dựng CRUD RESTful API sử dụng Node, Express, MongoDB.

Introduction. Trong phạm vi bài viết này chúng ta sẽ cùng tìm hiểu về cách tạo restful api với Node, Express và MongoDB. . Xử lý các hoạt động crud.

0 0 213

- vừa được xem lúc

Rate time limit trong NodeJS

Chào các bạn, lại là mình đây. Hôm nay mình xin giới thiệu tới các bạn một kỹ thuật rất hay ho và hữu ích đó là Rate Limiting. 1. Rate Limiting là gì.

0 0 49