@brainhubeu/hadron-auth
v0.0.2
Published
Security package for hadron
Downloads
5
Readme
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/1method
- HTTP methoduser
- User object, which need to contain rolesallRoles
- 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.