003-zero-trust-authentication
A self-hosted AI platform that keeps your data on your infrastructure. Busibox integrates document processing, semantic search, AI agents, and custom applications into a single platform — running on Docker with enterprise-grade security baked in. Think of it as a Linux distribution for AI.
Install / Use
npx skills add jazzmind/busiboxInstalls into whichever agent you are using.
Cursor Rules
Cursor IDE rules (v2)
Quality Score
Category
SecuritySupported Platforms
Skill content
View source on GitHubZero Trust Authentication Rules
Overview
Busibox uses a Zero Trust authentication model where user identity is cryptographically proven by JWT tokens, not by service-to-service credentials. All services trust tokens signed by the authz service.
Core Principles
1. NO Client Credentials for User Operations
NEVER use client_id/client_secret for operations on behalf of users:
# ❌ WRONG - Don't use client credentials
token_client = TokenExchangeClient(
client_id="agent-api",
client_secret="secret123",
)
token = await token_client.get_token_for_service(
requested_subject=user_id, # Impersonation!
target_audience="data-api"
)
# ✅ CORRECT - Use Zero Trust token exchange
token = await exchange_token_zero_trust(
subject_token=user_token, # User's actual token
target_audience="data-api",
user_id=user_id # For logging only
)
2. Token Exchange Flow
Any valid authz-signed token can be exchanged for another audience:
User Session JWT (aud=ai-portal)
↓ token exchange
Agent API Token (aud=agent-api)
↓ token exchange (THIS IS NOW ALLOWED)
Ingest API Token (aud=ingest-api)
The security comes from:
- Signature verification - Token must be signed by authz
- Expiration check - Token must not be expired
- RBAC from authz DB - User's scopes/roles come from database, not the incoming token
- Session revocation - Session/delegation tokens are checked for revocation
3. Scopes Come from RBAC, Not Tokens
When exchanging tokens, the scopes in the issued token come from the user's roles in the authz database, not from the incoming token's scopes:
# In authz token exchange:
roles = await db.get_user_roles(user_id)
all_scopes = set()
for r in roles:
all_scopes.update(r.get("scopes") or [])
# New token gets aggregated scopes from all user's roles
Exception: Source-Gated Scopes
Some scopes are never in any role's scope list but are injected conditionally by authz based on the origin (aud) of the incoming subject token. This is an approved pattern called "source gating".
Currently source-gated scopes:
| Scope | Injected when subject token aud is | Target audience |
|-------|--------------------------------------|-----------------|
| config.secrets.read | agent-api | config-api |
This allows agent-api to read raw LLM credentials from config-api without any other caller being able to do the same — even an admin user with a direct config-api token exchange cannot obtain this scope.
Implementation: srv/authz/src/routes/oauth.py — _AGENT_API_CONFIG_EXTRA_SCOPES constant.
4. Pass Tokens Through the Call Chain
Services should store and pass along user tokens for downstream calls:
# In FastAPI dependency
async def get_principal(authorization: str = Header(...)) -> Principal:
token = authorization.split(" ", 1)[1]
principal = await validate_bearer(token)
principal.token = token # Store for downstream use
return principal
# In service code
async def call_downstream_service(principal: Principal):
downstream_token = await exchange_token_zero_trust(
subject_token=principal.token,
target_audience="downstream-api",
user_id=principal.sub
)
Implementation Patterns
Python Services (busibox_common)
from app.auth.token_exchange import exchange_token_zero_trust
# Exchange user's token for downstream service
ingest_token = await exchange_token_zero_trust(
subject_token=principal.token,
target_audience="ingest-api",
user_id=principal.sub
)
if ingest_token:
headers = {"Authorization": f"Bearer {ingest_token}"}
# Make downstream call
TypeScript Services (busibox-app)
import { exchangeTokenZeroTrust } from '@jazzmind/busibox-app/lib/authz';
const result = await exchangeTokenZeroTrust({
sessionJwt: userToken,
audience: 'agent-api',
scopes: ['agents:read'],
});
Token Types
| Type | Audience | Purpose | Revocable | |------|----------|---------|-----------| | Session | ai-portal | User browser session | Yes (via session_id) | | Delegation | ai-portal | Background tasks | Yes (via delegation_id) | | Access | service-specific | API calls | No (short-lived) |
All types can be used as subject_token for token exchange.
Anti-Patterns to Avoid
❌ Service-to-Service Authentication (HARD RULE)
NEVER create service accounts, shared secrets, API keys, or client credentials for one service to call another. This violates Zero Trust. All inter-service calls must carry a user's identity via JWT token exchange.
If a service needs data from another service:
- During a user request: exchange the user's token for the target audience
- During startup/background: use delegation tokens created while a user was present, or read directly from the shared database layer (Postgres) which both services can access
- During deployment: use Ansible which has direct database/filesystem access
# ❌ NEVER - service-to-service API keys
CONFIG_API_KEY = "shared-secret-123"
headers = {"X-Service-Key": CONFIG_API_KEY}
resp = await client.get(f"{config_api_url}/admin/config", headers=headers)
# ❌ NEVER - client credentials grant
token = await get_client_credentials_token(
client_id="agent-api",
client_secret="secret",
audience="config-api"
)
# ✅ OK - user token exchange during request
config_token = await get_service_token(principal.token, principal.sub, "config-api")
# ✅ OK - direct database read at startup (same Postgres cluster)
conn = await asyncpg.connect(config_database_url)
row = await conn.fetchrow("SELECT value FROM config_entries WHERE key = $1", key)
# ✅ OK - Ansible reads config DB and injects into service env at deploy time
❌ Service Account Impersonation
# DON'T DO THIS
client = ServiceClient(client_id="agent-api", secret="...")
token = client.get_token_as(user_id) # Impersonation
❌ Hardcoded Audience Restrictions
# DON'T DO THIS
if token.audience != "ai-portal":
raise Error("wrong audience") # Over-restrictive
❌ Passing Scopes from Incoming Token
# DON'T DO THIS
new_token = exchange(subject_token, scopes=subject_token.scopes)
# Scopes should come from RBAC, not the incoming token
When to Use Delegation Tokens
For background tasks that run without user presence (e.g., scheduled jobs, webhooks):
- User explicitly creates a delegation token while authenticated
- Delegation token has limited scopes and expiration
- Background job uses delegation token as subject_token for exchanges
# Create delegation token (user must be present)
delegation = await create_delegation_token(
session_jwt=user_session,
name="nightly-sync",
scopes=["ingest:read"],
expires_in_seconds=86400
)
# Later, in background job
token = await exchange_token_zero_trust(
subject_token=delegation.token,
target_audience="ingest-api",
user_id=delegation.user_id
)
Related Files
srv/authz/src/routes/oauth.py- Token exchange implementationsrv/agent/app/auth/token_exchange.py- Agent service token exchange helpersrv/shared/busibox_common/auth.py- Shared auth utilitiesbusibox-app/src/lib/authz/zero-trust.ts- TypeScript Zero Trust client
Related Skills
AstrBot
40.8kAI Agent Assistant & development framework that integrates lots of IM platforms, LLMs, plugins and AI feature, and can be your openclaw alternative. ✨
Anthropic-Cybersecurity-Skills
33.1k817 structured cybersecurity skills for AI agents · Mapped to 6 frameworks: MITRE ATT&CK, NIST CSF 2.0, MITRE ATLAS, D3FEND, NIST AI RMF & MITRE F3 (Fight Fraud) · agentskills.io standard · Works with Claude Code, GitHub Copilot, Codex CLI, Cursor, Gemini CLI & 20+ platforms · 29 security domains ·…
nanoclaw
30.8kA lightweight alternative to OpenClaw that runs in containers for security. Connects to WhatsApp, Telegram, Slack, Discord, Gmail and other messaging apps,, has memory, scheduled jobs, and runs directly on Anthropic's Agents SDK
opcode
22.4kA powerful GUI app and Toolkit for Claude Code - Create custom agents, manage interactive Claude Code sessions, run secure background agents, and more.
Security Score
Audited on Invalid Date
