Authentication Overview
Context Enterprise supports three primary authentication methods:API Keys
Long-lived credentials for server-to-server communication
JWT Tokens
Short-lived tokens for secure API access with automatic expiration
Enterprise SSO
Integration with your existing identity provider (Okta, Azure AD, SAML)
API Key Authentication
Obtaining Enterprise API Keys
Enterprise API keys are provided during your onboarding process. Contact enterprise@context.ai to receive your credentials.Copy
Ask AI
# Your enterprise credentials (provided by Context team)
CONTEXT_ENTERPRISE_ORG_ID="org_abc123def456"
CONTEXT_API_KEY="ctx_ent_live_sk_1234567890abcdef"
CONTEXT_API_SECRET="ctx_ent_secret_abcdef1234567890"
API Key Usage
API keys are used for initial authentication and JWT token generation:Copy
Ask AI
// Using API keys for authentication
const headers = {
'X-API-Key': process.env.CONTEXT_API_KEY,
'Content-Type': 'application/json'
};
const response = await fetch('https://api.context.ai/v1/auth/enterprise/token', {
method: 'POST',
headers: headers,
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: process.env.CONTEXT_ENTERPRISE_ORG_ID,
client_secret: process.env.CONTEXT_API_SECRET,
scope: 'context-engine:read context-engine:write background-agents:read background-agents:write'
})
});
JWT Token Authentication
Token Generation
JWT tokens provide secure, time-limited access to Context APIs:Copy
Ask AI
class ContextAuthManager {
constructor(apiKey, orgId, apiSecret) {
this.apiKey = apiKey;
this.orgId = orgId;
this.apiSecret = apiSecret;
this.token = null;
this.tokenExpiry = null;
}
async getValidToken() {
// Check if we have a valid token
if (this.token && this.tokenExpiry > Date.now() + 300000) { // 5 min buffer
return this.token;
}
// Generate new token
const response = await fetch('https://api.context.ai/v1/auth/enterprise/token', {
method: 'POST',
headers: {
'X-API-Key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: this.orgId,
client_secret: this.apiSecret,
scope: 'context-engine:read context-engine:write background-agents:read background-agents:write'
})
});
if (!response.ok) {
throw new Error(`Authentication failed: ${response.statusText}`);
}
const data = await response.json();
this.token = data.access_token;
this.tokenExpiry = Date.now() + (data.expires_in * 1000);
return this.token;
}
async makeAuthenticatedRequest(url, options = {}) {
const token = await this.getValidToken();
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...options.headers
};
return fetch(url, { ...options, headers });
}
}
// Usage
const authManager = new ContextAuthManager(
process.env.CONTEXT_API_KEY,
process.env.CONTEXT_ENTERPRISE_ORG_ID,
process.env.CONTEXT_API_SECRET
);
// Make authenticated API calls
const response = await authManager.makeAuthenticatedRequest(
'https://api.context.ai/v1/context-engine/query',
{
method: 'POST',
body: JSON.stringify({
query: "Analyze market trends",
sources: ["financial_reports"]
})
}
);
Token Validation
Validate JWT tokens before making API calls:Copy
Ask AI
function validateToken(token) {
try {
// Decode JWT payload (without verification - for checking expiry)
const payload = JSON.parse(atob(token.split('.')[1]));
const now = Math.floor(Date.now() / 1000);
const isExpired = payload.exp < now;
return {
isValid: !isExpired,
expiresAt: payload.exp * 1000,
timeToExpiry: (payload.exp - now) * 1000,
organizationId: payload.org_id,
scopes: payload.scope ? payload.scope.split(' ') : []
};
} catch (error) {
return {
isValid: false,
error: 'Invalid token format'
};
}
}
// Usage
const tokenInfo = validateToken(jwtToken);
if (!tokenInfo.isValid) {
console.log('Token needs refresh');
// Refresh token logic here
}
Enterprise SSO Integration
Okta Integration
Configure Okta SSO for user authentication in enterprise deployments:Okta Configuration
Copy
Ask AI
# sso-config.yml
sso:
provider: "okta"
configuration:
domain: "yourcompany.okta.com"
client_id: "${OKTA_CLIENT_ID}"
client_secret: "${OKTA_CLIENT_SECRET}"
redirect_uri: "https://api.yourcompany.com/auth/callback"
scopes: ["openid", "profile", "email", "groups"]
# Map Okta attributes to Context permissions
attribute_mapping:
email: "preferred_username"
name: "name"
department: "department"
context_role: "context_role"
# Define role-based permissions
role_permissions:
"context_admin":
- "context-engine:read"
- "context-engine:write"
- "background-agents:read"
- "background-agents:write"
- "admin:users"
- "admin:settings"
"context_analyst":
- "context-engine:read"
- "background-agents:read"
- "background-agents:write"
"context_viewer":
- "context-engine:read"
- "background-agents:read"
Okta SSO Implementation
Copy
Ask AI
const OktaJwtVerifier = require('@okta/jwt-verifier');
class OktaSSO {
constructor(oktaConfig) {
this.oktaVerifier = new OktaJwtVerifier({
issuer: `https://${oktaConfig.domain}/oauth2/default`,
clientId: oktaConfig.client_id,
assertClaims: {
aud: 'api://context-enterprise',
cid: oktaConfig.client_id
}
});
}
async verifyToken(oktaToken) {
try {
const jwt = await this.oktaVerifier.verifyAccessToken(oktaToken, 'api://context-enterprise');
return {
isValid: true,
user: {
id: jwt.claims.uid,
email: jwt.claims.sub,
name: jwt.claims.name,
department: jwt.claims.department,
groups: jwt.claims.groups || []
}
};
} catch (error) {
return {
isValid: false,
error: error.message
};
}
}
async generateContextToken(oktaUser) {
// Convert Okta user to Context permissions
const contextPermissions = this.mapUserToPermissions(oktaUser);
// Generate Context JWT token
const response = await fetch('https://api.context.ai/v1/auth/enterprise/user-token', {
method: 'POST',
headers: {
'X-API-Key': process.env.CONTEXT_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: oktaUser.id,
email: oktaUser.email,
permissions: contextPermissions,
organization_id: process.env.CONTEXT_ENTERPRISE_ORG_ID
})
});
return await response.json();
}
mapUserToPermissions(user) {
const rolePermissions = {
'context_admin': ['context-engine:read', 'context-engine:write', 'background-agents:read', 'background-agents:write'],
'context_analyst': ['context-engine:read', 'background-agents:read', 'background-agents:write'],
'context_viewer': ['context-engine:read', 'background-agents:read']
};
// Extract role from user groups or department
const userRole = user.groups.find(group => group.startsWith('context_')) || 'context_viewer';
return rolePermissions[userRole] || rolePermissions['context_viewer'];
}
}
// Express.js middleware for SSO authentication
app.use('/auth/okta/callback', async (req, res) => {
const { code } = req.query;
try {
// Exchange authorization code for tokens
const tokenResponse = await exchangeCodeForTokens(code);
const oktaToken = tokenResponse.access_token;
// Verify Okta token
const oktaSSO = new OktaSSO(oktaConfig);
const userInfo = await oktaSSO.verifyToken(oktaToken);
if (userInfo.isValid) {
// Generate Context token
const contextToken = await oktaSSO.generateContextToken(userInfo.user);
// Store in secure session
req.session.contextToken = contextToken.access_token;
req.session.user = userInfo.user;
res.redirect('/dashboard');
} else {
res.status(401).json({ error: 'Invalid authentication' });
}
} catch (error) {
console.error('SSO authentication error:', error);
res.status(500).json({ error: 'Authentication failed' });
}
});
Azure AD Integration
Configure Azure AD for enterprise SSO:Copy
Ask AI
const { Client } = require('@azure/msal-node');
class AzureADSSO {
constructor(azureConfig) {
this.msalClient = new Client({
auth: {
clientId: azureConfig.client_id,
authority: `https://login.microsoftonline.com/${azureConfig.tenant_id}`,
clientSecret: azureConfig.client_secret
}
});
}
async verifyToken(azureToken) {
try {
// Verify Azure AD token
const response = await this.msalClient.acquireTokenSilent({
account: azureToken.account,
scopes: ['User.Read']
});
return {
isValid: true,
user: {
id: response.account.homeAccountId,
email: response.account.username,
name: response.account.name,
tenantId: response.account.tenantId
}
};
} catch (error) {
return {
isValid: false,
error: error.message
};
}
}
}
WebSocket Authentication
WebSocket Token Generation
For real-time features, generate WebSocket-specific tokens:Copy
Ask AI
async function getWebSocketToken(apiToken) {
const response = await fetch('https://api.context.ai/v1/auth/websocket-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
purposes: ['background_agent_updates', 'context_engine_stream'],
expires_in: 3600 // 1 hour
})
});
const data = await response.json();
return data.websocket_token;
}
WebSocket Connection with Authentication
Copy
Ask AI
class AuthenticatedWebSocket {
constructor(apiToken) {
this.apiToken = apiToken;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
async connect() {
try {
const wsToken = await this.getWebSocketToken();
this.ws = new WebSocket('wss://api.context.ai/v1/ws');
this.ws.onopen = () => {
console.log('WebSocket connected, authenticating...');
this.authenticate(wsToken);
};
this.ws.onmessage = (event) => {
this.handleMessage(JSON.parse(event.data));
};
this.ws.onclose = (event) => {
console.log('WebSocket closed:', event.code, event.reason);
this.handleReconnection();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
} catch (error) {
console.error('Failed to establish WebSocket connection:', error);
}
}
authenticate(wsToken) {
this.send({
type: 'authenticate',
token: wsToken,
organization_id: process.env.CONTEXT_ENTERPRISE_ORG_ID
});
}
handleMessage(message) {
switch (message.type) {
case 'authenticated':
console.log('WebSocket authenticated successfully');
this.reconnectAttempts = 0;
break;
case 'authentication_failed':
console.error('WebSocket authentication failed:', message.error);
break;
case 'task_update':
console.log('Background task update:', message.data);
break;
default:
console.log('Unknown message type:', message.type);
}
}
async getWebSocketToken() {
const response = await fetch('https://api.context.ai/v1/auth/websocket-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
return data.websocket_token;
}
handleReconnection() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000; // Exponential backoff
console.log(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connect();
}, delay);
} else {
console.error('Max reconnection attempts reached');
}
}
send(message) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
} else {
console.error('WebSocket is not connected');
}
}
close() {
if (this.ws) {
this.ws.close();
}
}
}
// Usage
const authManager = new ContextAuthManager(/* credentials */);
const wsToken = await authManager.getValidToken();
const authenticatedWS = new AuthenticatedWebSocket(wsToken);
await authenticatedWS.connect();
Security Best Practices
Token Security
Storage
- Store tokens in secure, encrypted storage
- Never log or expose tokens in client-side code
- Use environment variables for API keys
- Implement proper token rotation
Transmission
- Always use HTTPS/WSS for API communications
- Implement certificate pinning where possible
- Use TLS 1.3 or higher
- Validate SSL certificates
Access Control
Copy
Ask AI
class ContextAccessControl {
constructor(userPermissions) {
this.permissions = userPermissions;
}
canAccess(resource, action) {
const requiredPermission = `${resource}:${action}`;
return this.permissions.includes(requiredPermission) ||
this.permissions.includes('admin:all');
}
validateRequest(req, res, next) {
const { resource, action } = req.params;
if (this.canAccess(resource, action)) {
next();
} else {
res.status(403).json({
error: 'Insufficient permissions',
required: `${resource}:${action}`,
available: this.permissions
});
}
}
// Middleware factory
requirePermission(resource, action) {
return (req, res, next) => {
if (this.canAccess(resource, action)) {
next();
} else {
res.status(403).json({ error: 'Access denied' });
}
};
}
}
// Usage with Express.js
app.use('/api/v1/context-engine',
authenticateUser,
accessControl.requirePermission('context-engine', 'read')
);
Rate Limiting and Monitoring
Copy
Ask AI
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
// Configure rate limiting by authentication method
const createRateLimit = (windowMs, max) => rateLimit({
windowMs: windowMs,
max: max,
message: {
error: 'Too many requests',
retryAfter: Math.ceil(windowMs / 1000)
},
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => {
// Different limits for different auth methods
if (req.headers['x-api-key']) {
return `api-key:${req.headers['x-api-key'].substring(0, 10)}`;
} else if (req.user) {
return `user:${req.user.id}`;
}
return req.ip;
}
});
// Apply different limits for different endpoints
app.use('/api/v1/auth', createRateLimit(15 * 60 * 1000, 5)); // 5 requests per 15 minutes
app.use('/api/v1/context-engine', createRateLimit(60 * 1000, 100)); // 100 requests per minute
app.use('/api/v1/background-agents', createRateLimit(60 * 1000, 50)); // 50 requests per minute
Error Handling
Authentication Errors
Handle various authentication error scenarios:Copy
Ask AI
class AuthErrorHandler {
static handleAuthError(error, req, res, next) {
switch (error.code) {
case 'INVALID_API_KEY':
res.status(401).json({
error: 'Invalid API key',
code: 'INVALID_API_KEY',
message: 'The provided API key is invalid or has been revoked'
});
break;
case 'TOKEN_EXPIRED':
res.status(401).json({
error: 'Token expired',
code: 'TOKEN_EXPIRED',
message: 'JWT token has expired. Please obtain a new token.',
expires_at: error.expires_at
});
break;
case 'INSUFFICIENT_PERMISSIONS':
res.status(403).json({
error: 'Insufficient permissions',
code: 'INSUFFICIENT_PERMISSIONS',
message: 'Your account does not have permission to access this resource',
required_permissions: error.required_permissions,
user_permissions: error.user_permissions
});
break;
case 'RATE_LIMIT_EXCEEDED':
res.status(429).json({
error: 'Rate limit exceeded',
code: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests. Please try again later.',
retry_after: error.retry_after,
limit: error.limit
});
break;
default:
res.status(500).json({
error: 'Authentication error',
code: 'UNKNOWN_AUTH_ERROR',
message: 'An unexpected authentication error occurred'
});
}
}
}
// Apply error handler
app.use(AuthErrorHandler.handleAuthError);
Testing Authentication
Unit Tests
Copy
Ask AI
const test = require('ava');
const sinon = require('sinon');
const ContextAuthManager = require('./auth-manager');
test('should generate valid JWT token', async t => {
const authManager = new ContextAuthManager(
'test-api-key',
'test-org-id',
'test-secret'
);
// Mock fetch
const mockResponse = {
ok: true,
json: () => Promise.resolve({
access_token: 'test-jwt-token',
expires_in: 3600
})
};
sinon.stub(global, 'fetch').resolves(mockResponse);
const token = await authManager.getValidToken();
t.is(token, 'test-jwt-token');
t.true(authManager.tokenExpiry > Date.now());
global.fetch.restore();
});
test('should handle authentication errors', async t => {
const authManager = new ContextAuthManager('invalid-key', 'test-org', 'test-secret');
const mockResponse = {
ok: false,
statusText: 'Unauthorized'
};
sinon.stub(global, 'fetch').resolves(mockResponse);
await t.throwsAsync(
() => authManager.getValidToken(),
{ message: /Authentication failed/ }
);
global.fetch.restore();
});
Integration Tests
Copy
Ask AI
test('end-to-end authentication flow', async t => {
// Test full authentication flow
const response = await fetch(`${process.env.TEST_API_URL}/auth/enterprise/token`, {
method: 'POST',
headers: {
'X-API-Key': process.env.TEST_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: process.env.TEST_ORG_ID,
client_secret: process.env.TEST_API_SECRET,
scope: 'context-engine:read'
})
});
t.is(response.status, 200);
const data = await response.json();
t.truthy(data.access_token);
t.is(typeof data.expires_in, 'number');
// Test using the token
const apiResponse = await fetch(`${process.env.TEST_API_URL}/context-engine/health`, {
headers: {
'Authorization': `Bearer ${data.access_token}`
}
});
t.is(apiResponse.status, 200);
});