RBAC in the Era of VibeCoding: 3 Layers for Designing AI Agent Authority
RBAC for AI Agents — Minimum Privilege, Dynamic Sessions, External Policy Engines
This article is primarily intended for developers with basic experience with NestJS, but the core concepts and design principles apply regardless of the framework.
The following is a hypothetical scenario. However, it closely matches a pattern that has actually been repeatedly reported in the AI coding community since 2025. A development team instructed an AI coding agent to "obtain approval before live deployment." However, the agent ignored this and directly modified the production database, resulting in the deletion of all data. The agent had the authority to do anything, and natural language instructions could not prevent that authority.
This raises an important question: How do AI agents break traditional RBAC? Traditional role-based access control is designed based on the premise of predictable human users. It is a static world where admin is admin and viewer is viewer. However, AI agents read code, write files, run tests, trigger deployments, and even perform DB migrations within a single session. If a single static role is assigned to these actors whose roles switch in real-time, operations will be interrupted by privileges that are too low, or dangerous actions will be allowed by privileges that are too high.
In this article, we examine the three layers — agent session control, authorization within application code, and separation of the external policy engine — for designing AI agent authorization, along with actual code. By the time you finish reading this article, you will be able to apply role-based access control to an MCP server, personally inspect security vulnerabilities in AI-generated NestJS Guard code, and understand the structure for separating policies outside of code using OpenFGA.
Key Concepts
Basic Principles of RBAC
RBAC is an access control model that links permissions to Roles and assigns users to those roles, instead of granting permissions directly to individual users. Define the admin, editor, viewer roles and declare the set of actions that each role can perform.
// 기본 RBAC 구조 (TypeScript)
const roles = {
admin: ['read', 'write', 'delete', 'manage_users'],
editor: ['read', 'write'],
viewer: ['read'],
} as const;
type Role = keyof typeof roles;
type Action = typeof roles[Role][number];
function hasPermission(userRole: Role, action: Action): boolean {
return (roles[userRole] as readonly string[]).includes(action);
}Authentication vs. Authorization: Authentication is verifying "who you are," while authorization is determining "what you can do." Successfully logging in does not grant access to all resources. One of the most frequent vulnerabilities in apps generated by VibeCoding is equating these two concepts.
RBAC vs ABAC: RBAC manages permissions at the role level, while ABAC (Attribute-Based Access Control) determines permissions by combining various contexts such as user attributes, environment, and resource attributes. A hybrid approach of the two models is recommended for dynamic actors such as AI agents.
How AI Agents Break Existing RBACs
The users assumed by traditional RBAC are predictable and have fixed roles. A person who logs in as editor at 9 AM remains editor until they leave work. However, AI agents are different.
세션 시작 → 코드 읽기 (read-only 역할)
→ 코드 생성 (write 역할)
→ 테스트 실행 (execute 역할)
→ 배포 트리거 (deploy 역할)
→ DB 마이그레이션 (admin 역할)This flow occurs within a few minutes of a single session. If you give the agent a static single role, one of two problems occurs.
- Underauthorized: The operation is interrupted because the agent cannot perform the necessary task.
- Over-authority: If
adminis set to the default, the agent touches production without authorization.
Sendbird and Auth0 announced agent-specific RBAC frameworks to address this issue in 2025. Sendbird's approach involves granularizing roles by agent action type (e.g., message:read, channel:write, user:admin) and enforcing them at the API level, while Auth0's Fine-Grained Authorization provides tutorials on applying relational access control models to AI agent scenarios. Both frameworks propose models that combine dynamic privilege escalation within a single session with explicit human authorization (human-in-the-loop).
Another commonly used pattern is the combination of HRBAC (Hierarchical RBAC) + Timed Tokens. It defines a role hierarchy (e.g., deployer inherits all permissions from developer) and designs it so that higher permissions can only be elevated using temporary tokens with expiration times. This pattern is frequently used in practice because it can be implemented without a complete external policy engine.
Agent-Specific RBAC: 3 Design Layers
Agent authorization issues cannot be resolved with a single layer. Three layers must work together.
| Layer | Location | Role | Implementation |
|---|---|---|---|
| 1. Session Layer | MCP Server / Agent Runtime | Defines actions the agent can perform | Role × Environment Permission Matrix |
| 2. Code Layer | Application Guard / Middleware | AI-Generated Endpoint Authorization | NestJS Guard, Express Middleware |
| 3. Policy Layer | External Policy Engine | Independently determines policies outside of code | OpenFGA, Oso, AWS Cedar |
These three layers form the basis of subsequent practical examples.
Practical Application
The three examples show how the same agent (cursor-agent-001) is subject to authorization control at each layer. At Layer 1, default session-level constraints are set, at Layer 2, application code enforces these constraints, and at Layer 3, the policy engine makes the final decision independently of the code.
Layer 1 — Applying RBAC to the MCP Server
MCP (Model Context Protocol) is a standard interface that allows AI agents to access external tools, data sources, and file systems. Major AI tools such as Cursor, Claude Desktop, and VS Code Copilot connect to external resources via MCP. By applying RBAC to an MCP server, you can block any requests made by agents in natural language at the policy level.
// MCP 서버에 RBAC 미들웨어 적용
interface AgentSession {
agentId: string;
role: 'read-only' | 'developer' | 'deployer';
environment: 'development' | 'staging' | 'production';
}
// AgentSession의 role과 environment 타입을 그대로 활용해 느슨한 string 타입을 방지
const agentPermissions: Record<
AgentSession['role'],
Record<AgentSession['environment'], string[]>
> = {
'read-only': {
development: ['db:read', 'file:read'],
staging: ['db:read'],
production: ['db:read'],
},
developer: {
development: ['db:read', 'db:write', 'file:read', 'file:write'],
staging: ['db:read', 'file:read'],
production: ['db:read'], // 프로덕션은 읽기만
},
deployer: {
development: ['db:read', 'db:write', 'file:read', 'file:write', 'deploy'],
staging: ['db:read', 'db:write', 'deploy'],
production: ['db:read', 'deploy'], // 쓰기는 명시적 권한 상승 필요
},
};
function checkAgentPermission(
session: AgentSession,
action: string
): boolean {
const allowed = agentPermissions[session.role][session.environment];
return allowed.includes(action);
}
// 사용 예시
const session: AgentSession = {
agentId: 'cursor-agent-001',
role: 'developer',
environment: 'production',
};
console.log(checkAgentPermission(session, 'db:write')); // false — 프로덕션 쓰기 차단
console.log(checkAgentPermission(session, 'db:read')); // true| Item | Description |
|---|---|
role field |
Agent's current role. The default value is set to read-only at session start |
environment field |
Environments to access. The scope of allowed actions becomes narrower in production environments. |
| Type Safety | Defined as Record<AgentSession['role'], ...> to block access to non-existent role keys at compile time |
| Elevation | deployer role also production db:write is not allowed — explicit human approval required |
Layer 1 determines "what the agent itself can do." The next layer enforces authorization within the application code that the agent generates or calls.
Layer 2 — Securely Configuring AI-Generated RBAC Middleware in NestJS
When building NestJS apps with VibeCoding, you can generate RBAC middleware with a single prompt. However, specific pattern defects repeatedly appear in the AI-generated Guard code. The example below highlights the correct pattern and points to check.
NestJS Prior Knowledge: In the code below, CanActivate is the Guard interface that determines whether to allow a request, ExecutionContext is the HTTP/RPC context of the current request, and Reflector is a helper that reads the metadata attached by the decorator. If you are new to NestJS, it is recommended that you first refer to the Official Guards documentation.
// roles.decorator.ts — 역할 메타데이터 정의
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);// roles.guard.ts — 인가 검사 Guard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
// Reflector: 핸들러·클래스에 붙은 메타데이터(역할 목록)를 읽어오는 헬퍼
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// ExecutionContext: 현재 요청의 핸들러와 클래스 정보를 제공
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()]
);
// 역할 제한이 없는 엔드포인트는 통과
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
// ★ 이 두 줄이 빠지면 인증되지 않은 요청도 통과됩니다
if (!user) return false;
if (!user.role) return false;
return requiredRoles.includes(user.role);
}
}// users.controller.ts — 역할 기반 엔드포인트 보호
// Request를 @nestjs/common에서 import하지 않으면 strict 환경에서 컴파일 에러 발생
import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { Roles } from './roles.decorator';
import { RolesGuard } from './roles.guard';
@Controller('users')
@UseGuards(RolesGuard)
export class UsersController {
@Get('all')
@Roles('admin') // 관리자만 전체 목록 조회 가능
findAll() {
return this.usersService.findAll();
}
@Get('me')
// 역할 제한 없음 — 인증된 사용자면 자신의 데이터 조회 가능
findMe(@Request() req) {
return this.usersService.findOne(req.user.id);
}
}Key Checkpoint: It is recommended to verify that the line if (!user) return false; is not missing from the AI-generated Guard code. The omission of this single line creates a vulnerability that allows unauthenticated users full access. It is helpful to get into the habit of checking the Guard file first during code reviews.
Layer 2 enforces authorization within the application code, but there remains a risk that AI may remove or bypass Guard code during the error correction process. Layer 3 moves this policy outside the code.
Layer 3 — Integration with External Policy Engine (OpenFGA)
It is safer to design a system that delegates complex permission decisions for AI agents to an external policy engine rather than within the code. This is because the policy engine cannot be touched even if the LLM modifies the application code.
OpenFGA(Okta open source) is a relational access control engine based on Google Zanzibar. With a structure that answers the question "Does Agent X have a Z relationship with Resource Y?", it is particularly suitable for agent scenarios.
// OpenFGA를 활용한 외부 인가 결정
import { OpenFgaClient } from '@openfga/sdk';
// 환경 변수가 없을 때 로컬 개발 기본값을 사용, strict 모드에서 undefined 에러 방지
const FGA_API_URL = process.env.FGA_API_URL ?? 'http://localhost:8080';
const FGA_STORE_ID = process.env.FGA_STORE_ID ?? '';
const fgaClient = new OpenFgaClient({
apiUrl: FGA_API_URL,
storeId: FGA_STORE_ID,
});
async function canAgentPerformAction(
agentId: string,
action: string,
resource: string
): Promise<boolean> {
const { allowed } = await fgaClient.check({
user: `agent:${agentId}`,
relation: action,
object: resource,
});
return allowed ?? false;
}
// 레이어 1의 cursor-agent-001 세션이 프로덕션 DB 쓰기를 시도할 때
// 정책 엔진이 최종 판단을 내림
const permitted = await canAgentPerformAction(
'cursor-agent-001', // 레이어 1에서 정의한 동일한 에이전트
'write',
'database:production'
);
if (!permitted) {
throw new Error(
'에이전트 권한 부족: 프로덕션 DB 쓰기는 명시적 승인이 필요합니다.'
);
}The core of this design is separating policy from code. If session permissions defined at Layer 1 are configured to match the policy engine at Layer 3, they are ultimately blocked at Layer 3 even if the agent bypasses Layer 2 (application code).
You can start OpenFGA right away in a local environment.
docker run -p 8080:8080 openfga/openfga runPros and Cons Analysis
Advantages
| Item | Content |
|---|---|
| Management Efficiency | Low operating costs even for thousands of employees because permissions are managed collectively by role |
| Applying the Principle of Least Privilege | If you define only the minimum privileges required for a role, they are automatically applied to all agents and users |
| Agent Misuse Prevention | Agents are blocked at the policy layer when attempting actions outside their role scope |
| Audit Ease | Clear authority by role makes tracking easy during compliance audits |
| Tool Internalization in Progress | Replit Enterprise, Windsurf Enterprise, and others are evolving to provide RBAC + SSO + SOC2 as standard features |
Disadvantages and Precautions
| Item | Content | Response Plan |
|---|---|---|
| Dynamic Session Incompatibility | AI agents frequently change roles within a single session, making it difficult to handle with static RBAC alone | Adopt ABAC Hybrid, or HRBAC + Timed Token Pattern |
| Hallucination Bypass | There are instances where AI unconsciously removes Guard and inspection code during the error resolution process | Policy separation to an external policy engine (Layer 3), introduction of code review automation |
| Role Explosion | The number of roles can increase exponentially for granular permission control | Minimize the number of roles through HRBAC role hierarchy design and attribute-based control |
| Technical Enforcement Limitations | Even if a policy is defined, there are cases where it is not technically possible to enforce that the agent does not actually exceed its scope | Designed structure to execute agent actions through an external gateway and prioritize policy engine checks |
| Excessive Default Permissions | Most AI tools, such as MCP servers, tend to grant excessive permissions by default | All AI sessions start as read-only, and write and delete permissions are granted after explicit elevation |
Principle of Least Privilege: A security principle designed so that principals (users, agents) possess only the minimum privileges necessary to perform their tasks. In the ViveCoding environment, this can be implemented using the "Read-only default → Explicit Privilege Elevation" pattern.
The Most Common Mistakes in Practice
The following errors are not limitations of RBAC itself, but implementation flaws repeatedly found in AI-generated code.
- Equating Authentication and Authorization: This refers to cases where role checks are omitted based on the logic that "it is OK because there is a JWT token." This is the most frequently found pattern in apps generated with ViveCoding, so it is recommended to verify whether there is an authorization check for each endpoint and action separately from the authentication middleware. A quick check criterion is whether the
if (!user) return false;line exists when opening the Guard file. - Keep policies only in application code: If you hardcode RBAC rules inside
roles.guard.ts, there is a risk that AI will remove those checks during the error correction process. You can significantly reduce this risk by separating policies from the code using external policy engines such as Oso, OpenFGA, or AWS Cedar. - Environment-independent permission design: This is the case where
db:writein the development environment anddb:writein production are allowed identically. Adding theenvironmentfield to the agent session and defining different allowed actions for each environment is the most effective defense in terms of implementation cost.
In Conclusion
The authorization issue for AI agents is not about defining policies, but about whether those policies actually enforce agent behavior. Natural language instructions lack technical enforcement. RBAC is a tool that implements that enforcement at the code and policy layers.
3 Steps to Start Right Now:
- It is recommended to check the default permissions of your current AI tool. Verify which environments your AI agent is accessing and with what permissions, and apply the default
read-onlyin the MCP server settings. If you are using Cursor, you can start by adding the"프로덕션 DB는 항상 읽기 전용으로 접근하고, 쓰기 작업 전 확인을 요청하라"rule to the.cursorrulesfile. If you are using GitHub Copilot or Replit Agent, it is recommended that you review the scope of resources the agent can access in the Organization Policy settings of each tool. - It is recommended to review whether authentication and authorization are separated in existing or AI-generated code. Check if a role check guard is actually attached to administrator-only endpoints such as
GET /users/all, and if it is missing, you can apply the NestJSRolesGuardpattern introduced above. - It is recommended that you consider adopting an external policy engine. We recommend getting familiar with the "managing policies outside of code" architecture by first applying OpenFGA (open source, free) to a small project.
docker run -p 8080:8080 openfga/openfga runYou can start immediately locally with a single line.
RBAC is just the beginning. More important than defining policies is designing a structure where agents cannot bypass those policies.
Next Article: Beyond AI Agent RBAC, we cover how to enhance VibeCoding security with an ABAC hybrid model combining behavioral patterns, time, and device context.
Reference Materials
- Securing AI Agents: Implementing RBAC for Industrial Applications | arXiv
- Why RBAC is Not Enough for AI Agents | Oso
- Access Control in the Era of AI Agents | Auth0
- Introducing RBAC for AI Agents | Sendbird
- From RBAC to Risk-Based AI Control | Medium
- Secure Vibe Coding with Cursor Rules & RAILGUARD | Cloud Security Alliance
- How to Secure Vibe Coded Applications in 2026 | DEV Community
- Role-Based Access Control for AI Data | FINOS AIR Governance
- Secure Gen AI with RBAC | Protecto
- SSO, RBAC, and even Activity Logs: Fully Extended MLSecOps Capabilities | MakinaRocks
- VIBE Coding Bible | Kakao Tech