Skip to content

🔐 VideoAnnotator Client Token Setup Guide

Overview

This guide provides secure, user-friendly methods for setting up authentication tokens between VideoAnnotator clients and servers. The system supports multiple authentication flows designed for different use cases.

Target Audience: Client developers, system administrators, end users Server Version: VideoAnnotator API v1.4.1+


🎯 Authentication Methods

  • Use Case: Long-term programmatic access, production deployments
  • Lifetime: Configurable (30-365 days, or no expiration)
  • Security: High - encrypted storage, secure generation
  • Management: Full lifecycle management via CLI tools

2. Session Tokens (Web Applications)

  • Use Case: Web-based clients, temporary access
  • Lifetime: Short-term (1-24 hours)
  • Security: JWT-based with automatic expiration
  • Management: Automatic cleanup, refresh token support

3. Development Tokens (Development Only)

  • Use Case: Local development, testing, debugging
  • Lifetime: No expiration
  • Security: Basic - hardcoded values
  • Management: Built-in for convenience

🚀 Quick Start

For End Users (Client Applications)

# Set your API token (most secure)
export VIDEOANNOTATOR_API_TOKEN="va_api_your_token_here"
export VIDEOANNOTATOR_API_URL="http://your-server:18011"

# Start your client application
npm run dev
# or
python your_client.py

Option 2: Configuration File

Create ~/.videoannotator/config.json:

{
  "api_url": "http://your-server:18011",
  "api_token": "va_api_your_token_here",
  "user_info": {
    "username": "your_username",
    "email": "your@email.com"
  }
}

Option 3: Interactive Login

Many clients support interactive token entry:

# Client prompts for token on first run
videoannotator-client
> Enter your API token: va_api_your_token_here
> Token saved securely

For Developers (Testing)

// Use development tokens for testing
const apiToken = "dev-token"; // Built-in development token
const apiUrl = "http://localhost:18011";

// Make authenticated requests
fetch(`${apiUrl}/api/v1/jobs/`, {
  headers: {
    Authorization: `Bearer ${apiToken}`,
    "Content-Type": "application/json",
  },
});

macOS Notes

  • Apple Silicon (M1/M2) may require OpenMP runtime for some audio models. If you see OpenMP or segfault errors, install:
brew install libomp
  • Ensure Node.js is installed if using the web viewer:
brew install node

🔧 Server-Side Token Management

Creating API Keys (Server Administrator)

# Create new API key interactively (stored in database)
uv run videoannotator generate-token
> Enter user email: user@example.com
> Enter username (default: user): user
> Enter key name/description: My API Key
> Expires in days (default: 365, 0 for no expiry): 365

# Result
[SUCCESS] API Key Created Successfully!
Token:      va_...
User:       user (user@example.com)
...

Command-Line Method

# Create API key with specific parameters
uv run videoannotator generate-token \
  --user user@example.com \
  --username user \
  --key-name "Production client access" \
  --expires-days 365 \
  --output user-token.json

# Create token with no expiration
uv run videoannotator generate-token \
  --user admin@example.com \
  --no-expiry \
  --key-name "Admin access token"

Token Management Commands

Note: The videoannotator CLI currently supports token generation. For listing or revoking database-backed tokens, you may need to access the database directly or use the legacy scripts/manage_tokens.py for file-based tokens (not recommended for new deployments).

# Initialize database and create admin user
uv run videoannotator setup-db --create-admin

🛡️ Security Best Practices

For Server Administrators

  1. Token Storage Security:
# Tokens are automatically encrypted on disk
ls -la tokens/
# -rw------- tokens.json      (encrypted)
# -rw------- encryption.key   (secure key)
  1. Regular Token Rotation:
# Set up automated cleanup
crontab -e
# Add: 0 2 * * * cd /path/to/videoannotator && python scripts/manage_tokens.py cleanup

# Monitor token usage
python scripts/manage_tokens.py stats
  1. Scope Management:
  2. read: View data, download results
  3. write: Submit jobs, upload videos
  4. admin: User management, server configuration
  5. debug: Access debug endpoints, detailed logs

For Client Developers

  1. Secure Token Storage:
// ❌ DON'T: Store in code or plain text
const apiToken = "va_api_hardcoded_token";
localStorage.setItem("token", "va_api_plaintext");

// ✅ DO: Use secure storage
// Environment variables
const apiToken = process.env.VIDEOANNOTATOR_API_TOKEN;

// Encrypted storage (web)
import { EncryptedStorage } from "secure-storage";
const storage = new EncryptedStorage("your-secret-key");
storage.setItem("api_token", token);

// OS keychain (desktop)
import keytar from "keytar";
keytar.setPassword("videoannotator", "api_token", token);
  1. Token Validation:
// Validate token before making requests
async function validateToken(token) {
  try {
    const response = await fetch(`${API_URL}/api/v1/debug/token-info`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    return response.ok;
  } catch {
    return false;
  }
}
  1. Error Handling:
    // Handle authentication errors gracefully
    fetch(url, { headers: { Authorization: `Bearer ${token}` } }).then(
      (response) => {
        if (response.status === 401) {
          // Token expired or invalid
          promptForNewToken();
          return;
        }
        return response.json();
      },
    );
    

For End Users

  1. Token Security:

  2. Never share your API token

  3. Don't paste tokens in chat/email
  4. Use secure password managers
  5. Report lost/stolen tokens immediately

  6. Token Lifecycle:

  7. Note your token expiration date
  8. Request new tokens before expiration
  9. Revoke old tokens when no longer needed

🔄 Client Integration Examples

React/JavaScript Client

// config.js
export const API_CONFIG = {
  baseURL: process.env.REACT_APP_API_URL || "http://localhost:18011",
  token:
    process.env.REACT_APP_API_TOKEN ||
    localStorage.getItem("videoannotator_token"),
};

// api-client.js
class VideoAnnotatorAPI {
  constructor(config) {
    this.baseURL = config.baseURL;
    this.token = config.token;
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const headers = {
      "Content-Type": "application/json",
      Authorization: `Bearer ${this.token}`,
      ...options.headers,
    };

    const response = await fetch(url, { ...options, headers });

    if (response.status === 401) {
      throw new Error("Authentication failed - check your API token");
    }

    return response.json();
  }

  // API methods
  async getJobs() {
    return this.request("/api/v1/jobs/");
  }

  async submitJob(videoFile, pipelines) {
    const formData = new FormData();
    formData.append("video", videoFile);
    formData.append("selected_pipelines", pipelines.join(","));

    return this.request("/api/v1/jobs/", {
      method: "POST",
      headers: {}, // Let browser set Content-Type for FormData
      body: formData,
    });
  }
}

// usage
const client = new VideoAnnotatorAPI(API_CONFIG);

Python Client

# videoannotator_client.py
import os
import requests
from pathlib import Path
import json

class VideoAnnotatorClient:
    def __init__(self, api_url=None, api_token=None):
    self.api_url = api_url or os.getenv('VIDEOANNOTATOR_API_URL', 'http://localhost:18011')
        self.api_token = api_token or self._load_token()

        if not self.api_token:
            raise ValueError("API token required. Set VIDEOANNOTATOR_API_TOKEN environment variable or ~/.videoannotator/config.json")

    def _load_token(self):
        # Try environment variable first
        token = os.getenv('VIDEOANNOTATOR_API_TOKEN')
        if token:
            return token

        # Try config file
        config_file = Path.home() / '.videoannotator' / 'config.json'
        if config_file.exists():
            with open(config_file) as f:
                config = json.load(f)
                return config.get('api_token')

        return None

    def _request(self, method, endpoint, **kwargs):
        url = f"{self.api_url}{endpoint}"
        headers = kwargs.pop('headers', {})
        headers['Authorization'] = f'Bearer {self.api_token}'

        response = requests.request(method, url, headers=headers, **kwargs)

        if response.status_code == 401:
            raise Exception('Authentication failed - check your API token')

        response.raise_for_status()
        return response.json()

    def get_jobs(self):
        return self._request('GET', '/api/v1/jobs/')

    def submit_job(self, video_path, pipelines):
        with open(video_path, 'rb') as f:
            files = {'video': f}
            data = {'selected_pipelines': ','.join(pipelines)}
            return self._request('POST', '/api/v1/jobs/', files=files, data=data)

# Usage
client = VideoAnnotatorClient()
jobs = client.get_jobs()

Configuration File Setup

# Create user config directory
mkdir -p ~/.videoannotator

# Create configuration file
cat > ~/.videoannotator/config.json << EOF
{
  "api_url": "http://your-server:18011",
  "api_token": "va_api_your_token_here",
  "user_info": {
    "username": "your_username",
    "email": "your@email.com"
  },
  "client_settings": {
    "timeout": 30,
    "retry_count": 3,
    "auto_refresh": true
  }
}
EOF

# Secure the config file
chmod 600 ~/.videoannotator/config.json

🔍 Troubleshooting

Common Issues

"Authentication failed - check your API token"

# 1. Ensure you are using a valid token
# If using database tokens (recommended), ensure the token exists in the DB.

# 2. Create new token if needed
uv run videoannotator generate-token --user your@email.com

"Connection refused" or "Server not found"

# Check server status
curl http://your-server:18011/health

# Check if server is running
ps aux | grep videoannotator

# Check server logs (the server prints its log folder at start: "[INFO] Logs: ...";
# this is the Linux default)
tail -f ~/.local/state/videoannotator/logs/api_server.log

"Permission denied" or "Insufficient scopes"

# Request token with appropriate permissions
# Note: Database tokens currently default to read/write scopes.
uv run videoannotator generate-token \
  --user your@email.com \
  --key-name "Admin Access"

Development Debugging

# Use debug endpoints to test authentication
curl -H "Authorization: Bearer your_token" \
  http://localhost:18011/api/v1/debug/token-info

# Check server logs for authentication issues
tail -f ~/.local/state/videoannotator/logs/api_requests.log | grep "401\|403"

# Test with development token
curl -H "Authorization: Bearer dev-token" \
  http://localhost:18011/api/v1/debug/server-info

📚 Integration Checklist

For Client Developers

  • [ ] Token Storage: Implement secure token storage (environment variables, encrypted storage, or OS keychain)
  • [ ] Token Validation: Validate tokens before making requests
  • [ ] Error Handling: Handle 401/403 responses gracefully
  • [ ] Token Refresh: Implement token refresh logic for session tokens
  • [ ] Fallback Methods: Support multiple token input methods (env vars, config files, interactive)
  • [ ] Security: Never log or expose tokens in client-side code
  • [ ] User Experience: Provide clear authentication error messages

For Server Administrators

  • [ ] Token Management: Set up regular token cleanup and monitoring
  • [ ] Scope Configuration: Define appropriate scopes for different user roles
  • [ ] Security: Ensure token storage encryption and secure file permissions
  • [ ] Monitoring: Monitor token usage and authentication failures
  • [ ] Documentation: Provide clear token setup instructions for users
  • [ ] Backup: Include token storage in backup procedures

For End Users

  • [ ] Token Acquisition: Get API token from administrator or self-service portal
  • [ ] Secure Storage: Store token securely (not in plain text)
  • [ ] Configuration: Set up client configuration with token and server URL
  • [ ] Testing: Test authentication with client application
  • [ ] Monitoring: Note token expiration and renewal requirements

🎯 Advanced Features

Session Token with Refresh

class TokenManager {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
    this.accessToken = localStorage.getItem("access_token");
    this.refreshToken = localStorage.getItem("refresh_token");
  }

  async refreshAccessToken() {
    const response = await fetch(`${this.apiUrl}/api/v1/auth/refresh`, {
      method: "POST",
      headers: { Authorization: `Bearer ${this.refreshToken}` },
    });

    if (response.ok) {
      const { access_token } = await response.json();
      this.accessToken = access_token;
      localStorage.setItem("access_token", access_token);
      return access_token;
    }

    throw new Error("Token refresh failed");
  }

  async authenticatedRequest(url, options = {}) {
    let token = this.accessToken;

    // Try request with current token
    let response = await fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${token}`,
        ...options.headers,
      },
    });

    // If 401, try refreshing token
    if (response.status === 401 && this.refreshToken) {
      try {
        token = await this.refreshAccessToken();
        response = await fetch(url, {
          ...options,
          headers: {
            Authorization: `Bearer ${token}`,
            ...options.headers,
          },
        });
      } catch {
        // Refresh failed, redirect to login
        this.redirectToLogin();
      }
    }

    return response;
  }
}

Multi-Environment Configuration

# ~/.videoannotator/environments.json
{
  "development": {
    "api_url": "http://localhost:18011",
    "api_token": "dev-token"
  },
  "staging": {
    "api_url": "https://staging-api.example.com",
    "api_token": "va_api_staging_token_here"
  },
  "production": {
    "api_url": "https://api.example.com",
    "api_token": "va_api_production_token_here"
  }
}

# Usage
export VIDEOANNOTATOR_ENV=production
videoannotator-client  # Automatically uses production config

Document Version: v1.0 Last Updated: 24 August 2025 Compatible With: VideoAnnotator API v1.4.1+ Status: Production ready with comprehensive security features