Skip to main content

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: localhost for 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

DirectiveRequired ValuePurpose
connect-srchttps://api.knidian.aiAPI requests
connect-srcwss://api.knidian.aiWebSocket connection
script-srchttps://cdn.knidian.aiSDK script (if using CDN)
font-srchttps://cdn.knidian.aiCustom 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:

  1. Session Token Extracted: The API extracts the session token from the request
  2. User ID Retrieved: The userId is retrieved from the session token
  3. Ownership Validated: Every case/file operation validates the userId matches
  4. 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:

  1. Pass the correct userId when initializing the SDK
  2. Ensure your backend validates user authentication before creating session tokens
  3. 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:

  1. Obtain Patient Consent: Get explicit consent before using the SDK
  2. Encrypt Data in Transit: Ensured by HTTPS
  3. Secure Data at Rest: Knidian encrypts all stored data
  4. Implement Access Controls: Only authorized users should access patient cases
  5. Audit Logging: Log all access to patient data

HIPAA Requirements

To maintain HIPAA compliance:

// ✅ RECOMMENDED - Implement audit logging
const sdk = KnidianSDK.create({
authUrl: 'https://your-backend.com/api/knidian-auth',
userId: authenticatedUser.id,
container: '#app',
});

Dependency Security

Keep SDK Updated

Always pin the version you're using in production:

<script src="https://cdn.knidian.ai/sdk/v1.0.0/knidian-sdk.min.js"></script>

Next Steps