This is a submission for the Permit.io Authorization Challenge: AI Access Control

🦄 What I Built

I built a prototype task management API, like JIRA. You can keep track of Epics, Tasks, and Comments just like in JIRA. The app is not just a REST API, I also built a working MCP server to interact with the API.

With Permit.io, the authorization is seamless between the Hono REST API and MCP server. Cutting the development time and simplifies the policy configuration.

This project should fit into the AI Access Control and API-First Authorization category.

💻 Tech Stack

  • Hono for building REST API
  • MCP SDK for building the MCP server
  • SQLite as the database

🐋 Demo

Below is a demo when invoking the Hono REST API and MCP server using inspector.

When using the inspector, you can ask for a session code (akin to the JWT for authentication in the REST API) to access the resources.

MCP Inspector

The session token will be sent to a webhook, you can use services such as Webhook.site to receive the session code.

Webhook

The session code is formatted as SES-xxxxxx.

🤠 Next: Claude Desktop

I have tried to use the MCP server in Claude Desktop, and it worked... a little. I can login and get a session code but currently the MCP server is having some trouble to call the PDP API in the same Docker network.

I'll look into it in the future to troubleshoot the network communications between the MCP server container and PDP server.

💻 Project Repo

GitHub logo fahminlb33 / devto-permitio-mcp

A simple JIRA-like task management with Permit.io authorization (Hono + MCP)

DEV.to Permit.io Authorization Challenge

This is a simple JIRA-like task management API, you can expect feature such as Epics, Tasks, and Comments just like in JIRA.

The key point of this project is this repo contains two APIs, (1) a classic REST API using Hono.js and (2) an MCP using the MCP TypeScript SDK.

Permit.io Allowed for Easy Authorization

REST API with Hono

In traditional web API, implementing Permit.io authorization as a middleware simplifed and also centralized the authorization process, no more messy authorization check on every endpoints.

MCP Server

The same authorization technique in the web API can easily be reused in MCP server (with some changes). With Permit.io, the authorization process is "framework-agnostic" so I can effectively implement the authorization process with any framework easily.

Development Setup

To run this project, you will need NodeJS 23 and Docker.

Follow these steps:

  1. Clone this repo git clone https://github.com/fahminlb33/devto-permitio-mcp.git
  2. Install…

🛣️ My Journey

🤔 Understanding the Permit.io Concepts

Resources, Roles, are Instance Roles the top three concepts to master in this project. In this project, there are 4 resources:

  1. User
  2. Epic
  3. Task
  4. Comment

with this relationship:

  • Epic is a parent of Task
  • Task is a parent of Comment

I also created 3 different roles:

  1. Admin, can do everything
  2. Manager, can do most of the things like creating, editing, and deleting Epic and Task
  3. Developer, can be assigned/unassigned tasks, leave comments, and report status/time spent on a task

In conclusion, I implemented RBAC and ReBAC authorization for the APIs. Next, I configured the roles and actions in the Permit UI.

🧰 Policy Configuration

Policy editor 1

Policy editor 2

🔐 API-First Authorization

Implementing authorization in Hono is as simple as creating a middleware.

server.use(
  except("/api/public/*", async (c, next) => {
    const jwtPayload = c.get("jwtPayload");
    if (!jwtPayload) {
      return c.json({ error: "Unauthorized" }, HttpStatus.Unauthorized);
    }

    const path = c.req.path.split("/");
    if (path.length < 2) {
      return c.notFound();
    }

    const { resource, action } = getResourceActionFromReq(
      c.req.method,
      c.req.path,
      jwtPayload.role !== "Admin",
    );

    const permitted = await permit.check(jwtPayload.sub, action, resource);
    if (!permitted) {
      return c.json({ error: "Permission denied" }, HttpStatus.Forbidden);
    }

    await next();
  }),
);

🤖 Authorization for AI Applications with Permit.io

Authorization in the MCP SDK essenstialy the same, but with a custom user lookup. Also, since the MCP SDK does not have the concept of middleware, this authorizeTool function wraps the tool handler with the authorization routine.

export function authorizeTool<T extends z.ZodRawShape>(
  action: string,
  resourceName: string,
  cb: (
    data: z.objectOutputType<T, z.ZodTypeAny>,
    user: { id: string; role: UserRole },
  ) => Promise<CallToolResult>,
): ToolCallback<T> {
  // @ts-ignore
  return async (
    args: z.objectOutputType<T, z.ZodTypeAny>,
    extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
  ): Promise<CallToolResult> => {
    // query the user data
    const rows = await db
      .select()
      .from(sessionsTable)
      .innerJoin(usersTable, eq(usersTable.id, sessionsTable.user_id))
      .where(eq(sessionsTable.code, args.sessionCode));

    if (rows.length === 0) {
      return {
        content: [
          {
            type: "text",
            text: MessageConstants.Forbidden,
          },
        ],
      };
    }

    // authorize with Permit.io
    const user = rows[0];

    const permitted = await permit.check(user.users.id, action, resourceName);
    if (!permitted) {
      return {
        content: [
          {
            type: "text",
            text: MessageConstants.Forbidden,
          },
        ],
      };
    }

    return await cb(args, {
      id: user.users.id,
      role: user.users.role as UserRole,
    });
  };
}

🚧 Challenges

Getting used to the concepts of user, resource instance, role assignment, etc. is quite challenging at first, but when I started to tinker with the Permit.io App, it was quite easy to understand.

ReBAC require the use of local PDP server. I find out the hard way when my PDP check always failed. When testing with Postman to the cloud PDP endpoint, the response indicated that to use ReBAC you have to use the local PDP.

Some bulk operations are not yet available in the permitio npm package such as the bulk delete resource instances. Not a problem though since the Redoc API documentation has a thorough example for the API.

🐠 Conclusion

This project demonstrated the flexibility of Permit.io for authorization in a classic REST API using Hono library and MCP SDK.

Permit.io allowed seamless and flexible authorization for both classic REST APIs and MCP server. With a wide range of authorization policies, enforcing auth policy is as simple as calling permit.check().

You can check the GitHub repo for more information. You can access the source code, Postman collection, and tutorial on how to run the project and test it yourself.