Skip to main content
View as Markdown

Authentication - Agentic Avatars SDK

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": {"id": "visual-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​

Security

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: { id: 'visual-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: { id: req.body.visualId },
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({
visualId: 'visual-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/',
});
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​