| name | langchain-enterprise-rbac |
| description | Implement role-based access control for LangChain applications Use when this capability is needed. |
| metadata | {"author":"flight505"} |
LangChain Enterprise RBAC
Overview
Role-based access control for multi-tenant LangChain applications: permission models, model access gating, tenant-scoped vector stores, usage quotas, and FastAPI middleware integration.
Permission Model
enum Permission {
CHAIN_INVOKE = "chain:invoke",
CHAIN_STREAM = "chain:stream",
MODEL_GPT4O = "model:gpt-4o",
MODEL_GPT4O_MINI = "model:gpt-4o-mini",
MODEL_CLAUDE = "model:claude-sonnet",
TOOLS_EXECUTE = "tools:execute",
ADMIN_CONFIG = "admin:config",
ADMIN_USERS = "admin:users",
}
interface Role {
name: string;
permissions: Permission[];
}
const ROLES: Record<string, Role> = {
viewer: {
name: "viewer",
permissions: [Permission.CHAIN_INVOKE, Permission.MODEL_GPT4O_MINI],
},
user: {
name: "user",
permissions: [
Permission.CHAIN_INVOKE,
Permission.CHAIN_STREAM,
Permission.MODEL_GPT4O_MINI,
Permission.TOOLS_EXECUTE,
],
},
power_user: {
name: "power_user",
permissions: [
Permission.CHAIN_INVOKE,
Permission.CHAIN_STREAM,
Permission.MODEL_GPT4O_MINI,
Permission.MODEL_GPT4O,
Permission.MODEL_CLAUDE,
Permission.TOOLS_EXECUTE,
],
},
admin: {
name: "admin",
permissions: Object.values(Permission),
},
};
User and Tenant Models
interface Tenant {
id: string;
name: string;
monthlyTokenLimit: number;
tokensUsed: number;
allowedModels: string[];
}
interface User {
id: string;
tenantId: string;
role: string;
email: string;
}
function hasPermission(user: User, permission: Permission): boolean {
const role = ROLES[user.role];
if (!role) return false;
return role.permissions.includes(permission);
}
function canUseModel(user: User, tenant: Tenant, model: string): boolean {
const modelPermission = `model:${model}` as Permission;
return (
(user, modelPermission) &&
tenant..(model)
);
}
Middleware: Permission Enforcement
import { Request, Response, NextFunction } from "express";
function requirePermission(permission: Permission) {
return (req: Request, res: Response, next: NextFunction) => {
const user = req.user as User;
if (!user) {
return res.status(401).json({ error: "Authentication required" });
}
if (!hasPermission(user, permission)) {
return res.status(403).json({
error: `Missing permission: ${permission}`,
role: user.role,
});
}
next();
};
}
app.post("/api/chat",
requirePermission(Permission.CHAIN_INVOKE),
async (req, res) => {
result = chain.({ : req.. });
res.({ result });
}
);
app.(,
(.),
(req, res) => {
}
);
Model Access Control
import { ChatOpenAI } from "@langchain/openai";
import { ChatAnthropic } from "@langchain/anthropic";
class ModelGateway {
private models: Record<string, any> = {};
constructor() {
this.models["gpt-4o-mini"] = new ChatOpenAI({ model: "gpt-4o-mini" });
this.models["gpt-4o"] = new ChatOpenAI({ model: "gpt-4o" });
this.models["claude-sonnet"] = new ChatAnthropic({ model: "claude-sonnet-4-20250514" });
}
getModel(modelName: string, user: User, tenant: Tenant) {
if (!canUseModel(user, tenant, modelName)) {
throw new Error(
`User ${user.email} (role: ) cannot access model: `
);
}
(tenant. >= tenant.) {
(
);
}
.[modelName];
}
}
Tenant-Scoped Vector Store
import { PineconeStore } from "@langchain/pinecone";
class TenantScopedRetriever {
constructor(
private vectorStore: PineconeStore,
private tenantId: string,
) {}
async retrieve(query: string, k = 4) {
return this.vectorStore.similaritySearch(query, k, {
tenantId: { $eq: this.tenantId },
});
}
asRetriever(k = 4) {
return this.vectorStore.asRetriever({
k,
filter: { tenantId: { $eq: this.tenantId } },
});
}
}
async function ingestForTenant(tenantId: string, docs: any[]) {
tagged = docs.( ({
...doc,
: { ...doc., tenantId },
}));
vectorStore.(tagged);
}
Usage Quota Tracking
class UsageQuotaManager {
private usage = new Map<string, number>();
async trackUsage(tenantId: string, tokens: number) {
const current = this.usage.get(tenantId) ?? 0;
this.usage.set(tenantId, current + tokens);
}
async checkQuota(tenant: Tenant): Promise<boolean> {
const used = this.usage.get(tenant.id) ?? 0;
return used < tenant.monthlyTokenLimit;
}
async getUsageReport(tenantId: string) {
return {
tenantId,
tokensUsed: this.usage.get(tenantId) ?? 0,
period: new Date().toISOString().slice(, ),
};
}
}
{
name = ;
() { (); }
() {
tokens = output.?.?. ?? ;
..(., tokens);
}
}
Python FastAPI Equivalent
from fastapi import Depends, HTTPException
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = decode_jwt(token)
if not user:
raise HTTPException(status_code=401)
return user
def require_permission(permission: str):
async def checker(user = Depends(get_current_user)):
if permission not in ROLES[user.role]["permissions"]:
raise HTTPException(status_code=403, detail=f"Missing: {permission}")
return user
return checker
@app.post("/api/chat")
async def chat(input: dict, user = Depends(require_permission("chain:invoke"))):
return await chain.ainvoke({"input": input["text"]})
Error Handling
| Issue | Cause | Fix |
|---|
| 401 Unauthorized | Missing or invalid token | Check auth middleware, token format |
| 403 Forbidden | Insufficient permissions | Upgrade user role or add permission |
| Tenant data leak | Missing filter on vector store | Always filter by tenantId |
| Quota exceeded | High usage | Increase tenant limit or optimize |
Resources
Next Steps
Use langchain-data-handling for tenant-scoped RAG pipelines.
Source: flight505/skill-forge — distributed by TomeVault.