DEV Community

Alex Spinov
Alex Spinov

Posted on

Permit.io Has a Free API — Here's How to Add Fine-Grained Authorization to Any App

A developer I know hardcoded role checks everywhere: if (user.role === 'admin'). When the PM asked for 'editor can publish but not delete' — he had to change 47 files. With Permit.io, it's one policy change in a dashboard.

What Permit.io Offers for Free

Permit.io free tier:

  • 1,000 monthly active users
  • RBAC (Role-Based Access Control)
  • ABAC (Attribute-Based Access Control)
  • ReBAC (Relationship-Based Access Control)
  • Visual policy editor — no code needed
  • SDK for all languages — Node.js, Python, Go, Java, .NET
  • Audit logs — who tried to access what
  • Multi-tenant — per-tenant permissions
  • Open source PDP (Policy Decision Point)

Quick Start

npm install permitio
Enter fullscreen mode Exit fullscreen mode
const { Permit } = require('permitio');

const permit = new Permit({
  pdp: 'https://cloudpdp.api.permit.io',
  token: process.env.PERMIT_API_KEY
});

// Check permission
const allowed = await permit.check('user_123', 'update', 'document');

if (allowed) {
  // User can update documents
} else {
  res.status(403).json({ error: 'Forbidden' });
}
Enter fullscreen mode Exit fullscreen mode

Express Middleware

function authorize(action, resource) {
  return async (req, res, next) => {
    const allowed = await permit.check(
      req.user.id,
      action,
      { type: resource, id: req.params.id, tenant: req.user.orgId }
    );

    if (!allowed) {
      return res.status(403).json({ error: 'Not authorized' });
    }
    next();
  };
}

// Usage
app.get('/documents/:id', authorize('read', 'document'), getDocument);
app.put('/documents/:id', authorize('update', 'document'), updateDocument);
app.delete('/documents/:id', authorize('delete', 'document'), deleteDocument);
Enter fullscreen mode Exit fullscreen mode

REST API

# Check permission
curl -X POST 'https://cloudpdp.api.permit.io/allowed' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "user": {"key": "user_123"},
    "action": "update",
    "resource": {"type": "document", "key": "doc_456", "tenant": "org_789"}
  }'

# Assign role to user
curl -X POST 'https://api.permit.io/v2/facts/org_789/role_assignments' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "user": "user_123",
    "role": "editor",
    "resource_instance": "document:doc_456"
  }'

# Create a role
curl -X POST 'https://api.permit.io/v2/schema/org_789/roles' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "key": "editor",
    "name": "Editor",
    "permissions": ["document:read", "document:update", "document:publish"]
  }'
Enter fullscreen mode Exit fullscreen mode

Define Resources and Permissions

// Via API (or use the visual editor in dashboard)
await permit.api.resources.create({
  key: 'document',
  name: 'Document',
  actions: {
    read: {},
    create: {},
    update: {},
    delete: {},
    publish: {},
    share: {}
  }
});

// Create roles
await permit.api.roles.create({
  key: 'viewer',
  name: 'Viewer',
  permissions: ['document:read']
});

await permit.api.roles.create({
  key: 'editor',
  name: 'Editor',
  permissions: ['document:read', 'document:create', 'document:update', 'document:publish']
});

await permit.api.roles.create({
  key: 'admin',
  name: 'Admin',
  permissions: ['document:read', 'document:create', 'document:update', 'document:delete', 'document:publish', 'document:share']
});
Enter fullscreen mode Exit fullscreen mode

Multi-Tenant Permissions

// User is admin in Org A but viewer in Org B
await permit.api.roleAssignments.assign({
  user: 'user_123',
  role: 'admin',
  tenant: 'org_a'
});

await permit.api.roleAssignments.assign({
  user: 'user_123',
  role: 'viewer',
  tenant: 'org_b'
});

// Check with tenant context
const canDelete = await permit.check('user_123', 'delete', {
  type: 'document',
  tenant: 'org_a' // true — user is admin here
});

const canDeleteInB = await permit.check('user_123', 'delete', {
  type: 'document',
  tenant: 'org_b' // false — user is only viewer here
});
Enter fullscreen mode Exit fullscreen mode

Need to scrape data with access controls? Check out my web scraping actors on Apify — managed data collection.

Need authorization for your app? Email me at spinov001@gmail.com.

Top comments (0)