Generic authorization engine (Gates + Policies) for TypeScript. Inspired by Laravel authorization.
Library only — no roles, permissions, tenants, or URLs from any product are embedded.
npm install @coderic/aclGitHub Packages (org Coderic):
# .npmrc
@coderic:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}npm install @coderic/acl@^0.1.0Repo: https://github.com/Coderic/coderic-acl
| Import | Contents |
|---|---|
@coderic/acl |
Registry, gates, policies, claims parsing, AuthPrincipal types |
@coderic/acl/hono |
createRequireAuth for Cloudflare Workers / Hono |
@coderic/acl/react |
AuthzProvider, RequireAbility |
import {
allow,
deny,
denyAsNotFound,
createRegistry,
parseClaims,
hasAnyRole,
hasPermission,
} from "@coderic/acl";
import type { AuthPrincipal, ClaimsConfig } from "@coderic/acl";
const claimsConfig: ClaimsConfig = {
rolesClaim: "https://example.com/roles",
permissionsClaim: "https://example.com/permissions",
};
const registry = createRegistry()
.before((user, ability) => (user?.roles.includes("sudo") ? true : null))
.defineGate("billing.read", (user) =>
user && hasPermission(user, "read:billing") ? allow() : deny("Forbidden"),
)
.registerPolicy("invoice", {
update(user, invoice) {
return user?.id === invoice.owner_id ? allow() : deny("Not owner");
},
});
registry.authorize(user, "invoice.update", invoice);
const principal = parseClaims(jwtPayload, claimsConfig);import { createRequireAuth } from "@coderic/acl/hono";
const requireAuth = createRequireAuth({
resolveUser: async (c) => { /* return AuthPrincipal | null */ },
});
app.get("/account", requireAuth, (c) => c.json(c.get("user")));import { AuthzProvider, RequireAbility } from "@coderic/acl/react";
<AuthzProvider registry={registry} user={principal}>
<RequireAbility ability="billing.read">
<BillingPanel />
</RequireAbility>
</AuthzProvider>Portal repos keep auth/gates.ts and auth/registry.ts at repo root (portal-specific rules). The dashboard imports buildRegistry() from there.
MIT