How To Build a Simple Authentication System

ebook include PDF & Audio bundle (Micro Guide)

$12.99$5.99

Limited Time Offer! Order within the next:

We will send Files to your email. We'll never share your email with anyone else.

Authentication is one of the key components of modern web applications. Without a secure authentication system, sensitive user data could be exposed to attackers, and unauthorized users could gain access to protected resources. In this article, we'll discuss how to build a simple authentication system from scratch. We'll cover concepts such as user login, password hashing, token-based authentication, and session management.

While building a full-fledged authentication system involves various complexities, this guide will focus on creating a basic yet secure system that can be further customized and expanded upon.

Understanding Authentication

Authentication refers to the process of verifying a user's identity. When you build an authentication system, your goal is to confirm that a user is who they say they are. This process often involves checking credentials, such as usernames and passwords.

There are several common authentication methods, such as:

  • Username and Password: This is the most traditional form of authentication. It involves a user submitting their username and password, which the system then verifies.
  • Two-Factor Authentication (2FA): This adds an extra layer of security by requiring the user to provide a second form of identification, usually a temporary code sent to their phone.
  • OAuth: This allows users to log in using their credentials from other services, such as Google or Facebook.
  • JWT (JSON Web Tokens): This method uses tokens instead of session IDs to authenticate users and is especially popular for API-based authentication.

For this guide, we will focus on a simple username and password-based authentication system with token-based authentication (using JSON Web Tokens or JWT).

Key Concepts

Before diving into the code, let's first cover some important concepts we will use throughout the process:

1. Password Hashing

Storing passwords in plaintext is a huge security risk. In case of a data breach, attackers would have access to all users' passwords. Instead of storing plaintext passwords, we hash them using a secure hashing algorithm. A hash is a one-way function, which means it's nearly impossible to reverse the process and retrieve the original password from the hash.

Common hashing algorithms used in authentication systems include:

  • bcrypt
  • PBKDF2
  • Argon2

In this tutorial, we'll use bcrypt because it's both secure and computationally intensive, making it resistant to brute-force attacks.

2. JWT (JSON Web Token)

JSON Web Tokens (JWT) are compact, URL-safe tokens used for securely transmitting information between parties. A JWT can be signed using a secret key and optionally encrypted to ensure that the data it contains is both secure and verifiable.

JWTs are commonly used for API-based authentication, where a user logs in and receives a token. This token is then included in the HTTP header for subsequent requests to authenticate the user without requiring them to log in repeatedly.

A JWT typically consists of three parts:

  • Header: Contains the algorithm used to sign the token (e.g., HS256).
  • Payload: Contains the claims or the information about the user or session.
  • Signature: The signature is used to verify that the token was not tampered with.

3. Session Management

Session management is another key aspect of an authentication system. It involves maintaining the user's state across multiple requests. One way to do this is by storing a session ID, which the server associates with the user's information. JWT, as mentioned earlier, is often used as a token-based approach to manage authentication in a stateless manner.

Prerequisites

To follow along with this guide, you should have:

  • Basic knowledge of JavaScript and Node.js
  • Familiarity with Express.js, a popular Node.js web application framework
  • Installed Node.js and npm (Node Package Manager) on your machine

We will be using a few essential packages:

  • express: A minimal web framework for Node.js
  • bcryptjs: A library to hash and compare passwords securely
  • jsonwebtoken: A library for creating and verifying JWTs
  • dotenv: To manage environment variables (such as secret keys)
  • body-parser: A middleware to parse incoming request bodies

You can install these dependencies with npm:

Step 1: Set Up the Express Server

First, let's create a simple Express server to handle HTTP requests. We'll need to initialize a basic Node.js project.

cd auth-system
npm init -y

Next, create an index.js file for the main application logic:

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

dotenv.config();

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

const users = []; // In-memory "database" for this example

const PORT = process.env.PORT || 3000;

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

In this file, we've set up an Express app and used body-parser to parse incoming JSON data. We've also set up dotenv to manage environment variables, like our JWT secret key.

Step 2: Create the Registration Endpoint

The registration process will involve receiving a username and password, hashing the password, and saving the user data. Let's add the /register endpoint:

    const { username, password } = req.body;

    if (!username || !password) {
        return res.status(400).json({ message: 'Username and password are required' });
    }

    // Check if the user already exists
    const existingUser = users.find(user => user.username === username);
    if (existingUser) {
        return res.status(400).json({ message: 'User already exists' });
    }

    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create and store the user
    const newUser = { username, password: hashedPassword };
    users.push(newUser);

    res.status(201).json({ message: 'User registered successfully' });
});

Explanation:

  1. Validation: We first check if the username and password are provided.
  2. Check for existing user: If a user already exists with the provided username, we return an error.
  3. Hashing the password : The password is hashed using bcrypt with a salt factor of 10.
  4. Storing the user : For this example, we are storing the user in an in-memory array (users). In a real-world scenario, this would be a database.

Step 3: Create the Login Endpoint

Next, we'll create the login endpoint, where the user will send their username and password. We will check if the credentials are valid and, if so, generate a JWT for the user.

    const { username, password } = req.body;

    if (!username || !password) {
        return res.status(400).json({ message: 'Username and password are required' });
    }

    // Find the user by username
    const user = users.find(user => user.username === username);
    if (!user) {
        return res.status(400).json({ message: 'Invalid credentials' });
    }

    // Compare the hashed password with the provided password
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
        return res.status(400).json({ message: 'Invalid credentials' });
    }

    // Generate a JWT
    const token = jwt.sign({ username: user.username }, process.env.JWT_SECRET, { expiresIn: '1h' });

    res.status(200).json({ message: 'Login successful', token });
});

Explanation:

  1. User validation: We check if the user exists in the system by searching for their username.
  2. Password comparison : We use bcrypt.compare() to compare the stored hashed password with the user-provided password.
  3. Token generation : If the credentials are correct, we generate a JWT using jsonwebtoken.sign(). The JWT contains the username and is signed using a secret key from the environment variables.

Step 4: Protecting Routes with JWT

Now that we can generate JWTs, we need to protect certain routes so that only authenticated users can access them. To do this, we'll create middleware that verifies the JWT in the request headers.

    const token = req.header('Authorization')?.split(' ')[1]; // Expecting "Bearer <token>"

    if (!token) {
        return res.status(403).json({ message: 'Access denied' });
    }

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

app.get('/protected', authenticateJWT, (req, res) => {
    res.status(200).json({ message: 'Protected data', user: req.user });
});

Explanation:

  1. JWT extraction : We extract the token from the Authorization header, which should be in the form of Bearer <token>.
  2. Token verification : We use jwt.verify() to validate the token and extract the user information.
  3. Protected route : The /protected route is now protected by the authenticateJWT middleware. Only authenticated users with a valid JWT can access it.

Conclusion

In this tutorial, we've built a simple yet secure authentication system using JWT for managing authentication in a stateless manner. We covered key concepts like password hashing, JWT creation, and route protection. While this is just the basics, you can extend this system by adding features like refresh tokens, role-based access control, or two-factor authentication for added security.

By understanding these fundamental principles, you'll be able to build a robust authentication system for your applications while ensuring the security of user data.

Creative Ways for Saving Money on Subscription Boxes While Still Getting Great Deals
Creative Ways for Saving Money on Subscription Boxes While Still Getting Great Deals
Read More
How to Create a Master Document List for Your Home or Office
How to Create a Master Document List for Your Home or Office
Read More
How to Organize Your Remote Work Schedule Around Peak Productivity Times
How to Organize Your Remote Work Schedule Around Peak Productivity Times
Read More
How to Store Shoes in Small Spaces Without Creating a Mess
How to Store Shoes in Small Spaces Without Creating a Mess
Read More
Smart Budgeting Strategies for Beginners to Master Your Money
Smart Budgeting Strategies for Beginners to Master Your Money
Read More
Finding Authentic Cultural Experiences
Finding Authentic Cultural Experiences
Read More

Other Products

Creative Ways for Saving Money on Subscription Boxes While Still Getting Great Deals
Creative Ways for Saving Money on Subscription Boxes While Still Getting Great Deals
Read More
How to Create a Master Document List for Your Home or Office
How to Create a Master Document List for Your Home or Office
Read More
How to Organize Your Remote Work Schedule Around Peak Productivity Times
How to Organize Your Remote Work Schedule Around Peak Productivity Times
Read More
How to Store Shoes in Small Spaces Without Creating a Mess
How to Store Shoes in Small Spaces Without Creating a Mess
Read More
Smart Budgeting Strategies for Beginners to Master Your Money
Smart Budgeting Strategies for Beginners to Master Your Money
Read More
Finding Authentic Cultural Experiences
Finding Authentic Cultural Experiences
Read More