Security Best Practices
Implementing the Knidian SDK securely is critical for protecting patient data and maintaining HIPAA/GDPR compliance.
Session Token Architecture
The Knidian SDK uses session tokens instead of API keys for authentication. This keeps your API key secure on your backend server.
How It Works
1. Browser → Your Backend: Request session token
2. Your Backend → Knidian API: Exchange API key for session token
3. Knidian API → Your Backend: Return short-lived session token
4. Your Backend → Browser: Send session token to SDK
5. Browser → Knidian API: All SDK requests use session token
Security Benefits
✅ API Key Never Exposed: Your API key stays on your secure backend server ✅ Short-Lived Tokens: Session tokens expire after 1 hour (configurable) ✅ User-Scoped Tokens: Each token is tied to a specific user ID (REQUIRED) ✅ Data Isolation Enforced: Users can only access their own cases and files ✅ Revocable: Individual tokens can be revoked immediately ✅ No Proxy Latency: SDK calls Knidian API directly with session token
Backend Security
1. Protect Your API Key
NEVER expose your API key in client-side code:
// ❌ NEVER DO THIS
const sdk = KnidianSDK.create({
apiKey: 'knd_live_abc123...', // Exposed in browser!
});
DO store API key securely on your backend:
// ✅ CORRECT - API key on backend
// backend/server.js
const response = await fetch('https://api.knidian.ai/sdk/auth/session', {
headers: {
'x-api-key': process.env.KNIDIAN_API_KEY, // Secure!
},
});
2. Store API Keys Securely
Development:
# .env file (never commit to git!)
KNIDIAN_API_KEY=knd-your-api-key-here
Production - Use a secrets manager:
- AWS Secrets Manager
- Google Cloud Secret Manager
- Azure Key Vault
- HashiCorp Vault
// Example: AWS Secrets Manager
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
const getApiKey = async () => {
const secret = await secretsManager.getSecretValue({
SecretId: 'knidian-api-key'
}).promise();
return JSON.parse(secret.SecretString).KNIDIAN_API_KEY;
};
3. Implement User Authentication
ALWAYS verify users before issuing session tokens:
// ✅ CORRECT - Verify user authentication
app.post('/api/knidian-auth', authenticateUser, async (req, res) => {
// User is authenticated by middleware
const userId = req.user.id;
// Now safe to create session token
const response = await fetch('https://api.knidian.ai/sdk/auth/session', {
method: 'POST',
headers: {
'x-api-key': process.env.KNIDIAN_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId, expiresIn: 3600 }),
});
const { sessionToken, expiresAt } = await response.json();
res.json({ sessionToken, expiresAt });
});
// ❌ WRONG - No authentication check
app.post('/api/knidian-auth', async (req, res) => {
const { userId } = req.body; // Anyone can send any userId!
// ... dangerous!
});
4. Add Rate Limiting
Prevent abuse with rate limiting:
const rateLimit = require('express-rate-limit');
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window per IP
message: 'Too many authentication requests, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/knidian-auth', authLimiter);
5. Configure CORS Properly
Restrict CORS to your frontend domain only:
const cors = require('cors');
app.use(cors({
origin: process.env.FRONTEND_URL, // Only your frontend!
credentials: true,
methods: ['POST'], // Only allow POST for auth endpoint
}));
Don't use wildcards in production:
// ❌ DANGEROUS in production
app.use(cors({ origin: '*' }));
HTTPS Requirements
Always Use HTTPS
The SDK requires HTTPS in production:
- ✅ HTTPS (Required):
https://yourapp.com - ❌ HTTP (Blocked):
http://yourapp.com - ✅ Exception:
localhostfor development
Enforce HTTPS
Configure your server to redirect HTTP to HTTPS:
// Express.js
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
return res.redirect(`https://${req.header('host')}${req.url}`);
}
next();
});
SSL/TLS Best Practices
- Use TLS 1.2 or higher
- Use strong cipher suites
- Enable HSTS headers
- Use certificate pinning (optional, advanced)
// Enable HSTS
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
next();
});
Content Security Policy (CSP)
Configure CSP Headers
Allow connections to Knidian services:
Content-Security-Policy:
default-src 'self';
connect-src 'self' https://api.knidian.ai wss://api.knidian.ai;
script-src 'self' https://cdn.knidian.ai;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://cdn.knidian.ai;
Required CSP Directives
| Directive | Required Value | Purpose |
|---|---|---|
connect-src | https://api.knidian.ai | API requests |
connect-src | wss://api.knidian.ai | WebSocket connection |
script-src | https://cdn.knidian.ai | SDK script (if using CDN) |
font-src | https://cdn.knidian.ai | Custom fonts |
Implementation
// Express.js
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
connectSrc: ["'self'", "https://api.knidian.ai", "wss://api.knidian.ai"],
scriptSrc: ["'self'", "https://cdn.knidian.ai"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
fontSrc: ["'self'", "https://cdn.knidian.ai"],
},
}));
User Identity & Authorization
User ID Requirements
- Required: userId is REQUIRED when creating session tokens (not optional)
- Unique: Each user must have a unique ID
- Consistent: Use the same ID across sessions
- Not PII: Avoid using email or name as user ID
- From Your System: Use IDs from your authentication system
// ✅ GOOD - Using your system's user ID
userId: 'usr_8a7d9f2b3c4e5f6a'
// ❌ BAD - Using personal information
userId: 'john.doe@example.com'
Verify User Identity
Always verify user identity before initializing the SDK:
// ✅ RECOMMENDED
async function initializeKnidianSDK() {
// Verify user is authenticated
const user = await authenticateUser();
if (!user) {
throw new Error('User must be authenticated');
}
// Initialize SDK with verified user ID
const sdk = KnidianSDK.create({
authUrl: 'https://your-backend.com/api/knidian-auth',
userId: user.id, // Use your authenticated user ID
container: '#app'
});
return sdk;
}
Data Isolation & Access Control
Automatic User Isolation
The Knidian API automatically enforces data isolation at every level:
✅ Session Tokens Are User-Scoped: Every session token includes the userId ✅ All Operations Validate Ownership: Cases and files are validated against the session's userId ✅ Cross-User Access Blocked: Users cannot access other users' data, even with the same API key ✅ Enforced at Multiple Layers: Controller, service, and database levels
How It Works
When a user makes any API request:
- Session Token Extracted: The API extracts the session token from the request
- User ID Retrieved: The userId is retrieved from the session token
- Ownership Validated: Every case/file operation validates the userId matches
- Access Denied: If userId doesn't match, the request is rejected with 403 Forbidden
// Example: User A tries to access data
const sdk = KnidianSDK.create({
authUrl: 'https://your-backend.com/api/knidian-auth',
userId: 'user-a', // Session token contains 'user-a'
container: '#app'
});
// ✅ ALLOWED - User A can access their own cases
const cases = await sdk.cases.getCases(); // Returns only user-a's cases
// ✅ ALLOWED - User A can access their own case
const case = await sdk.cases.getCase('case-123'); // Only if case belongs to user-a
// ❌ BLOCKED - User A cannot access user-b's case
const otherCase = await sdk.cases.getCase('case-456'); // 404 if belongs to user-b
What This Means For You
No Additional Code Required: Data isolation is automatic. You just need to:
- Pass the correct
userIdwhen initializing the SDK - Ensure your backend validates user authentication before creating session tokens
- Trust that the Knidian API enforces user isolation automatically
Multi-Tenant Support: Multiple users can share the same API key (e.g., all patients in a hospital), and their data remains completely isolated.
// Hospital example - Same API key, different users
// Patient A
const sdkPatientA = KnidianSDK.create({
authUrl: 'https://hospital.com/api/knidian-auth',
userId: 'patient-a-id', // Only sees their own data
container: '#patient-a-portal'
});
// Patient B (same API key, different user)
const sdkPatientB = KnidianSDK.create({
authUrl: 'https://hospital.com/api/knidian-auth',
userId: 'patient-b-id', // Only sees their own data
container: '#patient-b-portal'
});
// Patient A and Patient B cannot access each other's cases or files
Data Privacy & HIPAA Compliance
Patient Data Handling
The Knidian SDK processes Protected Health Information (PHI). You must:
- Obtain Patient Consent: Get explicit consent before using the SDK
- Encrypt Data in Transit: Ensured by HTTPS
- Secure Data at Rest: Knidian encrypts all stored data
- Implement Access Controls: Only authorized users should access patient cases
- Audit Logging: Log all access to patient data