Skip to main content

Authentication

Deeployd supports multiple authentication methods to fit your security requirements.

Authentication Methods

MethodBest ForSecurity Level
JWT TokensWeb/mobile appsHigh
API KeysServer-to-serverHigh
SSO/SAMLEnterpriseVery High
OAuth 2.0Third-party appsHigh

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)
TokenLifetimePurpose
Access Token15 minutesAPI requests
Refresh Token7 daysGet 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

RoleDescriptionScope
ownerFull access, can delete workspaceWorkspace
adminFull access except billingWorkspace
memberStandard accessTeam-scoped
viewerRead-only accessTeam-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"

  1. Check token hasn't expired
  2. Verify token is for correct environment (live vs test)
  3. Ensure token is properly formatted in header

"Permission Denied"

  1. Check user's role has required permission
  2. Verify API key includes needed permissions
  3. 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.