Security Best Practices

Follow these security guidelines to protect your agents, tokens, and sensitive operations.

API Key Security

Storage

  • Never commit API keys to source control - Use environment variables or secrets managers
  • Use secrets management services - AWS Secrets Manager, HashiCorp Vault, or similar
  • Rotate keys regularly - Implement a 90-day rotation policy
  • Use separate keys per environment - Different keys for dev, staging, and production
// ❌ Bad: Hardcoded API key
const client = new AgentOTPClient({
  apiKey: 'ak_live_xxxx',
});

// ✅ Good: Environment variable
const client = new AgentOTPClient({
  apiKey: process.env.AGENT_OTP_API_KEY!,
});

// ✅ Better: Secrets manager
import { SecretsManager } from 'aws-sdk';
const secrets = new SecretsManager();
const { SecretString } = await secrets.getSecretValue({
  SecretId: 'agent-otp/api-key',
}).promise();
const client = new AgentOTPClient({
  apiKey: JSON.parse(SecretString!).apiKey,
});

Key Rotation

// Support multiple keys during rotation
const apiKey = process.env.AGENT_OTP_API_KEY_NEW
  || process.env.AGENT_OTP_API_KEY;

// After deploying with new key, remove old key from environment
// and revoke it via CLI: bun run cli agent:revoke --id AGENT_ID

Token Security

Token Handling

  • Use tokens immediately - Don't store them for later use
  • Never log tokens - They are sensitive credentials
  • Set short TTLs - Use the minimum TTL needed
  • Verify before critical operations - Call verifyToken before high-risk actions
// ❌ Bad: Logging tokens
console.log('Token:', permission.token);

// ✅ Good: Log permission ID only
console.log('Permission ID:', permission.id);

// ❌ Bad: Storing tokens
await cache.set('user_token', permission.token);

// ✅ Good: Use immediately
if (permission.status === 'approved') {
  await performAction(permission.token);
  await otp.useToken(permission.id, permission.token);
}

Scope Design

Principle of Least Privilege

Request only the minimum scope needed:

// ❌ Bad: Overly broad scope
const permission = await otp.requestPermission({
  action: 'email.send',
  scope: {
    max_emails: 100,
    allowed_recipients: ['*'],
  },
});

// ✅ Good: Minimal scope
const permission = await otp.requestPermission({
  action: 'email.send',
  scope: {
    max_emails: 1,
    allowed_recipients: [specificRecipient],
    subject_pattern: '^Invoice #\\d+$',
  },
});

Scope Validation

Always validate that the granted scope meets your needs:

const permission = await otp.requestPermission({
  action: 'file.write',
  scope: { max_size: 10485760 }, // Request 10MB
});

// Check granted scope
if (permission.scope.max_size < requiredSize) {
  throw new Error('Insufficient scope granted');
}

Context for Audit Trail

Always provide meaningful context for audit purposes:

const permission = await otp.requestPermission({
  action: 'payment.transfer',
  scope: { max_amount: 100 },
  context: {
    // Who triggered this?
    triggered_by: 'user_request',
    user_id: userId,

    // What is it for?
    reason: 'Monthly subscription payment',
    invoice_id: invoiceId,

    // When did this happen?
    timestamp: new Date().toISOString(),

    // Where did it come from?
    source_ip: requestIp,
    user_agent: userAgent,
  },
});

Error Handling

Secure Error Messages

try {
  const permission = await otp.requestPermission({...});
} catch (error) {
  // ❌ Bad: Exposing internal details
  console.error('Full error:', error);
  return res.status(500).json({ error: error.message });

  // ✅ Good: Log internally, return generic message
  logger.error('OTP request failed', {
    code: error.code,
    permissionId: error.permissionId,
    // Don't log tokens or sensitive data
  });

  return res.status(500).json({
    error: 'Operation failed. Please try again.',
  });
}

Network Security

TLS/SSL

  • Always use HTTPS - The SDK enforces HTTPS by default
  • Verify certificates - Don't disable certificate validation
  • Use TLS 1.2 or higher - Older protocols are disabled

Firewall Rules

# Allow outbound to Agent OTP API
iptables -A OUTPUT -p tcp -d api.agentotp.com --dport 443 -j ACCEPT

# For self-hosted: restrict API access to known IPs
iptables -A INPUT -p tcp --dport 3000 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 3000 -j DROP

Monitoring and Alerting

Key Metrics to Monitor

  • Denial rate - Sudden increase may indicate attack
  • Request volume - Unusual spikes need investigation
  • Token usage patterns - Unused tokens may indicate issues
  • Failed authentications - May indicate compromised keys
// Set up alerts
const metrics = await otp.getMetrics({ period: '1h' });

if (metrics.denialRate > 0.5) {
  alert('High denial rate detected', {
    rate: metrics.denialRate,
    threshold: 0.5,
  });
}

if (metrics.unusedTokenRate > 0.3) {
  alert('High unused token rate', {
    rate: metrics.unusedTokenRate,
  });
}

Compliance

Audit Log Retention

Configure retention based on your compliance requirements:

  • SOC 2 - 1 year minimum
  • HIPAA - 6 years
  • GDPR - As long as necessary, with deletion capability

Data Residency

For data residency requirements, use self-hosting or specify region:

// Use region-specific endpoint
const client = new AgentOTPClient({
  apiKey: process.env.AGENT_OTP_API_KEY!,
  baseUrl: 'https://api.eu.agentotp.com', // EU region
});

Security Checklist

Pre-Production Checklist

  • ☐ API keys stored in secrets manager
  • ☐ Environment-specific keys configured
  • ☐ Key rotation policy documented
  • ☐ Policies reviewed and tested
  • ☐ Explicit denies for dangerous operations
  • ☐ Scope templates minimize permissions
  • ☐ Error handling doesn't leak sensitive info
  • ☐ Audit logging configured
  • ☐ Monitoring and alerting set up
  • ☐ TLS/SSL verified
  • ☐ Firewall rules configured
  • ☐ Compliance requirements documented

Reporting Security Issues

If you discover a security vulnerability in Agent OTP:

See Also