Skip to content

Authentication Deep Dive

Master VBAPI's authentication system with comprehensive token management, security best practices, and multi-environment deployment strategies.


Overview

VBAPI uses a two-step authentication model designed for security and scalability:

  1. User Authentication — Exchange username/password credentials for an ID Token (JWT)
  2. Bearer Auth — Send the ID Token in the Authorization header for all API requests

This guide covers advanced authentication patterns, token lifecycle management, and production security practices.


Required Headers

Required headers for every request:

x-api-key: YOUR_API_KEY
Authorization: Bearer <id_token>
vbasoftware-database: YOUR_DATABASE
User-Agent: <your-app-name>   # required

Critical: User-Agent Header

Always include a clear User-Agent string. VBA uses it for:

  • Request fingerprinting
  • Debugging
  • Identifying traffic patterns
  • Issue triage via Support

Failing to set a User-Agent will result in a 403 Forbidden response.


Token Lifecycle Management

Token Expiration

  • ID Tokens are valid for 24 hours
  • Cache tokens locally to avoid unnecessary authentication calls
  • Implement automatic refresh when tokens near expiration

Token Refresh Strategy

import time
from datetime import datetime, timedelta

class VBAPITokenManager:
    def __init__(self, username, password, api_key, client_id, client_code, database):
        self.credentials = {
            'username': username,
            'password': password,
            'api_key': api_key,
            'client_id': client_id,
            'client_code': client_code,
            'database': database
        }
        self.token = None
        self.token_expires = None
    
    def get_valid_token(self):
        """Returns a valid token, refreshing if necessary"""
        if self.token is None or self._is_token_expired():
            self._refresh_token()
        return self.token
    
    def _is_token_expired(self):
        """Check if token expires in the next 5 minutes"""
        if self.token_expires is None:
            return True
        return datetime.now() + timedelta(minutes=5) >= self.token_expires
    
    def _refresh_token(self):
        """Refresh the authentication token"""
        try:
            self.token = self._get_id_token(
                self.credentials['username'], 
                self.credentials['password']
            )
            # Tokens expire after 24 hours
            self.token_expires = datetime.now() + timedelta(hours=23, minutes=55)
        except Exception as e:
            print(f"Token refresh failed: {e}")
            raise
    
    def _get_id_token(self, username, password):
        """Get ID token from VBAPI"""
        import requests
        import base64
        
        url = "https://vbapi.vbasoftware.com/vbasoftware/user-authentication"
        
        # Encode credentials: "username:password"
        raw = f"{username}:{password}"
        encoded = base64.b64encode(raw.encode()).decode()
        
        headers = {
            "x-api-key": self.credentials['api_key'],
            "Authorization": f"Basic {encoded}",
            "vbasoftware-client-id": self.credentials['client_id'],
            "vbasoftware-client-code": self.credentials['client_code'],
            "vbasoftware-database": self.credentials['database'],
            "User-Agent": "VBAPITokenManager/1.0"
        }
        
        response = requests.post(url, headers=headers)
        response.raise_for_status()
        
        data = response.json()
        return data["data"]["authenticationResult"]["idToken"]

Security Best Practices

Credential Storage

  • Never hardcode credentials in source code
  • Use environment variables or secure credential stores
  • Rotate API keys regularly

Token Security

  • Store tokens in memory, not on disk
  • Clear tokens from memory when application exits
  • Use HTTPS for all API communications
  • Never log or expose tokens in plain text

Advanced Authentication Patterns

Singleton Token Manager

import threading

class SingletonTokenManager:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, 'initialized'):
            self.token_manager = VBAPITokenManager(
                username=os.getenv('VBAPI_USERNAME'),
                password=os.getenv('VBAPI_PASSWORD'),
                api_key=os.getenv('VBAPI_KEY'),
                client_id=os.getenv('VBAPI_CLIENT_ID'),
                client_code=os.getenv('VBAPI_CLIENT_CODE'),
                database=os.getenv('VBAPI_DATABASE')
            )
            self.initialized = True
    
    def get_token(self):
        return self.token_manager.get_valid_token()

# Global token manager
token_manager = SingletonTokenManager()

Async Token Management

import asyncio
import aiohttp
import base64

class AsyncTokenManager:
    def __init__(self, credentials):
        self.credentials = credentials
        self.token = None
        self.token_expires = None
        self._refresh_lock = asyncio.Lock()
    
    async def get_valid_token(self):
        """Get a valid token, refreshing if necessary"""
        if self.token is None or self._is_token_expired():
            async with self._refresh_lock:
                if self.token is None or self._is_token_expired():
                    await self._refresh_token()
        return self.token
    
    async def _refresh_token(self):
        """Asynchronously refresh the token"""
        url = "https://vbapi.vbasoftware.com/vbasoftware/user-authentication"
        
        raw = f"{self.credentials['username']}:{self.credentials['password']}"
        encoded = base64.b64encode(raw.encode()).decode()
        
        headers = {
            "x-api-key": self.credentials['api_key'],
            "Authorization": f"Basic {encoded}",
            "vbasoftware-client-id": self.credentials['client_id'],
            "vbasoftware-client-code": self.credentials['client_code'],
            "vbasoftware-database": self.credentials['database'],
            "User-Agent": "AsyncVBAPIClient/1.0"
        }
        
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers) as response:
                response.raise_for_status()
                data = await response.json()
                self.token = data["data"]["authenticationResult"]["idToken"]
                self.token_expires = datetime.now() + timedelta(hours=23, minutes=55)

Common Authentication Issues

Issue: 403 Forbidden with valid credentials

Cause: Missing or invalid User-Agent header
Solution: Always include a descriptive User-Agent header

headers = {
    "User-Agent": "MyCompany-Integration/1.2.3",
    # ... other headers
}

Issue: 429 Too Many Requests on authentication endpoint

Cause: Too frequent authentication calls
Solution: Implement proper token caching and reuse

# BAD: Authenticate for every request
def bad_api_call():
    token = get_new_token()  # Don't do this!
    return make_request(token)

# GOOD: Reuse cached tokens
def good_api_call():
    token = token_manager.get_valid_token()  # Cached and reused
    return make_request(token)

Issue: 401 Unauthorized with valid token

Cause: Token has expired or is invalid
Solution: Implement automatic token refresh

def resilient_api_call(url, data):
    """API call with automatic token refresh"""
    token = token_manager.get_valid_token()
    
    try:
        response = requests.post(url, headers={
            'Authorization': f'Bearer {token}',
            # ... other headers
        }, json=data)
        
        if response.status_code == 401:
            # Token might be expired, refresh and retry once
            token_manager.force_refresh()
            token = token_manager.get_valid_token()
            
            response = requests.post(url, headers={
                'Authorization': f'Bearer {token}',
                # ... other headers  
            }, json=data)
        
        response.raise_for_status()
        return response.json()
        
    except requests.exceptions.RequestException as e:
        print(f"API call failed: {e}")
        raise

This authentication guide provides the foundation for secure, scalable VBAPI integrations. Combine these patterns with proper error handling and monitoring.