Skip to main content

Protecting Routes

Route level protection

You may want certain set different levels of permissions for different routes. For example, given a user model

const user = new EzUser('User', ['google'], {
isAdmin: {
type: Type.BOOL,
default: false,
},
});

and some blog posts

const post = new EzModel('Post', {
title: Type.VARCHAR,
body: Type.VARCHAR,
});

Perhaps we have the following requirements:

  1. Everyone can read posts
  2. Only logged in users can create posts
  3. Only admins can update and delete posts

The first requirement is the default functionality, so no changes are required.

Checking if user is logged in

The second requirement needs us to check if users are logged in

import Boom from '@hapi/boom'; //Library used for throwing HTTP errors

post.router.for('createOne').preHandler(async (req, res) => {
if (!req.user) {
throw Boom.unauthorized();
}
});

Breaking it down

post.router;

Allows access to the EzRouter instance

router.for('createOne');

A helper function that lets us edit the routes generated by an EzRouter

router.for('createOne').preHandler(async (req, res) => {
if (!req.user) {
throw Boom.unauthorized();
}
});
  1. For all of the routes specified
  2. Add a preHandler hook to the route
  3. If the user is undefined
  4. Throw an 'unauthorized' Error

The user object is always saved in req.user, and is automatically obtained from the database.

Checking if user has certain property

As for the third requirement, we can just check if the user has the predicate isAdmin

post.router.for('deleteOne', 'updateOne').preHandler(async (req, res) => {
if (!req.user || !req.user.isAdmin) {
throw Boom.unauthorized();
}
});

App level protection

We may instead want to perform a uniform protection of all of the routes under an app.

For example, if we only want admins to be able to create, read update or delete users:

user.router.addHook('preHandler', async (req, res) => {
if (!req.user || !req.user.isAdmin) {
throw Boom.unauthorized();
}
});

Now, for the user EzRouter, as well as all EzApps under it, the routes are only accessible if you are an admin

warning

Technically the below function will also work, but since the authorization router is part of the user EzApp, the login route will be protected by authorization as well and you wouldn't be able to login

user.addHook('preHandler', async (req, res) => {
if (!req.user || !req.user.isAdmin) {
throw Boom.unauthorized();
}
});

Full Working Sample

Note: This requires that you have set up your environment as specified in authenticating users

import { EzBackend, EzModel, Type } from '@ezbackend/common';
import { EzOpenAPI } from '@ezbackend/openapi';
import { EzDbUI } from '@ezbackend/db-ui';
import { EzAuth, EzUser } from '@ezbackend/auth';
import Boom from '@hapi/boom';

const app = new EzBackend();

//---Plugins---
app.addApp('ez-auth', new EzAuth());
app.addApp('openapi', new EzOpenAPI());
app.addApp('db-ui', new EzDbUI());
//---Plugins---

const user = new EzUser('User', ['google'], {
isAdmin: {
type: Type.BOOL,
default: false,
},
});

const post = new EzModel('Post', {
title: Type.VARCHAR,
body: Type.VARCHAR,
});

user.router.addHook('preHandler', async (req, res) => {
if (!req.user || !req.user.isAdmin) {
throw Boom.unauthorized();
}
});

post.router.for('createOne').preHandler(async (req, res) => {
if (!req.user) {
throw Boom.unauthorized();
}
});

post.router.for('updateOne', 'deleteOne').preHandler(async (req, res) => {
if (!req.user || !req.user.isAdmin) {
throw Boom.unauthorized();
}
});

app.addApp('User', user, { prefix: 'user' });
app.addApp('Post', post, { prefix: 'post' });

app.start();