Auth Skill
Authentication and authorization for agents using the Robutler Platform. Establishes a unified AuthContext and a secure, interoperable mechanism for agent‑to‑agent authorization via RS256 owner assertions (JWT).
Features
- API key authentication with the Robutler Platform
- Role‑based access control: admin, owner, user
on_connectionauthentication hook- Agent owner scope detection (from agent metadata)
- Harmonized
AuthContextwith minimal, stable fields - Optional agent‑to‑agent assertions via
X-Owner-Assertion(short‑lived RS256 JWT, verified via JWKS)
Configuration
from robutler.agents import BaseAgent
from robutler.agents.skills.robutler.auth import AuthSkill
agent = BaseAgent(
name="secure-agent",
model="openai/gpt-4o",
skills={
"auth": AuthSkill({
"api_key": "your_platform_api_key", # Optional: defaults to agent.api_key
"platform_api_url": "https://robutler.ai", # Optional: $ROBUTLER_INTERNAL_API_URL or $ROBUTLER_API_URL or http://localhost:3000
"require_auth": True # Optional: defaults to True
})
}
)
Note: platform_api_url resolves in this order: $ROBUTLER_INTERNAL_API_URL → $ROBUTLER_API_URL → http://localhost:3000.
Scopes
- admin: Platform administrators
- owner: Agent owner (API key belongs to the agent owner)
- user: Regular authenticated users
If not explicitly set, the default scope is user.
Identity and Context
The auth skill validates the API key during on_connection and exposes an AuthContext on the request context:
from robutler.server.context.context_vars import get_context
context = get_context()
auth = context.auth # instance of AuthContext
# Harmonized fields
user_id = auth.user_id # caller identity; overridden by JWT `sub` when verified
agent_id = auth.agent_id # agent id from verified assertion (if provided)
scope = auth.scope.value # "admin" | "owner" | "user"
authenticated = auth.authenticated # bool
assertion = auth.assertion # dict of decoded claims (if provided)
Deprecated identity fields (e.g., origin_user_id, peer_user_id, agent_owner_user_id) have been removed in favor of the harmonized fields above.
Authentication Flow
- Extract API key from
Authorization(Bearer) orX-API-Key - Validate API key with the Robutler Platform
- Determine scope based on the validated user and agent ownership
- Optionally verify
X-Owner-Assertion(RS256, JWKS) and merge acting identity intoAuthContext - Populate
context.authwith anAuthContextinstance
Agent‑to‑Agent Assertions (Owner Assertions)
- Primary purpose: secure, interoperable agent‑to‑agent authentication and authorization across services.
- Also enables owner‑only actions (e.g., ControlSkill) without exposing agent API keys to clients.
- Transport: send
X-Owner-Assertion: <jwt>alongside yourAuthorizationheader.
Claims
aud = robutler-agent:<agentId>— audience bound to the target agentagent_id = <agentId>— agent identity bindingsub = <userId>— acting end‑user identityowner_user_id = <ownerId>— agent owner (advisory)jti— unique token id for optional replay trackingiat/nbf/exp— very short TTL (2–5 minutes)
Verification by AuthSkill
- Signature verification via JWKS (RS256)
- Enforce audience and agent binding:
aud == robutler-agent:<agentId>andagent_id == agent.id - On success, update context:
context.auth.user_id = sub(acting identity)context.auth.agent_id = agent_idcontext.auth.assertion = <decoded claims>- OWNER scope is derived by comparing the API‑key owner to the agent’s
owner_user_id; the assertion does not grant owner scope by itself.
JWKS and configuration
- The skill discovers the JWKS at
OWNER_ASSERTION_JWKS_URLif set; otherwise at{platform_api_url}/api/auth/jwks. - Only RS256 is supported. HS256 and shared‑secret fallbacks are not supported.
High‑level flow
sequenceDiagram
participant U as User
participant C as Chat Server
participant A as Agent Service
participant Auth as AuthSkill
participant Ctrl as ControlSkill
U->>C: Edit agent description
C->>A: Request + headers\nAuthorization, X-Owner-Assertion, X-Payment-Token
A->>Auth: on_connection()
Auth-->>Auth: Verify API key + (optional) verify assertion
Auth-->>A: Set context.auth; derive scope (OWNER if API‑key owner == agent.owner_user_id)
A->>Ctrl: manage_agent(update_description)
Ctrl-->>A: Allowed (owner scope)
A->>U: ✅ Description updated
Defaults and edge cases
- If the skill is enabled and authentication succeeds,
auth.scopedefaults touserunless elevated toowneroradmin. - If the skill is disabled (
require_auth=False) or not configured, downstream tools should treat the request as unauthenticated and avoid owner/admin‑scoped operations.
Example: protecting an owner‑only tool
from robutler.agents.skills.robutler.auth.skill import AuthScope
def update_agent_settings(context, patch):
if context.auth.scope != AuthScope.OWNER:
raise PermissionError("Owner scope required")
# proceed with update
Implementation: see robutler/agents/skills/robutler/auth/skill.py.