Understanding Authentication

S. ApurbaS. Apurba
6 min read

Introduction

  • In this series, we’re going to dive deep into Authentication and Authorization in JavaScript, not just the theory, but hands-on implementation as well.

  • Before jumping into code, we’ll go over the core concepts and prerequisites that’ll make your journey smoother.

  • Let’s not delay things and dive into the real stuff.

What is Authentication ?

Think of Authentication as a black box (for now) that verifies who you are, whether you’re a user or a device, before giving access to protected resources (this part is called Authorization).

In simple terms, authentication asks:

Who are you?

It’s important to note that Authentication is a visible process. Means the client is aware that they’re being authenticated (e.g., login forms, tokens, OTPs). Authorization, on the other hand, is handled more silently behind the scenes.

Authentication types

1. Single-Factor Authentication (SFA)

  • Requires only one credential to verify identity.

  • Most common example:
    Password-based login

  • Simple, but less secure, if your password is compromised, you're exposed.


2. Two-Factor Authentication (2FA)

  • Requires two layers of verification, usually:
    Something you know (Password)
    Something you have (OTP sent to phone/email)

  • Adds a second layer of security beyond just passwords.


3. Multi-Factor Authentication (MFA)

  • Requires two or more verification factors from different categories:

    • Something you know: Password, PIN

    • Something you have: Smartphone, Authenticator app, Security token

    • Something you are: Fingerprint, Face recognition

  • MFA is considered highly secure, used in banking, enterprise logins, etc.


4. Biometric Authentication (BA)

  • Uses biological characteristics for identity verification.
    Examples:

    • Fingerprint scanning

    • Facial recognition

    • Voice recognition

    • Iris scan

  • Often used in smartphones and high-security applications for ease + strong security.

Why Authentication is needed ?

  • Authentication plays a vital role in security. It ensures that only authorized and verified users can access certain sensitive data or features.

  • If you're not authenticated, your access will be restricted to only public or non-sensitive areas of the application. This helps prevent unauthorized access and security vulnerabilities.

How it is implemented ?

Before we jump into implementing authentication, it’s important to understand a key building block of most backend frameworks. the middleware.

Middleware

  • In almost every backend framework (Node.js, Express, Django, etc.), middleware is a standard concept.

  • You can think of middleware as a gatekeeper — it intercepts requests, runs logic like validation or checks, and then either blocks or allows the request to continue.

  • You can obviously do manual checks in your API endpoint routes, but that is a very ugly way to actually check for the constraints.

  • Middleware functions are very much helpful in type checking, used in logging, authenticating user and restricting an user till certain checks are validated.

  • Here’s a basic example in Express:

const express = require("express");
const app = express();

// Built-in middleware
app.use(express.json());

// Application-level middleware
const sampleMiddleware1 = (req, res, next) => {
  req.number = 5;
  if (true) {
    next(); // Pass control to next middleware or route
  }
};

app.get("/", sampleMiddleware1, (req, res) => {
  res.send(`Hello from root\nFinal Request Number is: ${req.number}`);
});

app.listen(8001, () => {
  console.log(`Server running on port 8001`);
});
  • As you can see above there is two types of middleware that I have used.
    express.json() is a built-in middleware that parses incoming JSON.

    sampleMiddleware1() is a custom application-level middleware that sets a value in the request and then calls next() to continue.

  • The middleware runs before the route handler and can modify the request.

  • See app.use(…anything) is used to declare any middleware globally to all the routes. It means whenever any request hits any endpoint it must first go to the global middleware and after that it goes to the subsequent API endpoint.

  • Our custom middleware, sampleMiddleware1, performs two main tasks:

    1. It sets the number property on the request object to 5.

    2. It performs a simple check and then calls the next() function.

The next() function is crucial — it tells Express that the current middleware has finished its job and the request can proceed to the next middleware (if any) or directly to the route handler.

In our example, we’re using sampleMiddleware1 specifically for the "/" GET route. This means that whenever a request hits this endpoint, it first passes through sampleMiddleware1, then reaches the route handler. As a result, the response will be:

Hello from root Final Request Number is: 5
  • This is because we are accessing req.number, which was set to 5 inside the middleware.

Currently, the middleware is applied only to a single route. But if you want to apply it globally to all routes, you can simply use app.use(sampleMiddleware1). This ensures that the middleware runs before every incoming request — no matter which route it hits. Here is how you can do this:

const express = require("express");

const app = express();

//Builtin middleware
app.use(express.json());

//Application Level Middleware
const sampleMiddleware1 = (req, res, next) => {...};
app.use(sampleMiddleware1); //apply this middleware to all the routes

app.get("/", (req, res) => {
  res.send(`Hello from root
            Final Request Number is : ${req.number}`); //accessing the value set by the middleware
});

app.listen(8001, () => {
  console.log(`Server running on port 8001`);
});
  • You can also chain multiple middleware functions using the next() function.

  • This allows you to apply a series of checks in a clean, modular way— each middleware handling a specific concern, such as logging, validation, or authentication.

  • I think this much should give you a solid understanding of how middleware works. Now, let’s look at the following code example to see how the next() function helps the request flow through multiple middleware layers before reaching the final API endpoint.

const express = require("express");

const app = express();

app.use(express.json());

//Middleware declaration
const sampleMiddleware1 = (req, res, next) => {
  req.number = 5;
  if (true) {
    next();
  }
};

const sampleMiddleware2 = (req, res, next) => {
  console.log(`Request Number from Middleware1: ${req.number}`);
  req.number = 10;
  if (true) {
    next();
  }
};

app.get("/", sampleMiddleware1, sampleMiddleware2, (req, res) => {
  res.send(`Hello from root
        Final Request Number is : ${req.number}`);
}); //both the middleware checks are done here
//console will print Request Number from Middleware1: 5
//output: Hello from root Final Request Number is : 10

app.get("/home", sampleMiddleware1, (req, res) => {
  res.send(`Hello from home
            Final Request Number is : ${req.number}`);
}); //only the middleware1 check is done here
//console will not print anything
//output: Hello from root Final Request Number is : 5

app.get("/contact", sampleMiddleware2, (req, res) => {
  res.send(`Hello from contact
            Final Request Number is : ${req.number}`);
}); //only the middleware2 checks are done here
//console will print Request Number from Middleware1: undefined because sampleMiddleware1 never runs
//output: Hello from root Final Request Number is : 10

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

Summary

  • Authentication verifies who you are; authorization checks what you can access.

  • Middleware is used to achieve authentication.

  • Middleware helps you validate, log, and secure requests.

  • Using middleware smartly makes your code cleaner, modular, and more secure.

  • You can chain middleware functions to build pipelines of logic before your routes are executed.

What is Next ?

  • In the upcoming articles, we’ll dive deep into implementing authentication from scratch using real-world practices.

    Some of the key topics we’ll cover include:

    • Stateful vs Stateless Authentication
      Understand the core difference in how authentication state is stored and managed.

    • Session-Based Authentication
      Learn how sessions work, how they’re stored, and how to manage them securely.

    • Cookie-Based Authentication
      Explore how cookies can be used to store and validate session or token data securely in the browser.

    • Token-Based Authentication
      Dive into JWTs and how to implement secure stateless auth with tokens.

0
Subscribe to my newsletter

Read articles from S. Apurba directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

S. Apurba
S. Apurba