Authentication
Learn how to authenticate API requests using API keys and JWT tokens.
Two Authentication Methods
The Control API uses two different authentication methods:
1. Kaltura Session (KS) (for session creation)
2. JWT Token (for session control)
Kaltura Session (KS) Authentication
Used for: Creating new sessions
Header: Authorization: KS <ks>
Example:
curl -X POST https://api.avatar.us.kaltura.ai/v1/avatar-session/create \
-H "Authorization: KS your-kaltura-session-here" \
-H "Content-Type: application/json" \
-d '{"visualConfig": {"avatarId": "avatar-123"}}'
Getting Your Kaltura Session
You can generate a Kaltura Session (KS) using the Kaltura API. See the Kaltura Session Creation Guide for detailed instructions.
Alternatively, contact your Kaltura representative for assistance.
Storing Your Kaltura Session
NEVER expose your Kaltura Session in:
- Client-side code
- Frontend applications
- Public repositories
- Browser DevTools
ALWAYS store it:
- In environment variables
- In secure secret managers
- On your backend server only
Good practices:
# .env file
AVATAR_KS=your-kaltura-session-here
// Node.js
const KS = process.env.AVATAR_KS;
# Python
import os
KS = os.environ.get('AVATAR_KS')
JWT Token Authentication
Used for: All session operations (except creation)
Header: Authorization: Bearer {token}
Example:
curl -X POST https://api.avatar.us.kaltura.ai/v1/avatar-session/:sessionId/say-text \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{"text": "Hello!"}'
How to Get a Token
The JWT token is returned when you create a session:
const response = await fetch('https://api.avatar.us.kaltura.ai/v1/avatar-session/create', {
method: 'POST',
headers: {
Authorization: `KS ${process.env.AVATAR_KS}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
visualConfig: { avatarId: 'avatar-123' },
}),
});
const data = await response.json();
console.log('Token:', data.token); // JWT token here
console.log('Session ID:', data.sessionId);
Token Properties
- Scope: Valid only for the specific session
- Expiration: Tied to session lifetime
- Permissions: Full control over the session
- Single use: One token per session
Using the Token
// After creating session
const { sessionId, token } = await createSession();
// Use token for all operations
await fetch(`https://api.avatar.us.kaltura.ai/v1/avatar-session/${sessionId}/say-text`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'Hello!' }),
});
Authentication Flow
┌──────────────┐
│ Backend │
│ │
│ 1. Store │
│ KS │
└──────┬───────┘
│
│ Create session with KS
▼
┌──────────────┐
│ Avatar API │
│ │
│ 2. Validate │
│ KS │
│ │
│ 3. Generate │
│ JWT token │
└──────┬───────┘
│
│ Return sessionId + token
▼
┌──────────────┐
│ Backend │
│ │
│ 4. Send to │
│ frontend │
└──────┬───────┘
│
│ sessionId + token
▼
┌──────────────┐
│ Frontend │
│ │
│ 5. Use token │
│ for │
│ display │
└──────────────┘
Complete Example
Backend Server
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.use(express.json());
// Kaltura Session stored securely
const AVATAR_KS = process.env.AVATAR_KS;
const AVATAR_BASE_URL = 'https://api.avatar.us.kaltura.ai/v1/avatar-session';
// Session storage (in-memory for demo, use database in production)
const sessions = new Map();
// Create session endpoint
app.post('/api/avatar/create-session', async (req, res) => {
try {
// Call Kaltura API with KS
const response = await fetch(`${AVATAR_BASE_URL}/create`, {
method: 'POST',
headers: {
Authorization: `KS ${AVATAR_KS}`, // Secure on server
'Content-Type': 'application/json',
},
body: JSON.stringify({
visualConfig: { avatarId: req.body.avatarId },
voiceConfig: req.body.voiceId ? { id: req.body.voiceId } : undefined,
}),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
// Store session info
sessions.set(data.sessionId, {
token: data.token,
createdAt: new Date(),
});
// Return to frontend (token is safe to send)
res.json({
sessionId: data.sessionId,
token: data.token,
});
} catch (error) {
console.error('Failed to create session:', error);
res.status(500).json({ error: 'Failed to create session' });
}
});
// Control endpoint (uses token)
app.post('/api/avatar/say-text', async (req, res) => {
try {
const { sessionId, token, text } = req.body;
// Verify session exists
if (!sessions.has(sessionId)) {
return res.status(404).json({ error: 'Session not found' });
}
// Call Kaltura API with token
const response = await fetch(`${AVATAR_BASE_URL}/${sessionId}/say-text`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
res.json(data);
} catch (error) {
console.error('Failed to say text:', error);
res.status(500).json({ error: 'Failed to say text' });
}
});
Frontend Client
// Create session (token from backend)
const { sessionId, token } = await fetch('/api/avatar/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
avatarId: 'avatar-123',
voiceId: 'voice-456',
}),
}).then((r) => r.json());
console.log('Session ID:', sessionId);
console.log('Token:', token);
// Use token for display
import { KalturaAvatarSession } from '@unisphere/models-sdk-js';
const session = new KalturaAvatarSession(token, {
baseUrl: 'https://api.avatar.us.kaltura.ai/v1/avatar-session',
});
session.attachAvatar('avatar-container');
// Control from backend
await fetch('/api/avatar/say-text', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId,
token,
text: 'Hello from the backend!',
}),
});
Error Responses
401 Unauthorized
Kaltura Session:
{
"success": false,
"error": "Invalid Kaltura Session"
}
Causes:
- Kaltura Session is missing
- Kaltura Session is invalid
- Kaltura Session is expired
Solutions:
- Verify Kaltura Session is correct
- Check
Authorization: KS <ks>header is set - Contact Kaltura for new session
Token:
{
"success": false,
"error": "Invalid or expired token"
}
Causes:
- Token is missing
- Token is invalid
- Session has ended
- Token has expired
Solutions:
- Verify token is correct
- Check
Authorization: Bearer {token}header - Create a new session if needed
403 Forbidden
{
"success": false,
"error": "Insufficient permissions"
}
Causes:
- Kaltura Session doesn't have required permissions
- Token doesn't match session
Security Best Practices
1. Kaltura Session Security
✅ DO:
- Store in environment variables
- Use secret managers (AWS Secrets Manager, Azure Key Vault)
- Rotate sessions regularly
- Limit session scope if possible
❌ DON'T:
- Commit to version control
- Include in client code
- Share via email or chat
- Log in plain text
2. Token Security
✅ DO:
- Send tokens over HTTPS only
- Store tokens temporarily
- Clear tokens when session ends
- Validate tokens server-side
❌ DON'T:
- Store tokens long-term
- Share tokens between users
- Log tokens in plain text
3. HTTPS Only
Always use HTTPS in production:
// ❌ Bad - HTTP
const baseUrl = 'http://api.avatar.example.com';
// ✅ Good - HTTPS
const baseUrl = 'https://api.avatar.example.com';
4. Request Validation
Validate all requests before calling the API:
app.post('/api/avatar/say-text', async (req, res) => {
// Validate input
if (!req.body.text || req.body.text.length > 1000) {
return res.status(400).json({ error: 'Invalid text' });
}
// Validate session exists
if (!sessions.has(req.body.sessionId)) {
return res.status(404).json({ error: 'Session not found' });
}
// Call API
// ...
});
Next Steps
- Create Session - Create your first session
- Say Text - Make the avatar speak
- API Examples - Complete working examples