Authorization with Pundit

Pundit is a simple and powerful authorization library that allows you to define policies for your application.

Table of Contents

Install Pundit

Github repo: https://github.com/fatihky/pundit-ts

npm install pundit-ts

Create models

models.ts

export class User {
  constructor(
    // the type for Data can also come from an ORM
    readonly data: { id: number; role: "admin" | "editor" | "user" }) {}
}

export class Post {
  constructor(readonly data: { id: number; authorId: number }) {}
}

Define actions

actions.ts

export type PostActions = "create" | "delete" | "update";

Define policies

policies.ts

import { PunditPolicy } from "pundit-ts";
import { PostActions } from "./actions";
import { Post, User } from "./models";

export class PostPolicy extends PunditPolicy<User, Post, PostActions> {
  constructor() {
    super(Post);
  }

  async authorize(user: User, post: Post, action: PostActions) {
    switch (action) {
      case "create":
        return (
          user.data.role === "admin" ||
          user.data.role === "editor" ||
          user.data.role === "user"
        );
      case "update":
        return (
          user.data.role === "admin" ||
          post.data.authorId === user.data.id ||
          user.data.role === "editor"
        );
      case "delete":
        return (
          user.data.role === "admin" || post.data.authorId === user.data.id
        );
      default:
        return false;
    }
  }

  async filter(ctx) {
    // Placeholder for filtering logic if needed
  }
}


Create pundit instance

pundit.ts

import { Pundit } from "pundit-ts";
import { User } from "./models";
import { PostPolicy } from "./policies";

export const pundit = new Pundit<User>().register(new PostPolicy());
// use .register() to register multiple policies

Validate authorization

import { Post, User } from './models';
import { pundit } from './pundit';

const adminUser = new User({ id: 1, role: 'admin' });
const editorUser = new User({ id: 2, role: 'editor' });
const authorUser = new User({ id: 3, role: 'user' });
const otherUser = new User({ id: 4, role: 'user' });

const post = new Post({ id: 1, authorId: 3 });

console.log(await pundit.authorize(adminUser, post, 'update'));
// Admin update: true

console.log(await pundit.authorize(editorUser, post, 'delete'));
// Editor delete: false

console.log(await pundit.authorize(authorUser, post, 'update'));
// Author update: true
// True because the post author id matches the author id

console.log(await pundit.authorize(otherUser, post, 'delete'));
// Other user delete: false