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
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' });
}
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);
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"]
}'
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']
});
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
});
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)