Skip to main content

Security Best Practices

Ensure your Connect UI implementation follows security best practices to protect your users and their data.

API Key Management

Never Expose API Keys Client-Side

Wrong: API key in frontend code

// DON'T DO THIS
const response = await fetch('https://app.unizo.ai/api/v1/serviceKeys', {
headers: {
'apikey': 'sk_live_abcd1234' // Exposed API key!
}
});

Correct: API key on server only

// Backend API endpoint
app.post('/api/generate-service-key', async (req, res) => {
const response = await fetch('https://app.unizo.ai/api/v1/serviceKeys', {
headers: {
'apikey': process.env.UNIZO_API_KEY // Secure server-side
}
});
res.json({ serviceKey: response.serviceKey });
});

// Frontend calls your backend
const { serviceKey } = await fetch('/api/generate-service-key').then(r => r.json());

Service Key Security

Generate Fresh Keys

Always generate new service keys for each authentication session:

// Good: New key per session
async function startAuthFlow() {
const { serviceKey } = await generateServiceKey();
// Use immediately
}

// Bad: Reusing old keys
const STATIC_KEY = '196fe56aaae7c00a284dd96452b'; // Don't do this!

Set Appropriate Expiration

Configure service keys with reasonable expiration times:

{
"expirationMinutes": 30, // Expire after 30 minutes
"singleUse": true // Invalidate after first use
}

Iframe Security

Content Security Policy

Configure CSP headers to allow Unizo's domain:

Content-Security-Policy: frame-src https://dock.unizo.ai;

Sandbox Attributes

Use appropriate sandbox attributes:

<iframe
src="https://dock.unizo.ai/links/YOUR_KEY"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
/>

Message Validation

Verify Origin

Always validate the origin of postMessage events:

window.addEventListener('message', (event) => {
// Verify the message is from Unizo
if (event.origin !== 'https://dock.unizo.ai') {
console.warn('Ignoring message from unknown origin:', event.origin);
return;
}

// Safe to process
handleAuthMessage(event.data);
});

Validate Message Structure

Check message format before processing:

function handleAuthMessage(data) {
// Validate expected structure
if (!data.type || !data.integrationId) {
console.error('Invalid message format');
return;
}

switch (data.type) {
case 'AUTH_SUCCESS':
handleSuccess(data);
break;
case 'AUTH_ERROR':
handleError(data);
break;
}
}

Data Protection

Minimal Data Exposure

Only include necessary information in requests:

{
"subOrganization": {
"externalKey": "cust_123", // Use IDs, not sensitive data
"name": "Acme Inc" // Public name only
}
}

Secure Storage

Never store credentials or tokens client-side:

// Bad: Storing in localStorage
localStorage.setItem('userToken', token);

// Good: Let backend handle storage
await fetch('/api/store-integration', {
method: 'POST',
body: JSON.stringify({ integrationId })
});

Network Security

HTTPS Only

Always use HTTPS in production:

// Enforce HTTPS
if (location.protocol !== 'https:' && !location.hostname.includes('localhost')) {
location.protocol = 'https:';
}

Certificate Pinning

For mobile apps, implement certificate pinning for API calls.

Monitoring & Compliance

Audit Logs

Track authentication attempts:

async function logAuthAttempt(userId, provider, success) {
await fetch('/api/audit-log', {
method: 'POST',
body: JSON.stringify({
userId,
provider,
success,
timestamp: new Date().toISOString(),
ip: req.ip
})
});
}

Rate Limiting

Implement rate limiting for service key generation:

const rateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10 // Limit to 10 requests per window
});

app.post('/api/generate-service-key', rateLimiter, generateKey);

Security Checklist

  • API keys stored securely server-side
  • Service keys generated per session
  • CSP headers configured
  • PostMessage origin validation
  • HTTPS enforced
  • Rate limiting implemented
  • Audit logging enabled
  • Error messages don't expose sensitive info
  • Regular security audits scheduled