Skip to content

Instantly share code, notes, and snippets.

@mahmoudalnkeeb
Created May 21, 2024 18:44
Show Gist options
  • Select an option

  • Save mahmoudalnkeeb/365a0aa0ba448e182488e9124c11fc76 to your computer and use it in GitHub Desktop.

Select an option

Save mahmoudalnkeeb/365a0aa0ba448e182488e9124c11fc76 to your computer and use it in GitHub Desktop.

JWT-Based Authentication and Authorization System in Node.js

1. User Registration:

  • Users provide registration details (e.g., username, email, password) through a registration form.
  • Server receives the registration request, validates the input data, and securely hashes the password before storing it in the database.
  • Upon successful registration, the server responds with a success message or redirects the user to the login page.

2. User Authentication:

  • Users provide login credentials (username/email and password) through a login form. The server validates the credentials, checks the user's existence, and verifies the provided password against the stored hashed password.
  • If authentication is successful, the server generates a JWT token containing the user's ID and any necessary claims signs it with a secret key, and sends it back to the client.
  • The client stores the JWT token (usually in local storage or a cookie) for subsequent requests.

3. Accessing Protected Routes:

  • Client includes the JWT token in the Authorization header of HTTP requests to access protected routes.
  • Server middleware intercepts the requests to protected routes and verifies the JWT token's authenticity, signature, and expiration.
  • If the token is valid, the server extracts the user's ID from the token and attaches it to the request object for further processing.
  • If the token is invalid or expired, the server responds with a 401 Unauthorized error, prompting the client to reauthenticate.

4. Authorization:

  • Server middleware checks the user's permissions and role based on the user's ID extracted from the JWT token.
  • If the user has the necessary permissions, the server allows access to the requested resource or endpoint.
  • If the user lacks sufficient permissions, the server responds with a 403 Forbidden error, indicating that the user is not authorized to access the resource.

5. Token Refresh (Optional):

  • Client periodically checks the JWT token's expiration time.
  • If the token is about to expire, the client sends a token refresh request to the server.
  • Server validates the existing JWT token, generates a new token with a later expiration time, and sends it back to the client.
  • The client replaces the old token with the new one for future requests.

6. Logging Out:

  • When the user logs out, the client clears the stored JWT token.
  • Optionally, the server may invalidate the JWT token on logout by maintaining a blacklist of revoked tokens or by implementing token expiration strategies.

Additional Considerations:

  • Install necessary packages (jsonwebtoken for JWT operations, bcrypt for password hashing).
  • Securely store sensitive information like passwords (hashed) and JWT secret keys using environment variables.
  • Use HTTPS to encrypt data transmitted between the client and server to ensure security.
  • Implement rate limiting to prevent brute-force attacks on the authentication endpoint.
  • Implement logging and monitoring for security auditing purposes.
  • Follow security best practices such as using strong JWT secret keys and regularly updating dependencies.

By following these steps and best practices, you can design a robust and secure JWT-based authentication and authorization system in Node.js.

// Import necessary packages
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');

// Initialize Express app
const app = express();
app.use(express.json());

// Mock database for demonstration purposes
const users = [];

// Secret key for JWT
const JWT_SECRET = 'your_secret_key';

// Middleware to verify JWT token
function verifyToken(req, res, next) {
    const token = req.headers.authorization;

    if (!token) {
        return res.status(401).json({ message: 'Unauthorized: No token provided' });
    }

    jwt.verify(token, JWT_SECRET, (err, decoded) => {
        if (err) {
            return res.status(401).json({ message: 'Unauthorized: Invalid token' });
        }
        req.user = decoded;
        next();
    });
}

// Register route
app.post('/register', async (req, res) => {
    try {
        const { username, password } = req.body;
        const hashedPassword = await bcrypt.hash(password, 10);
        const user = { username, password: hashedPassword };
        users.push(user);
        res.status(201).json({ message: 'User registered successfully' });
    } catch (error) {
        res.status(500).json({ message: 'Internal server error' });
    }
});

// Login route
app.post('/login', async (req, res) => {
    try {
        const { username, password } = req.body;
        const user = users.find(user => user.username === username);
        if (!user) {
            return res.status(401).json({ message: 'Invalid username or password' });
        }
        if (await bcrypt.compare(password, user.password)) {
            const token = jwt.sign({ username: user.username }, JWT_SECRET);
            res.json({ token });
        } else {
            res.status(401).json({ message: 'Invalid username or password' });
        }
    } catch (error) {
        res.status(500).json({ message: 'Internal server error' });
    }
});

// Protected route
app.get('/profile', verifyToken, (req, res) => {
    res.json({ message: 'Protected route accessed successfully', user: req.user });
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

In this example:

  • We use Express.js to handle HTTP requests.
  • We use bcrypt for password hashing and jsonwebtoken for JWT operations.
  • The /register route handles user registration by hashing the password and storing it in the users array (mock database).
  • The /login route authenticates users by comparing the hashed password with the stored password. If the credentials are valid, a JWT token is generated and sent back to the client.
  • The /profile route is a protected route that requires a valid JWT token for access. The verifyToken middleware verifies the token before granting access to the route.
  • The server listens on port 3000 by default.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment