Authentication
Deeployd supports multiple authentication methods to fit your security requirements.
Authentication Methods
| Method | Best For | Security Level |
|---|---|---|
| JWT Tokens | Web/mobile apps | High |
| API Keys | Server-to-server | High |
| SSO/SAML | Enterprise | Very High |
| OAuth 2.0 | Third-party apps | High |
JWT Authentication
Login Flow
// 1. User logs in
const response = await fetch('https://api.deeployd.com/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'user@company.com',
password: 'secret'
})
});
const { accessToken, refreshToken, expiresIn } = await response.json();
// 2. Use access token for requests
const agents = await fetch('https://api.deeployd.com/v1/agents', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
// 3. Refresh when expired
const refreshResponse = await fetch('https://api.deeployd.com/v1/auth/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${refreshToken}`
}
});
Token Lifecycle
Login → Access Token (15 min) → Refresh → New Access Token
Refresh Token (7 days)
| Token | Lifetime | Purpose |
|---|---|---|
| Access Token | 15 minutes | API requests |
| Refresh Token | 7 days | Get new access token |
Token Contents
Access tokens contain:
{
"sub": "user-123",
"email": "user@company.com",
"tenantId": "tenant-456",
"role": "admin",
"permissions": ["agent.create", "agent.read", "..."],
"iat": 1705312200,
"exp": 1705313100
}
API Keys
For server-to-server communication:
Create API Key
const key = await client.apiKeys.create({
name: 'Production Server',
permissions: ['agent.read', 'agent.chat'],
expiresAt: '2025-01-01' // Optional
});
console.log(key.key); // sk_live_xxxx... (shown once)
console.log(key.id); // For management
Using API Keys
// In Authorization header
const response = await fetch('https://api.deeployd.com/v1/agents', {
headers: {
'Authorization': 'Bearer sk_live_xxxx...'
}
});
// Or as query parameter (not recommended)
const response = await fetch('https://api.deeployd.com/v1/agents?api_key=sk_live_xxxx...');
API Key Best Practices
// ✅ Good: Limited permissions, named clearly
await client.apiKeys.create({
name: 'prod-web-readonly',
permissions: ['agent.read', 'conversation.read']
});
// ✅ Good: Set expiration
await client.apiKeys.create({
name: 'temp-integration',
expiresAt: '2024-03-01'
});
// ❌ Bad: Full permissions, no name
await client.apiKeys.create({
permissions: ['*']
});
Rotate API Keys
// Create new key before revoking old one
const newKey = await client.apiKeys.create({
name: 'Production Server v2',
permissions: ['agent.read', 'agent.chat']
});
// Update your servers with new key
// ...
// Revoke old key
await client.apiKeys.revoke('old-key-id');
List and Manage Keys
// List all keys
const keys = await client.apiKeys.list();
for (const key of keys.data) {
console.log({
id: key.id,
name: key.name,
lastUsed: key.lastUsedAt,
createdAt: key.createdAt
});
}
// Revoke a key
await client.apiKeys.revoke('key-123');
Multi-Factor Authentication (MFA)
Enable MFA
// Generate TOTP secret
const setup = await client.auth.setupMfa();
console.log(setup.secret); // Base32 secret
console.log(setup.qrCode); // QR code URL for authenticator app
// Verify and enable
await client.auth.enableMfa({
code: '123456' // From authenticator app
});
Login with MFA
// Step 1: Initial login
const loginResult = await client.auth.login({
email: 'user@company.com',
password: 'secret'
});
if (loginResult.mfaRequired) {
// Step 2: Verify MFA
const tokens = await client.auth.verifyMfa({
sessionToken: loginResult.sessionToken,
code: '123456'
});
}
Recovery Codes
// Generate recovery codes during MFA setup
const codes = await client.auth.generateRecoveryCodes();
// Returns 10 single-use codes
// [
// "XXXX-XXXX-XXXX",
// "YYYY-YYYY-YYYY",
// ...
// ]
// Use recovery code when locked out
await client.auth.verifyMfa({
sessionToken: loginResult.sessionToken,
recoveryCode: 'XXXX-XXXX-XXXX'
});
Require MFA for Roles
// Admin setting
await client.settings.update({
authentication: {
mfaRequired: true, // All users
// Or specific roles
mfaRequiredRoles: ['owner', 'admin']
}
});
Role-Based Access Control (RBAC)
Built-in Roles
| Role | Description | Scope |
|---|---|---|
owner | Full access, can delete workspace | Workspace |
admin | Full access except billing | Workspace |
member | Standard access | Team-scoped |
viewer | Read-only access | Team-scoped |
Role Hierarchy
owner
│
▼
admin
│
▼
member
│
▼
viewer
Assign Roles
// Invite user with role
await client.users.invite({
email: 'newuser@company.com',
role: 'member',
teamIds: ['team-123'] // Optional: restrict to teams
});
// Change user role
await client.users.updateRole('user-123', {
role: 'admin'
});
Granular Permissions
Beyond roles, use granular permissions:
Permission Format
{resource}.{action}
Examples:
- agent.create
- agent.read
- agent.update
- agent.delete
- agent.chat
- workflow.execute
- team.manage
- user.invite
Check Permissions
// Check if user has permission
const canCreate = await client.auth.hasPermission('agent.create');
// Get all permissions
const permissions = await client.auth.getPermissions();
Custom Permission Sets
// Create API key with specific permissions
await client.apiKeys.create({
name: 'Chat Only',
permissions: [
'agent.read',
'agent.chat',
'conversation.read',
'conversation.create'
]
});
Tenant Isolation
Multi-Tenant Architecture
Each organization (tenant) is completely isolated:
Tenant A Tenant B
┌─────────────┐ ┌─────────────┐
│ Users │ │ Users │
│ Agents │ │ Agents │
│ Data │ │ Data │
│ Settings │ │ Settings │
└─────────────┘ └─────────────┘
↓ ↓
Database A Database B
(isolated) (isolated)
Tenant Context
Every API request is scoped to a tenant:
// From JWT claims
{
"tenantId": "tenant-123",
"sub": "user-456"
}
// All queries automatically filtered
SELECT * FROM agents WHERE tenant_id = 'tenant-123'
Cross-Tenant Access
Not allowed by default. For enterprise multi-org:
// Admin can access child organizations
const childOrgs = await client.organizations.listChildren();
Session Management
Active Sessions
// List user's active sessions
const sessions = await client.auth.listSessions();
for (const session of sessions) {
console.log({
id: session.id,
device: session.deviceInfo,
ip: session.ipAddress,
lastActive: session.lastActiveAt,
createdAt: session.createdAt
});
}
// Revoke a session
await client.auth.revokeSession('session-123');
// Revoke all sessions (logout everywhere)
await client.auth.revokeAllSessions();
Session Security
// Admin settings
await client.settings.update({
sessions: {
maxActiveSessions: 5,
sessionTimeout: '24h',
idleTimeout: '1h',
requireReauthFor: ['billing', 'security-settings']
}
});
IP Allowlisting
Enterprise feature to restrict access by IP:
await client.settings.update({
security: {
ipAllowlist: {
enabled: true,
addresses: [
'203.0.113.0/24', // Office network
'198.51.100.10' // VPN exit
],
bypassForSso: false // Also applies to SSO
}
}
});
Password Policies
await client.settings.update({
authentication: {
passwordPolicy: {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: true,
preventReuse: 5, // Can't reuse last 5 passwords
maxAge: 90 // Days until password expires
}
}
});
Authentication Troubleshooting
"Invalid Token"
- Check token hasn't expired
- Verify token is for correct environment (live vs test)
- Ensure token is properly formatted in header
"Permission Denied"
- Check user's role has required permission
- Verify API key includes needed permissions
- Check team membership for scoped resources
"Account Locked"
After too many failed attempts:
// Admin can unlock
await client.users.unlock('user-123');
Next: Learn about SSO & SCIM for enterprise identity management.