npm package: @random-guys/gateman
Description: Encrypted RBAC and authentication management.
Author: Chukwudi Oranu
Latest version: 2.0.1

<p align="center">
  <img width="70" height="70" src="https://res.cloudinary.com/chudi/image/upload/v1561559483/sterling/G.svg">
</p>

<p align="center">Encrypted RBAC and authentication management for Node.js</p>

# Gateman offers

- Minimal overhead and complexity
- Role based access control (RBAC) for services and authenticated users
- Encryption and decryption of url safe encrypted tokens

# Installation

Install via yarn

`yarn add @random-guys/gateman`

Install with NPM

`npm install @random-guys/gateman`

# Introduction

Gateman allows you transform a Javascript object containing a particular role e.g `user`, `admin`, `agent` and a user's id, into an encrypted cookie/URI/HTTP header friendly token with an integrity check. This allows you securely pass around this token without exposing the user's id and role.

Encrypted signed (integrity-checked) tokens allow you encrypt the role and id of the user contained within the token, pass this token around from `client to server` or `server to server`, without worrying about someone modifying (or even knowing) what those roles are. Any modification to the encrypted data will invalidate its integrity.

Gateman also provides an Express middleware for securing endpoints. This middleware guards against requests that do not contain signed encrypted tokens created by Gateman.

# How it works

Encryption is done using the [Iron](https://github.com/hapijs/iron) library which uses `AES256` for encryption and `SHA256` for Integrity checks.

Gateman generates tokens for two main uses cases `Client to Server` and `Server to Server`

### Client to Server

Gateman allows you create signed encrypted tokens that can be safely used for client to server communication. A typical instance is a token created and sent back to the client when a user logs in from an iOS or android app. This token is then subsequently used for communication between the app (client) and backend (server) until the token session expires.

When a token is created in this scenario the following assumptions and actions are taken:

1. An object in the following format `{id: some-unique-user-id, role: "user" | "admin" | "agent"}` is signed and encrypted, thus generating the token.
2. The token is persisted to Redis for a fixed period of time (indicating the user's session)
3. The Gateman library expects the `Bearer` authentication scheme to be used when sending the token from the client to server i.e it expects the following Authorization header `Bearer <token>`

### Server to Server (Headless tokens)

_`server` and `services` are used interchangebly_

Gateman allows you create signed encrypted tokens that can be safely used for server to server communication. Imagine a scenario in a microservice architecture where a particular service `service A` performs a cronjob by 2am every day, which makes an API request to another service `service B` on the behalf of a particular user. This API request is _headless_ in the sense that an authenticated user does not trigger it directly. We would need a way to get the id of the user. as well as validating that only recognized services can call the API endpoint that fulfills the request.

Gateman specifically caters to this scenario by creating headless tokens that contain the name of the `source` service and can also decode and validate that the token was sent from a recognized service.

When a token is created in this scenario the following assumptions and actions are taken:

1. An object in the following format `{ id: some-unqiue-user-id, role: 'service', service: some-service-name }` is signed and encrypted, thus generating the token.
2. The token is created with a short Time-to-live (TTL) of 60 seconds i.e the token is only valid for 1 minute, after which subsequent requests made with it would fail.
3. The Gateman library expects a custom authentication scheme to be used when sending the token from the client to server i.e it expects the following Authorization header `<Custom scheme> <token>`. This custom authentication scheme is passed when creating a new instance of Gateman.

# API

### new Gateman({ service: string, authScheme: string, redis: IRedisService, secret: string, sessionDuration?: number })

Creates a new Gateman instance where:

- `service` Name of the service initializing Gateman
- `authScheme` Custom authentication scheme used for service to service calls
- `redis` Redis instance object
- `secret` Secret key used for encryption and integrity signing. Should be at least 32 characters
- `sessionDuration` How long tokens should be peristed to Redis in seconds. Defaults to 600 seconds i.e 10 minutes

```javascript
import { Gateman } from '@random-guys/gateman';

const gateman = new Gateman({
  service: 'service-name',
  authScheme: 'Customscheme',
  redis: RedisClient,
  secret: '9b2e051cf4e90bc86dcd128184fc7614',
  sessionDuration: 180,
});
```

### persistSession(id, token, [sessionDuration])

Persists a token using the user's id in Redis for a specific period of time. Stores the token and user's id as a key value pair, where the user's id is the key and the token is the value in Redis. You typically do not need to call this directly, call `createSession` instead.

- `id` The user's id
- `token` The user's encrypted token
- `sessionDuration` How long the token should be peristed to Redis in seconds. Overwrites the session duration provided in the constructor. If it is not provided it defaults to the session duration provided in the constructor

```javascript
await gateman.persistSession(
  '51c2168c-00f6-4e9a-becb-6274ae9fa5d9',
  'Fe26.2**a03bffbf883a555e953afe7d524ed30da778bd5a4747141c40fd9a0e3c2a63f7*9rarsXkWp9TvymsXB7oTiQ*ifTyoXxOHEH4cArCcjKBEY4fA5vK6tSStfagGVuy2GqMSSa4BybLKtZY4JXKFQ5ARLACuY-oEDx7ybqIdPLDLoDHZrxNzSBYUIm-4Capj1I*1561642137848*845cbd2a8beb578138ddb626eafe3b384b96ee7b6c8d88cd27e95053a375ec96*K7QBSsEi54ZaPXz5DABQ8MrYhqW181ivFiblXZ00GKs',
  100
);
```

### clearSession(id)

Deletes a token from Redis using the user's id

- `id` The user's id

```javascript
await gateman.clearSession('51c2168c-00f6-4e9a-becb-6274ae9fa5d9');
```

### createSession(id, role, [sessionDuration])

Creates and returns an encrypted token using the user's id and role, and persists it to Redis for a specific period of time. Used for creating `admin` and `user` sessions. Calls `persistSession` internally to persist the tokens.

- `id` The user's id
- `role` The user's role. Defaults to `user`
- `sessionDuration` How long the token should be peristed to Redis in seconds. Overwrites the session duration provided in the constructor. If it is not provided it defaults to the session duration provided in the constructor

```javascript
const token = await gateman.createSession(
  '51c2168c-00f6-4e9a-becb-6274ae9fa5d9',
  'user',
  120
);
```

### createHeadlessToken(id)

Creates a token that can be used for headless (i.e not triggered by a human user) inter-service calls by encrypting the service name and the id of the user whom the call is made for. The token is not persisted. The token is not persisted instead a TTL is attached to the token, after the which the token would be invalidated.

- `id` The user's id

```javascript
const token = await gateman.createHeadlessToken(
  '51c2168c-00f6-4e9a-becb-6274ae9fa5d9'
);
```

### guard([roles], [service])

Returns an Express middleware that guards requests to a particular endpoint using the token in the `Authorization` header against recognized `roles`.

- `roles` The role(s) allowed to call the endpoint, defaults to `user`. Should be either `user`, `agent` or `admin`. An array can be provided if multiple roles can call the endpoint e.g `['user', 'admin', 'service']`
- `service` The optional service(s) allowed to call the endpoint. `roles` should either contain or be `service` when this argument is provided. An array can provided if multiple services can call the endpoint e.g `['wallets', 'cards']`
  If `service` is `"*"` all services can call the endpoint

```javascript
// User guard
app.post('/albums', gateman.guard('user'), routeHandler);

// Admin guard
app.put('/albums', gateman.guard('admin'), routeHandler);

// Agent guard
app.put('/albums', gateman.guard('agent'), routeHandler);
```

# Examples

The following examples show how Gateman can be used for securing endpoints for various use cases

### Callable by only users

```javascript
app.get('/user', gateman.guard('user'), routeHandler);

// Same as above because the default role is `user`
app.get('/default', gateman.guard(), routeHandler);
```

### Callable by only admins

```javascript
app.get('/admin', gateman.guard('admin'), routeHandler);
```


### Callable by only agents

```javascript
app.get('/agent', gateman.guard('agent'), routeHandler);
```

### Callable by only a specific service

```javascript
// Only allows headless calls by the wallet service
app.get('/service', gateman.guard('service', 'wallet'), routeHandler);
```

### Callable by specific multiple services

```javascript
// Only allows headless calls by the wallet and transaction service
app.get(
  '/service',
  gateman.guard('service', ['wallet', 'transaction']),
  routeHandler
);
```

### Callable by all services

```javascript
// Only allows headless calls by all services
app.get('/service', gateman.guard('service', '*'), routeHandler);
```

### Callable by users and a specific service

```javascript
app.get(
  '/user-and-service',
  gateman.guard(['user', 'service'], 'wallets'),
  routeHandler
);
```

### Callable by users and multiple services

```javascript
app.get(
  '/user-and-multiple-services',
  gateman.guard(['user', 'service'], ['transactions', 'trips']),
  routeHandler
);
```