Protect Your API-Endpoints in Next.js 14 with the Chain of Responsibility Pattern.
Tags: webdev, typescript, security, nextjs
Published: 2024-05-08 08:40:15

## Introduction

Today we will do two things with the Chain of Responsibility Pattern (CoR):

1. Implement the Chain of Responsibility Pattern in Next.js
2. Add an authentication step to the chain to protect our API-Endpoint

This is moreover a copy and paste guide and then understand it later when using it in your code. But have fun!

## Use it for Security

The CoR can be used for different security steps into your API-Endpoints. For example to check if the user is authenticated or not. The flow for that use case looks like that:


![Chain of Responsibility](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqif391vxac84o3gmccd.png)

And that's all for the authentication. But yeah, we could do this with a simple if-else. But let’s say we want to add all steps from a security standard like the OWASP ASVS to our API-Endpoint. This would be too many if-else conditions.

## Explanation

We can call all chain or steps in the CoR the same way. I name them with “Handler”. So the chain or step that checks if the user is authenticated is the “AuthenticationHandler”.

Let’s have a look at the Base Request Handler, which is the blue print for our other handlers. It is called Request because I want to handle the Request with this.

### Base Handler

I am using TypeScript (TS) what makes it easier and faster to see errors.

```tsx
// /lib/handler/request.handler.ts
interface Handler {
  setNext(_handler: Handler): Handler;
  handle(request: Request, context: any): Promise<Error | null>;
}

export abstract class AbstractRequestHandler implements Handler {
  private nextHandler!: Handler;

	// We need this method to set the next handler in our chain
	// AuthenticationHandler => ValidationHandler => ...
  public setNext(handler: Handler): Handler {
    this.nextHandler = handler;
    return handler;
  }

	// This method is needed to "start" the chain.
	// It is called once (you are going to see it later)
	// It handles each step in our CoR (Authenticaition => Validati..)
  public async handle(
    request: Request,
    context: Context,
  ): Promise<Error | null> {
    if (this.nextHandler) {
	    // use the await keyword when you need to handle async methods
      return await this.nextHandler.handle(request, context);
    }

    return null;
  }
}
```

### Authentication Handler

Let’s code our first “Handler”. It’s going to be the AuthenticationHandler becuse many Application need Authentication of API-Endpoints in the first place.

```tsx
**//** /lib/handler/**authentication.handler.ts

// We need to import out abstract request handler because our new handler
// should be a child of our Handler (so we could use methods from the parent)
import { AbstractRequestHandler } from "./request.handler";

// This is our first handler in the chain:
export class UserAuthenticationHandler extends AbstractRequestHandler {
	// We get two parameters: request and _context
	// Request pocesses for example the json body and headers
	// Context is in nextjs currently the search param e.g. /api/handler/[id]/route.ts
  public async handle(request: Request, _context: any): Promise<Error | null> {
    // The handler follow all the same approach:
    // Check if something true or false (in this case the session)
    // if false throw an error else go on with the next chain/step.
    const isCurrentSessionIsAvailable = true;
    if (!isCurrentSessionIsAvailable) {
	    // Throw an error so we could use this in our route to "catch" the error
	    // Instead of using a flag value
      throw Error("Forbidden");
    }
		
		// If session is "true" then we go on with the next chain/step
    return super.handle(request, _context);
  }
}**
```

### Implement into a Route.ts (Next.js 14)

```tsx

// /api/v1/cor/route.ts

import { UserAuthenticationHandler } from "@/lib/handler/authentication.handler";
import { NextResponse } from "next/server";

export async function POST(req: Request, _context: any) {
	// In each route we have to initiate the handler we build.
  const userAuthenticationHandler = new UserAuthenticationHandler();
  //// Add Handlers as you need: (Implement them first in your /lib/handler/example.handler.ts)
  //const exampleHandler = new ExampleHandler();

  // Set the chain (there is no real chain at the moment)
  // Set the next chain/step with ".setNext(yourHandlerName)"
  userAuthenticationHandler//.setNext(exampleHandler);

  try {
	  // In nextjs we have to clone the request because if we call await once
	  // on the request we can't use access the body in our busines logic.
    const requestClone = req.clone();
    // Pass the request clone to the handle function which starts
    // the chain of checks we setup above the try block.
    await userAuthenticationHandler.handle(requestClone, _context);

		
    // After this comment the code runs only if the CoR don't throw an error!
    // YOUR BUSINESS LOGIC

    return NextResponse.json("All good!", { status: 201 });
  } catch (error) {
	  // Return Forbidden to the client or handle it your way!
    return NextResponse.json("Forbidden" { status: 403 });
  }
}
```

## Conclusion and Future topics

And that's all. You should now know how to use the CoR to protect your API-Endpoints. Just try it out. Copy the code and change some things. You can find this code on GitHub: https://github.com/TobiasGleiter/nextjs-security.

In the future we should add some things to make our application more secure. For this it is useful to use a standard like the OWASP ASVS which is a security standard that provides a checklist of security steps. For example we should verify all input using a validation Framework like Zod and validate HTTP Headers and Cookies.

# References

Code: https://github.com/TobiasGleiter/nextjs-security
Me: [https://tobiasgleiter.de](https://tobiasgleiter.de/en)
OWASP ASVS: https://owasp.org/www-project-application-security-verification-standard/tobvi
CoR: https://refactoring.guru/design-patterns/chain-of-responsibility