cookbook/05_agent_os/rbac/README.md
This directory contains examples demonstrating AgentOS's RBAC (Role-Based Access Control) system with JWT authentication.
AgentOS RBAC provides fine-grained access control using JWT tokens with scopes.
Important: RBAC is opt-in. Router-level authorization checks (scope validation, resource filtering) only run when authorization=True is set on the JWT middleware. Without this flag, JWT tokens are validated but all resources are accessible.
The system supports:
aud claim must match the AgentOS IDRBAC on AgentOS is compatible with the AgentOS Control Plane. When connecting your AgentOS, you'll need to enable "Authorization" for traffic from the control plane to have the required JWT token with the correct scopes.
See the documentation for more information about AgentOS Security.
Note: Only Asymmetric keys are supported for AgentOS Control Plane traffic. The public key will be provided by the control plane when you connect your AgentOS.
AgentOS supports both symmetric and asymmetric JWT signing:
| Algorithm | Type | Use Case |
|---|---|---|
| RS256 (default) | Asymmetric | Production - uses public/private key pairs |
| HS256 | Symmetric | Development/testing - uses shared secret |
Most examples in this directory use HS256 (symmetric) for simplicity. This allows running examples without setting up key pairs. For production deployments, we recommend RS256 asymmetric keys.
For an asymmetric key example, see asymmetric/basic.py which demonstrates:
The aud (audience) claim in JWT tokens is used to verify the token is intended for this AgentOS instance:
# Token payload must include aud claim matching AgentOS ID
payload = {
"sub": "user_123",
"aud": "my-agent-os", # Must match AgentOS ID
"scopes": ["agents:read", "agents:run"],
"exp": datetime.now(UTC) + timedelta(hours=24),
}
When verify_audience=True, tokens with a mismatched aud claim will be rejected with a 401 error.
agent_os:admin
Grants full access to all endpoints and resources.
Format: resource:action
Examples:
system:read # Read system configuration
agents:read # List all agents
agents:run # Run any agent
teams:read # List all teams
workflows:read # List all workflows
Format: resource:<resource-id>:action
Examples:
agents:my-agent:read # Read specific agent
agents:my-agent:run # Run specific agent
agents:*:run # Run any agent (wildcard)
teams:my-team:read # Read specific team
teams:*:run # Run any team (wildcard)
The system checks scopes in this order:
agents:*:runList endpoints automatically filter results based on user scopes:
Examples:
# User with specific agent scopes:
# ["agents:agent-1:read", "agents:agent-2:read"]
GET /agents -> Returns only agent-1 and agent-2
# User with wildcard resource scope:
# ["agents:*:read"]
GET /agents -> Returns all agents
# User with global resource scope:
# ["agents:read"]
GET /agents -> Returns all agents
# User with admin:
# ["agent_os:admin"]
GET /agents -> Returns all agents
Run endpoints check for matching scopes with resource context:
Valid scope patterns for running agent "web-agent":
agents:web-agent:run (specific agent)agents:*:run (any agent - wildcard)agents:run (global scope)agent_os:admin (full access)See symmetric/basic.py for a simple example using HS256 symmetric keys.
See asymmetric/basic.py for RS256 asymmetric key example (recommended for production).
See symmetric/agent_permissions.py for custom scope mappings per agent.
See symmetric/advanced_scopes.py for comprehensive examples of:
There are two ways to enable RBAC:
Option A: Via AgentOS parameters (recommended)
from agno.os import AgentOS
agent_os = AgentOS(
id="my-agent-os",
agents=[agent1, agent2],
authorization=True, # Enable RBAC
authorization_config=AuthorizationConfig(
verification_keys=["your-public-key-or-secret"], # Or set JWT_VERIFICATION_KEY env var
algorithm="RS256", # Default; use "HS256" for symmetric keys
),
)
app = agent_os.get_app()
Option B: Via JWTMiddleware directly
from agno.os import AgentOS
from agno.os.middleware import JWTMiddleware
agent_os = AgentOS(
id="my-agent-os",
agents=[agent1, agent2],
)
app = agent_os.get_app()
app.add_middleware(
JWTMiddleware,
verification_keys=["your-public-key-or-secret"],
algorithm="RS256",
authorization=True, # Enable RBAC - without this, scopes are NOT enforced
)
Note: When authorization=False (or not set), JWT tokens are still validated but scope-based access control is disabled - all authenticated users can access all resources.
import jwt
from datetime import datetime, timedelta, UTC
# Admin user
admin_token = jwt.encode({
"sub": "admin_user",
"aud": "my-agent-os",
"scopes": ["agent_os:admin"],
"exp": datetime.now(UTC) + timedelta(hours=24),
}, "your-secret", algorithm="HS256")
# Power user (global access to all agents)
power_user_token = jwt.encode({
"sub": "power_user",
"aud": "my-agent-os",
"scopes": [
"agents:read", # List all agents
"agents:*:run", # Run any agent
],
"exp": datetime.now(UTC) + timedelta(hours=24),
}, "your-secret", algorithm="HS256")
# Limited user (specific agents only)
limited_token = jwt.encode({
"sub": "limited_user",
"aud": "my-agent-os",
"scopes": [
"agents:agent-1:read",
"agents:agent-1:run",
],
"exp": datetime.now(UTC) + timedelta(hours=24),
}, "your-secret", algorithm="HS256")
# List agents (filtered by scopes)
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:7777/agents
# Run specific agent
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "message=Hello" \
http://localhost:7777/agents/agent-1/runs
All AgentOS endpoints have default scope requirements:
{
# System
"GET /config": ["system:read"],
"GET /models": ["system:read"],
# Agents
"GET /agents": ["agents:read"],
"GET /agents/*": ["agents:read"],
"POST /agents/*/runs": ["agents:run"],
# Teams
"GET /teams": ["teams:read"],
"POST /teams/*/runs": ["teams:run"],
# Workflows
"GET /workflows": ["workflows:read"],
"POST /workflows/*/runs": ["workflows:run"],
# Sessions
"GET /sessions": ["sessions:read"],
"POST /sessions": ["sessions:write"],
"DELETE /sessions/*": ["sessions:delete"],
# And more...
}
See /libs/agno/agno/os/scopes.py for the default scope mappings.
You can override or extend default mappings:
from agno.os.middleware import JWTMiddleware
app.add_middleware(
JWTMiddleware,
verification_keys=["your-public-key-or-secret"],
algorithm="RS256", # Default; use "HS256" for symmetric keys
authorization=True,
verify_audience=True, # Verify aud claim matches AgentOS ID
scope_mappings={
# Override default
"GET /agents": ["custom:scope"],
# Add new endpoint
"POST /custom/endpoint": ["custom:action"],
# Allow without scopes
"GET /public": [],
}
)
agents:read and agents:<id>:read scopesauthorization=True is set on JWTMiddleware or AgentOS