@brainhubeu/hadron-auth

0.0.2 • Public • Published

Installation

npm install @brainhubeu/hadron-auth --save

Overview

hadron-auth provides back-end authorization layer for routes You will choose.

Configuration with Hadron Core

If You want to use hadron-auth with hadron-core You should also use hadron-typeorm and hadron-express. All You need to provide is two schemas for typeorm:

  • User (id, username, and roles many-to-many relation required) Here is the example schema:
// schemas/User
const userSchema = {
  name: 'User',
  columns: {
    id: {
      primary: true,
      type: 'int',
      generated: true,
    },
    username: {
      type: 'varchar',
      unique: true,
    },
    passwordHash: {
      type: 'varchar',
    },
    addedOn: {
      type: 'timestamp',
    },
  },
  relations: {
    roles: {
      target: 'Role',
      type: 'many-to-many',
      joinTable: {
        name: 'user_role',
      },
      onDelete: 'CASCADE',
    },
  },
};

module.exports = userSchema;
  • Role (id and name required) Example schema:
// schemas/Role
const roleSchema = {
  name: 'Role',
  columns: {
    id: {
      primary: true,
      type: 'int',
      generated: true,
    },
    name: {
      type: 'varchar',
      unique: true,
    },
    addedOn: {
      type: 'timestamp',
    },
  },
};

module.exports = roleSchema;

Don't forget to add schemas to Your database config, example below:

// config/db.js
const userSchema = require('../schemas/User');
const roleSchema = require('../schemas/Role');

const connection = {
  name: 'mysql-connection',
  type: 'mysql',
  host: 'localhost',
  port: 3306,
  username: 'root',
  password: 'my-secret-pw',
  database: 'done-it',
  entitySchemas: [roleSchema, userSchema],
  synchronize: true,
};

module.exports = connection,

Now You need to prepare Your hadron configuration file, where You can add secured routes, for example:

// index.js
const config = {
  routes: {
    helloWorldRoute: {
      path: '/',
      methods: ['GET'],
      callback: () => 'Hello World',
    },
    adminRoute: {
      path: '/admin',
      methods: ['GET'],
      callback: () => 'Hello Admin',
    },
    userRoute: {
      path: '/user',
      methods: ['GET'],
      callback: () => 'Hello User',
    },
  },
  securedRoutes: [
    {
      path: '/admin/*',
      methods: ['GET', 'POST', 'PUT', 'DELETE'],
      roles: 'Admin',
    },
    {
      path: '/user/*',
      roles: ['Admin', 'User'],
    },
  ],
};

Finally You need to add hadron-auth to hadron initialization method:

const hadron = require('@brainhubeu/hadron-core').default;
const hadronExpress = require('@brainhubeu/hadron-express');
const hadronTypeOrm = require('@brainhubeu/hadron-typeorm');
const hadronAuth = require('@brainhubeu/hadron-auth');
const express = require('express');

const expressApp = express();

const hadronInit = async () => {
  const config = {
    routes: {
      helloWorldRoute: {
        path: '/',
        methods: ['GET'],
        callback: () => 'Hello World',
      },
      adminRoute: {
        path: '/admin',
        methods: ['GET'],
        callback: () => 'Hello Admin',
      },
      userRoute: {
        path: '/user',
        methods: ['GET'],
        callback: () => 'Hello User',
      },
    },
    securedRoutes: [
      {
        path: '/admin/*',
        methods: ['GET', 'POST', 'PUT', 'DELETE'],
        roles: 'Admin',
      },
      {
        path: '/user/*',
        roles: ['Admin', 'User'],
      },
    ],
  };

  const container = await hadron(
    expressApp,
    [hadronAuth, hadronExpress, hadronTypeOrm],
    config,
  );
};

Warning, You should pass hadronAuth as first to hadron packages array.


Now Your routes are secured, by default, hadron-auth authorize user by JWT Token, passed as Authorization header.

Creating custom auth middleware

You can pass Your own function in hadron configuration to check if a user is authorized to the secured route. Here is the skeleton for the authorization middleware:

const authorizationMiddleware = (container) => {
  return (req, res, next) => {};
};

hadron-auth provides isAllowed function, to check if a user is allowed to specified route:

isAllowed(path, method, user, allRoles);

Where:

  • path - path to secured route, for example /api/admin/1
  • method - HTTP method
  • user - User object, which need to contain roles
  • allRoles - All roles stored in database (only role names)

Here is an example authorization middleware:

const jwt = require('jsonwebtoken');
const { isRouteSecure, isAllowed } = require('@brainhubeu/hadron-auth');

const errorResponse = {
  message: 'Unauthorized',
};

const expressMiddlewareAuthorization = (container) => {
  return async (req, res, next) => {
    try {
      if (!isRouteSecure(req.path)) {
        return next();
      }

      const userRepository = container.take('userRepository');
      const roleRepository = container.take('roleRepository');

      const token = req.headers.authorization;

      const decoded: any = jwt.decode(token);

      const user = await userRepository.findOne({
        where: { id: decoded.id },
        relations: ['roles'],
      });

      if (!user) {
        return res.status(403).json({ error: errorResponse });
      }

      const allRoles = await roleRepository.find();

      if (
        isAllowed(req.path, req.method, user, allRoles.map((role) => role.name))
      ) {
        return next();
      }

      return res.status(403).json({ error: errorResponse });
    } catch (error) {
      return res.status(403).json({ error: errorResponse });
    }
  };
};

module.exports = expressMiddlewareAuthorization;

To use it, You need to pass an expressMiddlewareAuthorization function as authorizationMiddleware key in hadron config.

const config = {
  authorizationMiddleware: YourCustomFunction,
};

Usage:

const securedRoutes = [
  {
    path: '/api/**',
    methods: ['GET'],
    roles: ['Admin', 'User'],
  },
  {
    path: '/api/**',
    methods: ['POST', 'PUT', 'DELETE'],
    roles: 'Admin',
  },
  {
    path: '/admin/*',
    roles: 'Admin',
  },
  {
    path: 'product/info',
    methods: ['GET'],
    roles: [['Admin', 'User'], 'Manager'],
  },
];
  • Path - here we can specify the route path we want to secure, we can use a static path like /api/admin/tasks or by pattern:
    • /api/admin/* - route after /api/admin/ is secured, for example /api/admin/tasks - is secured, but /api/admin/tasks/5 - will be not secured
    • /api/admin/** - every route after /api/admin is secured
  • methods - an array of strings, where You can pass role names, if You will not provide any role, then the route is secured and user with ANY role can access this if a user does not have any role he will be unauthorized.
  • roles - here You can pass single role name, an array of role names or array of arrays of strings, which add some logic functionality, for example, if we declare:
roles[(['Admin', 'User'], 'Manager')];

The user needs Admin AND User OR Manager role to access the route.

Readme

Keywords

Package Sidebar

Install

npm i @brainhubeu/hadron-auth

Weekly Downloads

0

Version

0.0.2

License

MIT

Unpacked Size

74 kB

Total Files

33

Last publish

Collaborators

  • benedyktdryl
  • dluber
  • brainhubeu-devops
  • annalach
  • szymon.morawski
  • dyoda
  • brainhubeu-ci
  • roberthebel