# 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:** ```http x-api-key: YOUR_API_KEY Authorization: Bearer vbasoftware-database: YOUR_DATABASE User-Agent: # 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python # 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 ```python 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.