Auth & Security
Authentication Overview
Behavry has two separate authentication planes:
| Plane | Who | Mechanism | Token |
|---|---|---|---|
| Agent auth | AI agents (SDK, simulators) | OAuth 2.1 client credentials | RS256 JWT (short-lived) |
| Admin auth | Human operators (dashboard) | Password / Clerk OIDC / Generic OIDC | RS256 JWT |
Both planes use the same RS256 keypair.
Agent Authentication
OAuth 2.1 Client Credentials Flow
- Admin registers an agent → receives
client_id+client_secret(plaintext, one-time) - Agent calls
POST /api/v1/auth/tokenwithclient_idandclient_secret - Behavry verifies the secret hash (bcrypt), creates a
Session, issues a JWT - Agent uses the JWT as
Authorization: Bearer <token>on all proxy requests - On every proxy request, Behavry:
- Validates JWT signature and expiry
- Checks the
session_id(JWTjti) is stillactivein the database
- Sessions can be explicitly revoked — the DB check catches revoked sessions even if the JWT is not yet expired
JWT Payload (Agent Token)
{
"sub": "<agent_id>",
"jti": "<session_id>",
"iss": "behavry",
"exp": 1700000000,
"iat": 1699996400,
"roles": ["filesystem-reader", "db-analyst"],
"permissions": ["filesystem:read", "database:read"],
"risk_tier": "medium"
}
Admin Authentication
Password Provider (default)
- Username + password submitted to
POST /api/v1/auth/admin/login - Password verified against bcrypt hash
- JWT issued; stored in
localStorageasbehavry_admin_token - Dashboard decodes the JWT client-side (no signature verification — display only) to get username
Clerk OIDC
Set BEHAVRY_AUTH_PROVIDER=clerk. Clerk handles the sign-in flow; the backend validates Clerk session tokens using the Clerk SDK.
Dashboard requires VITE_CLERK_PUBLISHABLE_KEY in dashboard/.env. The ClerkTokenSync component keeps the Clerk session token in localStorage so the existing API client needs no changes.
Generic OIDC (Entra ID, Okta, Ping)
Set BEHAVRY_AUTH_PROVIDER=oidc. The backend validates tokens against the configured JWKS endpoint and issuer. Used for enterprise IdP integration.
RBAC
Model
Agent → AgentRole → Role → permissions + resource_scopes
Permissions are strings evaluated by OPA policies. Examples:
| Permission | Meaning |
|---|---|
filesystem:read | Read files via filesystem MCP server |
filesystem:write | Write files (also required for delete-escalation) |
database:read | Query database (non-sensitive tables) |
database:write | Insert/update records |
database:sensitive_read | Query PII/financial tables |
slack:read | List Slack channels |
slack:post | Post to non-broadcast, non-protected channels |
slack:protected_channels | Post to admin/security channels |
github:read | List repositories |
github:write | Create issues |
github:merge | Merge pull requests |
web:read | HTTP GET requests |
web:write | HTTP POST requests |
Resource scopes constrain where permissions apply:
{
"name": "project-reader",
"permissions": ["filesystem:read"],
"resource_scopes": ["/home/projects/"]
}
An agent with this role can only read files under /home/projects/.
Super Admin
AdminUser rows with is_super_admin=true and tenant_id=null are break-glass super-admins. They can see and manage all tenants. Created automatically on first boot as <admin_username>-super.
Session Model
- Sessions are created at agent login and tracked in the
sessionstable - Every proxy request validates
session_idagainst the DB (active check) - Sessions expire after a configurable TTL (not yet exposed as a config var — defaults to token expiry)
- Revocation: admin can revoke a session via
DELETE /api/v1/sessions/{id}(not yet implemented in UI — use API directly) - On logout (
POST /api/v1/auth/logout), the session is markedrevoked
Audit Trail
Every authentication event, policy decision, and tool call is written to audit_events. The audit log:
- Is append-only (no UPDATE or DELETE on audit rows)
- Includes an
event_hash(SHA-256 of content) andprevious_hashforming a tamper-evidence chain - Is stored in a TimescaleDB hypertable (partitioned by timestamp)
- Can be exported to SIEM via webhook (CEF or JSON format)
See Audit & Integrity for the full schema.
DLP (Data Loss Prevention)
The DLP scanner runs on every tool call's input parameters before the OPA call. It detects:
| Pattern | Severity |
|---|---|
| AWS/GCP/Azure access keys | critical |
| Generic API keys / tokens | high |
| Private keys (PEM) | critical |
| SSN (US Social Security Number) | high |
| Credit card numbers (Luhn check) | high |
| Email addresses | medium |
| Phone numbers | low |
Actions:
critical: Block the request immediately (before OPA)warn(anything lower): Log the finding, proceed with OPA evaluation
DLP findings are written to audit_events.dlp_findings (JSONB array).
Agent Security Baseline
An approved baseline enforces:
- Tool manifest: any tool call using a tool not in the registered list is blocked as "baseline drift"
- System prompt hash: on
initialize, if the agent sends abehavry.system_prompt_hashmeta-field and it doesn't match the registered hash, asystem_prompt_driftalert is raised (non-blocking)
See Domain Model — AgentBaseline.
Threat Considerations
| Threat | Mitigation |
|---|---|
| Stolen agent JWT | Short expiry; DB session check on every request; explicit revocation |
| Stolen client_secret | Bcrypt hash stored; plaintext returned once; token can be rotated |
| Prompt injection via tool response | Behavioral monitor tracks anomaly in action sequences; alerts raised |
| System prompt tampering | Prompt hash checked on initialize; drift alert created |
| Tool manifest drift | Baseline enforcement blocks unregistered tools |
| Policy bypass | OPA is fail-closed; unreachability = deny |
| Sensitive data exfiltration | DLP scanner on all inputs; filesystem blocked paths in OPA |
| Multi-tenant data leakage | tenant_id on all entities; middleware sets context on every request |
| Escalation abuse | Escalations time out automatically; all decisions are audited |