Authentication and Authorization
Digitorn provides a complete, provider-agnostic authentication system with JWT tokens, role-based access control (RBAC), and pluggable identity providers. Designed for enterprise environments where security, auditability, and multi-provider support are requirements.
Architecture Overview
Authentication Flow
Configuration
Authentication is configured in the server configuration file, not in individual app YAML files. This is intentional: auth is an infrastructure concern, not an application concern.
# server.yaml or environment variables
server:
auth:
enabled: true
secret_key: "{{env.JWT_SECRET}}" # auto-generated if omitted
access_token_ttl: 900 # 15 minutes (seconds)
refresh_token_ttl: 604800 # 7 days (seconds)
providers:
# Local provider (default) — username/password in DB
- type: local
default: true
# LDAP / Active Directory
- type: ldap
config:
server: "ldap://ad.company.com"
base_dn: "dc=company,dc=com"
bind_dn: "{{env.LDAP_BIND_DN}}"
bind_password: "{{env.LDAP_BIND_PASSWORD}}"
user_search: "(sAMAccountName={username})"
auto_provision: true
# OAuth2 (Google, GitHub, Azure, custom)
- type: oauth2
config:
provider: google
client_id: "{{env.GOOGLE_CLIENT_ID}}"
client_secret: "{{env.GOOGLE_CLIENT_SECRET}}"
redirect_uri: "http://localhost:8000/auth/callback"
# API keys for machine-to-machine
- type: api_key
config:
header: "X-API-Key"
When auth.enabled is false (default), all requests pass through without authentication. The user_id is set to "anonymous".
Identity Providers
Local Provider
The default provider. Users register with a username and password. Passwords are hashed with bcrypt (12 rounds, constant-time comparison).
POST /auth/register
{
"username": "john",
"password": "secure_password_123",
"email": "john@company.com",
"display_name": "John Doe"
}
New users are assigned the developer role by default.
LDAP / Active Directory
Authenticates against an LDAP directory. On first login, the user record is automatically created in the local database (auto-provisioning). Subsequent logins update the email and display name from the directory.
The authentication process:
- Service account connects to LDAP and searches for the user DN
- A separate bind attempt verifies the user's password
- User attributes (email, display name) are extracted
- Local user record is created or updated
Supports Active Directory, OpenLDAP, and FreeIPA.
OAuth2 / OIDC
Supports authorization code flow with well-known configurations for Google, GitHub, and Azure AD. Any OIDC-compliant provider can be used with custom URLs.
The flow:
GET /auth/oauth/{provider}redirects the user to the identity provider- User authorizes the application
- Callback receives an authorization code
- Code is exchanged for an access token
- User info is fetched from the provider
- Local user record is created or updated
API Key
For machine-to-machine integrations (CI/CD, scripts, SDK clients). API keys are generated with dk_ prefix, hashed with SHA-256, and stored in the database.
# API key format
dk_{prefix}_{random_48_chars}
# Usage
curl -H "X-API-Key: dk_a1b2c3d4_..." https://api.example.com/apps
Keys can be scoped to specific applications and have expiration dates.
Role-Based Access Control
Built-in Roles
| Role | Permissions | Description |
|---|---|---|
admin | * (all) | Full access to all features, users, and configuration |
developer | apps, sessions, agents, MCP | Create and manage apps, run agents, view sessions |
viewer | apps:read, sessions:read | Read-only access to apps and sessions |
Custom Roles
Custom roles can be created via the admin API with any combination of permissions:
POST /admin/roles
{
"name": "data_analyst",
"description": "Can run apps and view sessions but not deploy",
"permissions": ["apps:read", "sessions:read", "sessions:write", "agents:run"]
}
Users can have multiple roles. Permissions are merged (union of all role permissions).
Session Management
Every conversation session is bound to a user_id. Users can only see and manage their own sessions.
Session Persistence
Messages are automatically persisted after each agent turn. When a user returns to a session, the full message history is reloaded.
Session Resume
When a session is resumed, the agent receives:
- The full message history (user messages, agent responses, tool calls)
- The memory snapshot (goal, plan, tasks, facts, notes, checkpoints)
- The original request (preserved verbatim)
This means the agent continues exactly where it left off, even across daemon restarts.
Session Fork
Forking creates an independent copy of a session. The original session continues unchanged. Use this to explore alternative approaches:
POST /auth/sessions/abc123/fork
{"new_session_id": "abc123-alt"}
Both sessions have the same message history up to the fork point. After forking, they diverge independently.
API Reference
Authentication Endpoints
| Method | Path | Description | Auth Required |
|---|---|---|---|
| POST | /auth/register | Create local user account | No |
| POST | /auth/login | Authenticate and receive tokens | No |
| POST | /auth/refresh | Exchange refresh token | No |
| POST | /auth/logout | Revoke refresh token | No |
| GET | /auth/me | Get current user info | Yes |
| GET | /auth/sessions | List user's sessions | Yes |
| GET | /auth/sessions/{id}/history | Get session message history | Yes |
| POST | /auth/sessions/{id}/fork | Fork a session | Yes |
| DELETE | /auth/sessions/{id} | Delete a session | Yes |
Protected Endpoints
When auth is enabled, all API endpoints except the ones listed above require a valid Bearer token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Or an API key in the X-API-Key header:
X-API-Key: dk_a1b2c3d4_...
Token Format
Access tokens contain:
{
"sub": "user_id",
"type": "access",
"iss": "digitorn",
"iat": 1710000000,
"exp": 1710000900,
"email": "user@example.com",
"name": "John Doe",
"roles": ["developer"],
"perms": ["apps:read", "apps:write", "agents:run"]
}
Security Considerations
- Passwords are hashed with bcrypt (12 rounds). Verification uses constant-time comparison.
- Refresh tokens are stored as SHA-256 hashes in the database. The raw token is never stored.
- JWT secret keys are auto-generated (64 bytes hex) and stored with 600 permissions (owner-only).
- API keys use the
dk_prefix for easy identification in logs and key scanning tools. - OAuth2 state parameters prevent CSRF attacks on the authorization flow.
- Session isolation is enforced at the query level. Users cannot access other users' sessions even with a valid token.
- The auth middleware runs before all request handlers. There is no way to bypass it for protected endpoints.