Is it safe to authorize user models using `instanceof` in JavaScript / TypeScript?

370 views Asked by At

Let's say I have 3 models: Admin, User, Product (I'm using model classes)

Only an admin can ADD, UPDATE, DELETE a product and users can only GET products, so I have registered an ACL middleware for corresponding routes.

Now in my ACL middleware, I want to authorize users, and if they are ADMIN, I will call next() method, otherwise, I'm going to reject the request with a 401.

I found it easy to do the check using JavaScript's instanceof operator:

const user = await auth.authenticate()

if (user instanceof Admin) {
  await next()
} else {
  throw UnAuthorizedUserException
}

await auth.authenticate() returns the current user sending the request, be it a user or an admin

However, I'm not sure if this is the safest way to distinguish b/w admins and users.

Now my question is, am I doing it right? Which approach is better than what I'm doing?

Note (if it helps): I'm using Adonis.js v5, TypeScript and Lucid models

2

There are 2 answers

1
louisbuchbinder On BEST ANSWER

Yes you can do this. You will need to be careful about inheritance patterns if you use this approach. You may want to consider adding a role property to the user object and using that for the check.

example using a role prop.

if (user.role === 'ADMIN') {
  ...
}

example of instanceof backfiring on you

class User {}
class Admin extends User {}

const user = new User;
const admin = new Admin;

console.log(user instanceof User); // true
console.log(user instanceof Admin); // false
console.log(admin instanceof User); // true **watch out for this**
console.log(admin instanceof Admin); // true
1
KiraLT On

The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.

So user instanceof Admin will test positive if user is Admin or its subclass instance. Using this pattern is very common in JS/TS world and is safe as long auth.authenticate() correctly authenticates users and Admin is the same class authenticate method returns.

Also in TypeScript instanceof is treated as TypeGuard, so inside if block you can use user as Admin instance (for example if admin instance has more methods).