JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 171
  • Score
    100M100P100Q79270F
  • License MIT

Authorization library in pure typescript.

Package Exports

  • pundit-ts
  • pundit-ts/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (pundit-ts) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Pundit-TS - Plain Typescript Authorization Library

Pundit-TS is an authorization library highly-inspired by the pundit gem.

Pundit-TS is a fully type-safe `pundit` alternative.
Here how it auto-completes the actions based on the object you pass:

https://github.com/user-attachments/assets/02994236-0182-4d84-a8b8-25589a511aad


Use Cases

  • Check if a user is authorized to perform an action on an entity (ie. Post, Product, Category etc..)
  • Declare actions can be performed per entity class basis
    • UserActions: create, update
    • PostActions: create, publish, unpublish, update, delete
    • CategoryActions: create, update, delete
  • Filter entities on database to avoid unnecessary database queries
    • Apply joins, specific where clauses or similar things to filter database rows.
  • Apply user tiers: only premium users can add more than 3 seats to their organization.
  • Avoid code duplication: Keep your authorization logic in one place. Use whenever you need them. Change one place to affect rest of the code.

Examples

Blog: Plain typescript blog example. Has no relation with a database. Great starting point if you are just starting to use pundit-ts

Prisma Blog: Prisma ORM based blog example. Advanced version of the plain blog example. Uses Prisma ORM for querying database, utilizes PostPolicy#filter for building argument for prisma.post.findMany method.

Usage

Install the package first:

npm i -S pundit-ts

Authorize users

Encapsulate your authorization logic behind your PunditPolicy implementations. Reuse those policies when you need. Manage your authorization logic from one place.

const currentUser = {}; // get user from cookies, headers, jwt etc...

// update post

// fetch post from db
const post = await prisma.post.findFirst({ where: { id: 123 } });

- if (post.authorId === currentUser.id) {
-   // update logic...
- }
+ if (await pundit.authorize(currentUser, post, 'update')) {
+   // update logic...
+ }

Filter entitites

Pundit-TS is a ORM-agnostic library. You may use your choice of ORM, query builder or anything.

-prisma.post.findMany({ /* your arguments  */ })
+prisma.post.findMany(pundit.filter(context, Post))

Declare your models.

// models.ts
class User {}

class Post {}

Declare your actions for each model.

// actions.ts
export type UserActions = "create" | "delete" | "update";
export type PostActions = "create" | "delete" | "update";

Declare your policies

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

export class PolicyContext {
  // your orm related properties might go here
}

export class PostPolicy extends PunditPolicy<PolicyContext, Post, PostActions> {
  authorize(context, post, action) {
    switch (action) {
      case "create":
        return true;
      case "delete":
        return false; // to be implemented...
      case "update":
        return false; // to be implemented...
    }
  }

  filter(ctx) {
    // modify context
  }
}

export class UserPolicy extends PunditPolicy<PolicyContext, User, UserActions> {
  authorize(context, post, action) {
    switch (action) {
      case "create":
        return true;
      case "delete":
        return false; // to be implemented...
      case "update":
        return false; // to be implemented...
    }
  }

  filter(ctx) {
    // modify context
  }
}

Create your Pundit instance:

// pundit.ts
import { Pundit } from "pundit-ts";
import { PostPolicy, UserPolicy } from "./policies";

export const pundit = new Pundit<PolicyContext>()
  .register(new UserPolicy())
  .register(new PostPolicy());

Authorize your actions in a fully type-safe way.

// index.ts
import { PolicyContext } from "./policies";
import { Post } from "./models";
import { pundit } from "./pundit";

const ctx = new PolicyContext();
const post = new Post();

await pundit.authorize(ctx, post, "create");