
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@bdcode/acl
Advanced tools
A TypeScript-first authorization library with RBAC-first design, multi-tenant support, and optional ABAC policies
A TypeScript-first authorization library with RBAC-first design, multi-tenant support, and optional ABAC policies. Built for modern applications with performance and developer experience in mind.
✨ RBAC-First Authorization
inventory:*, *:read, *:*)🏗️ Multi-Tenant Ready
🚀 Frontend + Backend
⚡ High Performance
🔒 Type Safe & Developer Friendly
resource:action)npm install @bdcode/acl
# or
pnpm add @bdcode/acl
# or
yarn add @bdcode/acl
import { ACL } from "@bdcode/acl";
// Create your data adapter (implement ACLAdapter interface)
const adapter = new MyDatabaseAdapter();
// Initialize ACL
const acl = new ACL({
adapter,
debug: true, // Enable debug logging
wildcardSupport: true, // Enable wildcard permissions
});
// Simple permission check
const allowed = await acl.can("user123", "posts", "read");
// With tenant (multi-tenant applications)
const allowed = await acl.can("user123", "posts", "read", "tenant-abc");
// Detailed enforcement result
const result = await acl.check({
userId: "user123",
resource: "posts",
action: "delete",
tenantId: "tenant-abc", // optional
});
console.log(result.allowed); // true/false
console.log(result.reason); // "RBAC: Allowed by permissions: posts:delete"
console.log(result.matchedRoles); // ["Editor", "Manager"]
console.log(result.matchedPermissions); // ["posts:delete"]
import { ACLClient } from "@bdcode/acl";
// Get ACL data from your API endpoint
const aclData = await fetch("/api/user/acl").then((r) => r.json());
// Create client enforcer (synchronous checks!)
const enforcer = new ACLClient(aclData);
// Check permissions in your UI
if (enforcer.can("posts", "delete")) {
// Show delete button
}
if (enforcer.hasRole("Manager")) {
// Show management features
}
// Check roles and permissions
console.log(enforcer.getRoles()); // ["Editor", "Manager"]
console.log(enforcer.getPermissions()); // ["posts:read", "posts:write", ...]
// Roles can inherit from other roles
const viewerRole = {
id: "viewer",
name: "Viewer",
permissions: [
{ resource: "posts", action: "read", effect: "allow" },
{ resource: "posts", action: "list", effect: "allow" },
],
};
const editorRole = {
id: "editor",
name: "Editor",
extendsRoleId: "viewer", // Inherits all viewer permissions
permissions: [
{ resource: "posts", action: "create", effect: "allow" },
{ resource: "posts", action: "update", effect: "allow" },
],
};
const adminRole = {
id: "admin",
name: "Administrator",
extendsRoleId: "editor", // Inherits all editor + viewer permissions
permissions: [
{ resource: "posts", action: "delete", effect: "allow" },
{ resource: "users", action: "*", effect: "allow" }, // Wildcard
],
};
// Wildcard examples
"posts:*"; // All actions on posts
"*:read"; // Read action on all resources
"*:*"; // All actions on all resources (super admin)
// The library automatically matches:
acl.can("user", "posts", "delete"); // matches "posts:*"
acl.can("user", "comments", "read"); // matches "*:read"
// Single-tenant usage (automatic)
const canRead = await acl.can("user123", "posts", "read");
// Multi-tenant usage (explicit)
const canRead = await acl.can("user123", "posts", "read", "company-a");
// The library handles tenant isolation automatically
Implement the ACLAdapter interface to connect to your data source:
import { ACLAdapter } from "@bdcode/acl";
class DrizzleACLAdapter implements ACLAdapter {
async getUser(userId: string, tenantId?: string) {
// Return user with roles
return {
id: userId,
tenantId,
roles: ["editor", "viewer"],
};
}
async getUserRoles(userId: string, tenantId?: string) {
// Return expanded roles with permissions
return roles;
}
async getRole(roleId: string, tenantId?: string) {
// Return role with permissions
return role;
}
async getRoleHierarchy(roleId: string, tenantId?: string) {
// Return role inheritance chain
return hierarchy;
}
async getUserDirectPermissions(userId: string, tenantId?: string) {
// Return direct user permissions (overrides)
return permissions;
}
async getTenant(tenantId: string) {
// Optional: Return tenant information
return tenant;
}
// ... other required methods
}
Add caching for better performance:
import { CacheAdapter } from "@bdcode/acl";
class RedisACLCache implements CacheAdapter {
async get<T>(key: string): Promise<T | null> {
const value = await redis.get(key);
return value ? JSON.parse(value) : null;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const serialized = JSON.stringify(value);
if (ttl) {
await redis.setex(key, ttl, serialized);
} else {
await redis.set(key, serialized);
}
}
async del(key: string): Promise<void> {
await redis.del(key);
}
}
// Use with ACL
const acl = new ACL({
adapter: new DrizzleACLAdapter(),
cache: new RedisACLCache(),
cacheTTL: 300, // 5 minutes
});
interface ACLConfig {
adapter: ACLAdapter; // Required: Your data adapter
cache?: CacheAdapter; // Optional: Caching layer
cacheTTL?: number; // Cache TTL in seconds (default: 300)
enablePolicies?: boolean; // Enable ABAC policies (future)
wildcardSupport?: boolean; // Enable wildcard permissions (default: true)
debug?: boolean; // Enable debug logging (default: false)
defaultTenantId?: string; // Default tenant ID (default: "default")
}
import { authorize } from "./middleware/acl";
app.use("/posts/*", authMiddleware); // Ensure user is authenticated
app.get("/posts", authorize("posts", "list"), async (c) => {
// User is authorized to list posts
return c.json({ posts: [] });
});
import { ACL } from "@bdcode/acl";
const acl = new ACL({ adapter: new MyAdapter() });
const authorize = (resource: string, action: string) => {
return async (req, res, next) => {
const allowed = await acl.can(
req.user.id,
resource,
action,
req.user.tenantId,
);
if (!allowed) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
};
app.get("/posts", authorize("posts", "list"), (req, res) => {
res.json({ posts: [] });
});
import { useACL } from "./hooks/useACL";
function PostList() {
const { can, hasRole } = useACL();
return (
<div>
{can("posts", "create") && (
<button>Create Post</button>
)}
{hasRole("Manager") && (
<AdminPanel />
)}
</div>
);
}
// src/routes/+page.svelte
<script lang="ts">
import { onMount } from "svelte";
import { ACLClient } from "@bdcode/acl";
let enforcer: ACLClient;
onMount(async () => {
const aclData = await fetch("/api/user/acl").then((r) => r.json());
enforcer = new ACLClient(aclData);
});
</script>
{#if enforcer?.can("posts", "create")}
<button>Create Post</button>
{/if}
{#if enforcer?.hasRole("Manager")}
<AdminPanel />
{/if}
can(userId, resource, action, tenantId?) - Quick permission checkcheck(context) - Detailed enforcement with reasoninggetUserACLData(userId, tenantId?) - Get user data for client enforcerclearUserCache(userId, tenantId?) - Clear cached user datacan(resource, action) - Check permission (synchronous)cannot(resource, action) - Inverse permission checkhasRole(role) - Check if user has rolegetRoles() - Get all user rolesgetPermissions() - Get all user permissionsgetTenantId() - Get current tenant IDThe library provides specific error types for different scenarios:
import {
ACLError,
UserNotFoundError,
TenantNotFoundError,
UnauthorizedError,
RoleNotFoundError,
} from "@bdcode/acl";
try {
await acl.check({ userId: "user123", resource: "posts", action: "delete" });
} catch (error) {
if (error instanceof UserNotFoundError) {
console.error("User not found:", error.message);
} else if (error instanceof UnauthorizedError) {
console.error("Access denied:", error.message);
}
}
getUserACLData() once and use ACLClient for multiple checksEnable debug mode to see detailed logging:
const acl = new ACL({
adapter: myAdapter,
debug: true, // Enable debug logging
});
// Output example:
// [ACL] Enforcement result for user123:posts:delete = true
// [ACL] Cache hit for acl:user123:default:posts:delete
// [ACL] Matched roles: ["Editor", "Manager"]
The library is currently in development. Breaking changes will be documented here as we approach v1.0.
This library is written in TypeScript and provides full type safety:
import type {
ACLConfig,
EnforcementContext,
EnforcementResult,
Permission,
Role,
User,
} from "@bdcode/acl";
// All types are fully typed and provide excellent IntelliSense
This library is part of a larger authorization framework. See the main repository for contribution guidelines.
MIT © CodeContinent
@bdcode/acl - Authorization that scales with your application 🚀
FAQs
A TypeScript-first authorization library with RBAC-first design, multi-tenant support, and optional ABAC policies
We found that @bdcode/acl demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.