Authorize NodeJS APIs using Authorizer
Tags: opensource, javascript, auth, node
Published: 2023-05-23 08:35:04

In this blog post, we will learn how to authorize users for API calls, based on valid sessions and roles using [Authorizer](http://authorizer.dev/). [Authorizer](http://authorizer.dev/) is an open-source database-independent auth solution. You can bring your database and have authentication and authorization service ready out of the box.

For the demo purpose, we will be using \[Express\]([https://expressjs.com/](https://expressjs.com/)) server and writing a middleware that will validate user sessions & roles based on the JWT (JSON Web Token) in the `Authorization` header.

Refer to the source code on [github](https://github.com/authorizerdev/examples/tree/main/with-express-js).

## Step 1: Setup Authorizer Instance

Deploy production-ready Authorizer instance using one-click deployment options available below

| **Infra provider** | **One-click link** | **Additional information** |
| --- | --- | --- |
| Railway.app | <a target="_blank" href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><img src="https://railway.app/button.svg"  alt="Deploy on Railway" /></a>   | [docs](https://docs.authorizer.dev/deployment/railway) |
| Heroku | <a target="_blank" href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" /></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
| Render | <a target="_blank" href="https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render"><img alt="render button" src="https://render.com/images/deploy-to-render-button.svg" /></a>  | [docs](https://docs.authorizer.dev/deployment/render) |

For more information & deployment options like docker / Kubernetes / helm charts refer to the [docs](https://docs.authorizer.dev/deployment)

**Configure Instance**

* Open the Authorizer instance endpoint in a browser
    
* SignUp as an Admin with a secure password
    
* Configure environment variables from the dashboard. Check [env docs](https://docs.authorizer.dev/core/env) for more information.
    
    > Note: `DATABASE_URL` , `DATABASE_TYPE` , `REDIS_URL` are the variables that can only be configured as the authorizer instance's system environment variable.
    

## Step 2: Create Express App

> Note: This step is optional if you already have a Nodejs application up and running

* Setup project
    
    ```bash
    # Create directory
    mkdir my-apis
    
    # Change directory
    cd my-apis
    
    # Initialize nodejs APP
    npm init -y
    
    # Install express
    npm install express
    
    # Create index.js
    touch index.js
    ```
    
* Add Start Command to `package.json`
    
    ```javascript
    {
      "name": "my-apis",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "Lakhan Samani",
      "license": "ISC",
      "dependencies": {
        "express": "^4.18.2"
      }
    }
    ```
    
* Setup Express App and Create a basic API in `index.js`
    
    ```javascript
    // index.js
    const express = require('express');
    
    const app = express();
    const port = `3000`;
    
    app.get('/', (req, res) => {
      res.send('Hello World');
    });
    
    app.listen(port, () => {
      console.log(`[server]: Server is running at http://localhost:${port}`);
    });
    ```
    

## Step 3: Create Authorization Middleware

* Install `@authorizerdev/authorizer-js`
    
    ```bash
    npm i --save @authorizerdev/authorizer-js
    ```
    
* Create `auth_middleware.js`
    
    ```bash
    touch auth_middleware.js
    ```
    
* Implement authorization middleware
    
    ```javascript
    // auth_middleware.js
    
    const { Authorizer } = require("@authorizerdev/authorizer-js");
     
    const authRef = new Authorizer({  
      authorizerURL: "AUTHORIZER_URL_FROM_STEP 1",
      redirectURL: "FRONTEND_URL",
      clientID: "AUTHORIZER_CLIENT_ID FROM DASHBOARD"
    });
     
    const authMiddleware = async (req, res, next) => {
        const authHeader = req.headers.authorization;
      if (!authHeader) {
        return res.status(403).json({ error: "Authorization not found" });
      }
    
      const splitHeader = authHeader.split(" ");
      if (splitHeader.length != 2) {
        return res.status(403).json({ error: "Invalid auth header" });
      }
    
      if (splitHeader[0].toLowerCase() != "bearer") {
        return res.status(403).json({ error: "Bearer token not found" });
      }
    
      const token = splitHeader[1];
      // Validate jwt token via authorizer sdk
      try {
        const res = await authRef.validateJWTToken({
          token,
          token_type: "id_token", // This can be access_token, refresh_token
           // roles: [user] // specify roles that you want to validate jwt for, by default it will just verify jwt.
        });
        req.user = res.claims;
      } catch (err) {
        console.error(err);
        return res.status(403).json({ error: "Invalid JWT token" });
      }
    
      next();
    }
    
    module.exports = authMiddleware
    ```
    

## Step 4: Add auth middleware to APIs

Update `index.js` with following content

```javascript
// index.js
const express = require('express');
const authMiddleware = require('./auth_middleware')

const app = express();
const port = `3000`;

app.get('/', authMiddleware, (req, res) => {
  res.send('Hello World');
});

app.listen(port, () => {
  console.log(`[server]: Server is running at http://localhost:${port}`);
});
```

## Step 5: Test the API

* Start API Server
    
    ```bash
    npm start
    ```
    
* Make a curl request
    
    ```bash
    curl http://localhost:3000
    ```
    

> Note: This will return an error, as we have not specified the Authorization header with a valid JWT token

* For this test to pass we need to have a valid JWT token, so let's generate a valid JWT by making a login call to the authorizer server
    
    ```bash
    # Replace Authorizer URL from step 1
    # Replace credentials with right credentials in --data-raw (demo@yopmail.com, Test@123#)
    # If you have no user on your instance, first signup using AUTHORIZER_URL_FROM_STEP_1/app?redirect_uri=AUTHORIZER_URL_FROM_STEP_1/app
    
    curl --location --request POST 'AUTHORIZER_URL_FROM_STEP_1/graphql' \
    --header 'Content-Type: application/json' \
    --data-raw '{"query":"mutation login {\n  login(params: {\n    email: \"demo@yopmail.com\",\n    password: \"Test@123#\"\n  }) {\n    id_token\n  }\n}","variables":{}}'
    ```
    
    This should return an `id_token` in response, as shown in the screenshot below. Copy the `id_token` from the response and pass it to our nodeJS API server
    
* Test the API
    
    ```bash
    curl --header 'Authorization: Bearer TOKEN_COPIED_FROM_ABOVE_STEP' http://localhost:3000
    ```
    

That's all I hope this helps you authorize your APIs easily and make them secure based on the correct session and roles.

**For more information check out the links below.**

* Website: [https://authorizer.dev](https://authorizer.dev)
    
* Docs: [https://docs.authorizer.dev](https://docs.authorizer.dev)
    
* Github: [https://github.com/authorizerdev/authorizer](https://github.com/authorizerdev/authorizer)
    
* React SDK: https://github.com/authorizerdev/authorizer-react
    
* Javascript SDK: https://github.com/authorizerdev/authorizer-js
    
* Youtube: [https://youtube.com/playlist?list=PLSQGbUjHc6bpaAgCiQPzNxiUPr7SkDAFR](https://youtube.com/playlist?list=PLSQGbUjHc6bpaAgCiQPzNxiUPr7SkDAFR)
    
* Examples: [https://github.com/authorizerdev/examples/](https://github.com/authorizerdev/examples/)
    
* Discord: [https://discord.gg/Zv2D5h6kkK](https://discord.gg/Zv2D5h6kkK)
    
* Github Sponsorship: https://github.com/sponsors/authorizerdev/
    
* Buy me Coffee: [https://www.buymeacoffee.com/lakhansamani](https://www.buymeacoffee.com/lakhansamani)