Dead simple Access Control with iamjs
Published: 2023-07-08 03:06:28

As web applications evolve and grow in complexity, the need for advanced security measures also increases. One particular area of focus is the authorization process, which controls who has access to what within an application. But how do you manage this efficiently?

Introducing iamjs, a robust, performance-driven authorization library engineered to streamline your authorization process through a schema-based approach. Composed of several modules such as @iamjs/core, @iamjs/express, @iamjs/koa, @iamjs/next and @iamjs/react, iamjs has something to accommodate a variety of programming environments.

## 🎉 **Features:**

**iamjs** is a feature-rich access control library that offers high performance and a wide range of capabilities. It revolutionizes the authorization process by empowering developers with efficient tools that simplify their workflow. Here are some key features that make **iamjs** stand out:

- 🔑 Role-based Access Control: **iamjs** allows users to effortlessly assign roles with fine-grained permissions, enabling precise control over resource access and security.
- 🌐 Versatile Programming Environment Support: With **iamjs**, you can seamlessly integrate access control into various programming environments, including Express.js, Koa.js, Next.js, and React. This flexibility ensures that you can use **iamjs** across your entire tech stack.
- 📝 Customizable Permissions: Tailoring access permissions to your specific requirements is a breeze with **iamjs**. You have the freedom to define and configure custom permissions for individual resources and actions, empowering you to establish a robust security model.
- 🎯 Type safety: **iamjs** is built with TypeScript, ensuring type safety and reducing the likelihood of errors in your code.
- 🧪 Effortless Testing: **iamjs** is equipped with comprehensive testing tools that simplify the process of validating your authorization logic. This feature ensures that your application remains secure throughout development, giving you peace of mind.

## 🧠 **@iamjs/core:**

The `@iamjs/core` module provides the core logic for the library, empowering developers with a toolkit that makes building access control systems a simpler task.

For instance, let's begin by defining two roles, `user` and `admin`, both equipped with specific permissions for different actions and resources:

```ts
import { AuthManager, Role, Schema } from "@iamjs/core";

const roles = {
  user: new Role({
    name: "user",
    config: {
      user: {
        scopes: "-r--l",
        custom: {
          ban: false,
        },
      },
      post: {
        scopes: "crudl",
        custom: {
          publish: true,
        },
      },
    },
  }),
  admin: new Role({
    name: "admin",
    config: {
      user: {
        scopes: "crudl",
        custom: {
          ban: true,
        },
      },
      post: {
        scopes: "crudl",
        custom: {
          publish: true,
        },
      },
    },
  }),
};

const schema = new Schema(roles);
const auth = new AuthManager(schema);
```

With the roles and authorization schema defined, you can now utilize the AuthManager class to perform authorization checks effortlessly:

```ts
const isAdminAuthorized = auth.authorize({
  role: "admin",
  actions: ["ban", "create"],
  resources: "user",
}); // true

const isUserAuthorized = auth.authorize({
  role: "user",
  actions: ["read", "create"],
  resources: ["post", "user"],
  strict: true,
}); // false
```

## **🚀 @iamjs/express:**

For Express.js enthusiasts, `@iamjs/express` provides seamless integration with the Express.js framework. Leveraging this module, you can easily set up middleware for assigning roles and checking authorization, amplifying your application's security layer in just a few lines of code:

```ts
import { Role, Schema } from '@iamjs/core';
import { ExpressRoleManager } from '@iamjs/express';
import express from 'express';

const role = new Role({
  name: 'role',
  config: {
    resource1: {
      scopes: 'crudl'
    },
    resource2: {
      scopes: 'cr-dl',
      custom: {
        'create a new user': false
      }
    }
  }
});

const schema = new Schema({
  role
});

const roleManager = new ExpressRoleManager({
  schema: schema,
  onError(_err, _req, res, _next) {
    res.status(403).send('Forbidden');
  },
  onSucess(_req, res, _next) {
    res.status(200).send('Hello World from the success handler!');
  }
});

const app = express();

app.get(
  '/resource1',
  roleManager.check({
    resources: 'resource1', // the resources to be accessed
    actions: ['create', 'update'], // the actions to be performed
    role: 'role',// the role to check
    strict: true // make the checks run in strict mode
    // additionnaly you can construct role from permissions by using the construct option
		construct: true,
		data: async(req){
			return schema.get('role').toObject()
		}
}),
  (_req, res) => {
    res.send('Hello World!');
  }
);

```

## ☢️ **@iamjs/react:**

Finally, weaving this into a React setting, `@iamjs/react` simplifies checking permissions within your components. It introduces a nifty hook - `useAuthorize`, allowing you to control component rendering based on user roles:

Import `useAuthorize` and `createSchema` from the package

```tsx
import { Role } from "@iamjs/core";
import { createSchema, useAuthorization } from "@iamjs/react";
import { useEffect, useState } from "react";
```

Create the initial schema with a default roles

```tsx
const schema = createSchema({
  user: new Role({
    name: 'user',
    description: 'User role',
    meta: {
      name: 'user'
    },
    config: {
      books: {
        scopes: 'crudl',
        custom: {
          upgrade: true,
          downgrade: false,
          sort: true
        }
      }
    }
  })

```

Create your own hook to fetch the user's permissions from your backend

```tsx
const useUser = () => {
  const { build } = useAuthorization(schema);
  const [userRole, setUserRole] = useState(null);

  useEffect(() => {
    // Call the API endpoint to fetch user permissions
    fetch("/permssions")
      .then((response) => response.json())
      .then((data) => {
        // Build the role based on the received permissions
        const builtRole = build(data);
        setUserRole(builtRole);
      })
      .catch((error) => {
        console.error("Error fetching user permissions:", error);
      });
  }, []);

  return userRole;
};
```

Use your own hook to check the permissions or to check the user's permissions or render components conditionally

```tsx
const Component = () => {
  const userRole = useUser();

  if (!userRole) {
    return <div>Loading...</div>;
  }

  const { can, Show } = userRole;

  return (
    <div>
      <div>{can("books", "create").toString()}</div>
      <Show resources="books" actions="create">
        <div>Rendered if user has 'create' permission for 'books'</div>
      </Show>
    </div>
  );
};
```

Additionally your use the `Show` and `can` methods directly from `useAuthorize` and use the roles you provided in the schema to render conditionally or check the users permissions

```tsx
const { can, Show } = useAuthorization(schema);

const canDo = can("user", "books", "create").toString(); // 'true'

<Show role="user" resources="books" actions="create">
  <div>can show</div>
</Show>;
```

## 📔Conclusion

**iamjs** is a powerful and versatile library for access control that simplifies the authorization process using a schema-based approach. With features such as role-based access control, customizable permissions, and support for a variety of programming environments, **iamjs** provides a comprehensive solution for building secure and robust web applications.

To learn more about **iamjs**, check out the official documentation [here](https://iamjs.achaq.dev/). And if you're interested in contributing to the project, head over to the [repository on GitHub](https://github.com/triyanox/iamjs).