Authentication and Authorization in Node.js

Arunangshu Das
4 min readJun 24, 2024

--

Authentication and Authorization in Node.js

In the digital age, securing web applications is paramount. Two core concepts to achieving this are authentication and authorization. Although often used interchangeably, they serve distinct purposes. Authentication verifies who a user is, while authorization determines what a user is allowed to do. Understanding and implementing these concepts correctly in your Node.js applications is critical for maintaining robust security.

Understanding Authentication

What is Authentication?

Authentication is the process of verifying the identity of a user. It’s like a digital handshake where the user proves their identity by providing credentials, typically a username and password.

Common Authentication Methods in Node.js

  1. Basic Authentication:
  • The simplest form of authentication.
  • Users send their credentials (username and password) with each request.
  • Implemented via HTTP headers, this method is straightforward but not the most secure.

2. Token-based Authentication:

  • Users authenticate with their credentials, and the server responds with a token.
  • Subsequent requests include this token for access, reducing the need to resend credentials.
  • Commonly implemented with JWT (JSON Web Tokens).

3. OAuth (Open Authorization):

  • A protocol that allows third-party services to exchange information without exposing passwords.
  • Widely used by platforms like Google, Facebook, and Twitter for login.

4. Session-based Authentication:

  • Upon successful login, the server creates a session for the user and stores it on the server.
  • The session ID is sent to the client, usually stored as a cookie.
  • Subsequent requests use this session ID to validate the user’s identity.

Implementing Authentication in Node.js

To illustrate how to implement authentication, we will use JWT, a popular method due to its stateless nature and ease of use.

Step 1: Setup Node.js Project

First, create a new Node.js project:

mkdir auth-demo
cd auth-demo
npm init -y

Step 2: Install Required Packages

Install necessary packages such as express for creating the server, jsonwebtoken for handling JWTs, and bcryptjs for hashing passwords.

npm install express jsonwebtoken bcryptjs body-parser

Step 3: Create Authentication Middleware

Create a simple express server and middleware to handle user authentication.

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

const users = []; // In-memory user storage for demo purposes.

app.post('/register', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 8);
users.push({ username, password: hashedPassword });
res.status(201).send({ message: 'User registered successfully!' });
});

app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(user => user.username === username);
if (!user) return res.status(400).send({ message: 'User not found' });

const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) return res.status(401).send({ message: 'Invalid password' });

const token = jwt.sign({ id: user.username }, 'secret-key', { expiresIn: '1h' });
res.status(200).send({ token });
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

Step 4: Protecting Routes

Use middleware to protect routes that require authentication:

const authMiddleware = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send({ message: 'No token provided' });

jwt.verify(token, 'secret-key', (err, decoded) => {
if (err) return res.status(500).send({ message: 'Failed to authenticate token' });
req.userId = decoded.id;
next();
});
};

app.get('/protected', authMiddleware, (req, res) => {
res.status(200).send({ message: 'You have access to this protected route!' });
});

Understanding Authorization

What is Authorization?

Authorization is the process of determining what actions a user can perform or what resources they can access. It’s a set of rules that dictate the permissions of authenticated users.

Authorization Strategies in Node.js

  1. Role-based Access Control (RBAC):
  • Users are assigned roles, and permissions are granted based on these roles.
  • Simplifies permission management, especially in systems with well-defined roles like admin, user, and guest.

2. Attribute-based Access Control (ABAC):

  • Uses attributes (user, resource, environment) to make access decisions.
  • More flexible than RBAC but also more complex to implement.

3. Access Control Lists (ACLs):

  • Permissions are specified for each user-resource pair.
  • Provides granular control but can become unwieldy in large systems.

Implementing Authorization in Node.js

Continuing from our previous example, we can extend the authMiddleware to check user roles:

const roles = {
admin: ['read', 'write', 'delete'],
user: ['read']
};

const roleMiddleware = (requiredRole) => {
return (req, res, next) => {
const userRole = getUserRole(req.userId); // Implement this function to retrieve user role.
if (roles[userRole].includes(requiredRole)) {
next();
} else {
res.status(403).send({ message: 'Forbidden: You do not have access to this resource' });
}
};
};

app.get('/admin', authMiddleware, roleMiddleware('write'), (req, res) => {
res.status(200).send({ message: 'Welcome Admin!' });
});

Best Practices for Secure Authentication and Authorization

  1. Use Strong Passwords and Hashing:
  • Always hash passwords before storing them.
  • Use a library like bcrypt to hash passwords with a secure algorithm.

2. Implement Token Expiration and Refresh Mechanisms:

  • JWT tokens should have expiration times to mitigate risks.
  • Implement a refresh token mechanism to allow users to obtain a new token without re-authenticating.

3. Secure Your Communication:

  • Use HTTPS to secure data in transit.
  • Ensure that sensitive data like tokens are not exposed in URLs or logs.

4. Regularly Update Dependencies:

  • Keep your packages up-to-date to avoid vulnerabilities.
  • Use tools like npm audit to check for known vulnerabilities.

5. Implement Least Privilege:

  • Grant users the minimum permissions they need to perform their tasks.
  • Regularly review and adjust permissions.

6. Monitor and Log Activities:

  • Keep logs of authentication attempts and access to sensitive resources.
  • Monitor these logs for suspicious activities.

Conclusion

Implementing robust authentication and authorization is vital for securing your Node.js applications. With Node.js, a plethora of tools and libraries are available to help you build a secure and scalable system, ensuring that only the right users have access to the right resources.

Follow me on Linkedin

--

--