Fine-grained authorization for AI agents using OpenFGA.
AI agents are getting access to production systems - databases, APIs, file systems. But who decides what they can do? Traditional RBAC wasn't designed for autonomous agents that make decisions without human approval.
This demo shows how to implement Relationship-Based Access Control (ReBAC) for AI agents using OpenFGA, with three levels of authorization:
- Team Level - Which teams can use which tool categories
- Project Level - Which projects have access to which resources
- Operation Level - Fine-grained control over destructive operations
# Start services and seed data
make setup
# Run the demo
make demo
# Or use the interactive script
./scripts/demo.sh| Problem | Solution |
|---|---|
| Agent has same permissions as user | Fine-grained, context-aware permissions |
| Prompt injection → data exfiltration | Blast radius contained by project scope |
| No audit trail for agent actions | Every authorization decision logged |
| "All or nothing" tool access | Operation-level restrictions |
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AI Agent │────▶│ Gateway │────▶│ OpenFGA │
│ (Claude/GPT) │ │ (Go) │ │ (AuthZ) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ MCP Tools │
│ (Mock/Real) │
└─────────────────┘
Three levels of access control:
Teams are assigned to tool categories.
| Team | Code Tools | Communication | Data Tools | Infrastructure |
|---|---|---|---|---|
| Engineering | ✅ | ✅ | ✅ | ❌ |
| Marketing | ❌ | ✅ | ❌ | ❌ |
| DevOps | ✅ | ✅ | ✅ | ✅ |
Projects have specific tool access.
| Project | GitHub | Slack | Postgres | Filesystem |
|---|---|---|---|---|
| auth-service | ✅ | ✅ | ✅ | ❌ |
| landing-page | ❌ | ✅ | ❌ | ❌ |
| infrastructure | ✅ | ✅ | ✅ | ✅ |
Specific operations are restricted to certain roles.
| Operation | Allowed Roles |
|---|---|
github:delete_repo |
Team Leads only |
postgres:delete |
Team Leads only |
| # | Scenario | User | Tool | Expected |
|---|---|---|---|---|
| 1 | Engineering → GitHub | Alice (lead) | github:create_pr |
✅ Allowed |
| 2 | Marketing → GitHub | Bob (lead) | github:create_pr |
❌ Denied |
| 3 | auth-service → Postgres | Alice | postgres:query |
✅ Allowed |
| 4 | landing-page → Postgres | Bob | postgres:query |
❌ Denied |
| 5 | Member → delete_repo | Charlie | github:delete_repo |
❌ Denied |
| 6 | Lead → delete_repo | Alice | github:delete_repo |
✅ Allowed |
| Service | Port | Description |
|---|---|---|
| Demo UI | 3002 | Interactive demo interface |
| Admin UI | 3001 | Permission management dashboard |
| OpenFGA Playground | 3000 | OpenFGA visual explorer |
| Gateway API | 9000 | Authorization gateway |
| OpenFGA API | 8080 | OpenFGA HTTP API |
# Install dependencies
make install-deps
# Start OpenFGA
make setup-openfga
# Run gateway in mock mode
make dev-gateway
# Run Demo UI
make dev-demo
# Run Admin UI
make dev-admincurl -X POST http://localhost:9000/v1/tools/call \
-H "Content-Type: application/json" \
-d '{
"tool": "github",
"operation": "create_pr",
"params": {"title": "My PR"},
"context": {
"user": "alice",
"project": "auth-service",
"team": "engineering"
}
}'curl http://localhost:9000/v1/toolscurl http://localhost:9000/v1/auditcurl http://localhost:9000/v1/demo/scenariosagentic-authz/
├── gateway/ # Go authorization gateway
│ ├── cmd/gateway/ # Main entry point
│ └── internal/ # Internal packages
│ ├── authz/ # OpenFGA client
│ ├── config/ # Configuration
│ ├── handler/ # HTTP handlers
│ └── mcp/ # MCP proxy
├── demo-ui/ # Next.js demo interface
├── admin-ui/ # Next.js admin dashboard
├── openfga/ # Authorization model
│ ├── model.fga # FGA DSL model
│ ├── model.json # JSON model for API
│ └── tuples.json # Seed data
├── scripts/ # Demo scripts
├── docker-compose.yml # Service orchestration
└── Makefile # Build commands
The authorization model uses OpenFGA's DSL:
type tool
relations
define category: [tool_category]
define allowed_team: [team]
define can_use: member from allowed_team or can_use from category
type tool_operation
relations
define tool: [tool]
define allowed_role: [user]
define can_execute: allowed_role or can_use from tool
make help # Show all commands
make setup # Full setup
make demo # Run all demo scenarios
make audit # View audit log
make tools # List available tools
make users # List demo users
make projects # List demo projects
make clean # Stop and clean upUser: "Summarize our Q4 sales data"
Agent: *has database access*
Agent: SELECT * FROM users; DROP TABLE users;--
Without fine-grained authorization, a compromised or manipulated agent can:
- Access data outside its task scope
- Perform destructive operations
- Pivot between systems
- Exfiltrate sensitive information
User: "Summarize our Q4 sales data"
Agent: *requests database access*
Gateway: Check(user:alice, can_execute, tool_operation:postgres:query)
OpenFGA: ✅ ALLOWED (project:sales-dashboard has postgres access)
Agent: SELECT SUM(revenue) FROM sales WHERE quarter='Q4'
With this architecture:
- Every tool call is authorized
- Permissions are scoped to project context
- Destructive operations require elevated privileges
- All decisions are audited
When AI coding agents work on security-critical codebases, the blast radius of a compromised or misdirected agent is much larger than in a typical project. This pattern applies directly to confidential computing stacks where certain components must never be touched by an agent.
Example: Confidential AI inference platform (Intel TDX)
A typical confidential AI stack has components where agent write access would be catastrophic:
| Component | Why it matters |
|---|---|
cvm/attestation-service/ |
TDX quote generation and EKM validation logic |
cvm/auth-service/ |
Bearer token auth and HMAC comparison |
cvm/cert-manager/ |
TLS cert and EKM shared secret |
A developer running a frontend review agent should have no access to these components. With agentic-authz, this is expressed as the absence of tuples — no tuple written means no access, full stop.
# Grant the frontend review agent access to the frontend only
fga tuple write \
'{"object":"repo:my-project/frontend","relation":"reader","user":"agent:frontend-review-agent"}'
# No tuples written for attestation-service, auth-service, cert-manager.
# The agent cannot read or write those components regardless of what it "decides."
# Verify
fga query check --user agent:frontend-review-agent \
--relation reader --object repo:my-project/attestation-service
# { "allowed": false }Connecting attestation identity to authorization
For platforms using attested TLS, the TEE's measurement (e.g. app_compose_hash from a TDX attestation report) can serve as the identity anchor in OpenFGA tuples:
# After verifying the TEE attestation, extract the identity
tee_identity = report.tdx.app_compose_hash # deterministic per deployment
allowed = await fga.check(
user=f"tee:{tee_identity}",
relation="can_invoke",
object="service:inference-endpoint"
)This gives confidential AI platforms a full authorization layer on top of their existing attestation stack: the aTLS layer handles authentication (who is connecting), OpenFGA handles authorization (what they can do).
Built by Siddhant Khare - OpenFGA Core Maintainer
- X: @siddhant_K_code
- GitHub: @Siddhant-K-code
- LinkedIn: Siddhant Khare
Helping teams implement secure AI agent architectures.
MIT