CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-source-xero

Airbyte source connector for extracting financial and accounting data from Xero's cloud-based accounting platform.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

oauth-authentication.mddocs/

OAuth2 Authentication

OAuth2 authentication implementation for the Airbyte Source Xero connector. This module handles secure authentication with Xero's API, including automatic token refresh, rate limiting, and error handling.

Core Imports

import base64
import logging
from typing import Any, Mapping
import backoff
import requests
from airbyte_cdk.sources.streams.http.exceptions import DefaultBackoffException
from airbyte_cdk.sources.streams.http.requests_native_auth import SingleUseRefreshTokenOauth2Authenticator

Capabilities

Xero OAuth2 Authenticator

Custom OAuth2 authenticator that extends Airbyte CDK's standard OAuth2 implementation with Xero-specific requirements for token refresh and authentication headers.

class XeroSingleUseRefreshTokenOauth2Authenticator(SingleUseRefreshTokenOauth2Authenticator):
    """
    OAuth2 authenticator for Xero API with custom token refresh handling.
    
    Inherits from airbyte_cdk SingleUseRefreshTokenOauth2Authenticator and
    implements Xero-specific authentication patterns including Basic Auth
    headers for token refresh and specialized retry logic.
    """
    
    def build_refresh_request_body(self) -> Mapping[str, Any]:
        """
        Build request body for OAuth2 token refresh.
        
        Removes client_id and client_secret from request body as Xero
        requires these in the Authorization header instead.
        
        Returns:
        Request body mapping without client credentials
        """
    
    def build_refresh_request_headers(self) -> Mapping[str, Any]:
        """
        Build request headers for OAuth2 token refresh.
        
        Creates Basic Authentication header using client_id and client_secret
        as required by Xero's OAuth2 implementation.
        
        Returns:
        Headers mapping with Authorization header containing Basic auth credentials
        """
    
    def _get_refresh_access_token_response(self):
        """
        Execute token refresh request with Xero-specific retry logic.
        
        Implements backoff and retry for 429 (rate limiting) and 500+ 
        (server error) status codes with exponential backoff strategy.
        
        Returns:
        JSON response from token refresh endpoint
        
        Raises:
        requests.exceptions.RequestException: For unrecoverable HTTP errors
        """

Authentication Flow

Token Refresh Process

The Xero OAuth2 flow follows these steps:

  1. Initial Authentication: Uses provided access_token and refresh_token
  2. Token Validation: Checks token expiry before API requests
  3. Automatic Refresh: Refreshes expired tokens using refresh_token
  4. Retry Logic: Handles rate limiting and server errors during refresh
  5. Header Management: Applies proper authentication headers to API requests

Request Authentication

# The authenticator automatically handles:
AuthenticationHeaders = {
    "Authorization": "Bearer {access_token}",      # OAuth2 bearer token
    "Xero-Tenant-Id": "{tenant_id}",              # Required for all Xero API calls
    "Accept": "application/json",                  # Content type specification
    "User-Agent": "Airbyte/{version}"             # Client identification
}

Usage Examples

Manual Authenticator Creation

from source_xero.oauth import XeroSingleUseRefreshTokenOauth2Authenticator

# Configuration with OAuth2 credentials
config = {
    "authentication": {
        "client_id": "your-xero-client-id",
        "client_secret": "your-xero-client-secret",
        "refresh_token": "your-refresh-token", 
        "access_token": "current-access-token",
        "token_expiry_date": "2024-12-31T23:59:59Z"
    },
    "tenant_id": "your-xero-tenant-id"
}

# Create authenticator instance
authenticator = XeroSingleUseRefreshTokenOauth2Authenticator(
    connector_config=config,
    token_refresh_endpoint="https://identity.xero.com/connect/token",
    client_id=config["authentication"]["client_id"],
    client_secret=config["authentication"]["client_secret"],
    access_token_config_path=["authentication", "access_token"],
    refresh_token_config_path=["authentication", "refresh_token"],
    token_expiry_date_config_path=["authentication", "token_expiry_date"]
)

Integration with HTTP Requests

import requests

# The authenticator can be used with any HTTP client
session = requests.Session()

# Apply authentication to request
authenticated_request = authenticator.apply(
    session.prepare_request(
        requests.Request("GET", "https://api.xero.com/api.xro/2.0/Accounts")
    )
)

# Execute authenticated request
response = session.send(authenticated_request)

Token Refresh Handling

# Token refresh is handled automatically, but you can monitor it:
try:
    # This will automatically refresh token if needed
    response = authenticator._get_refresh_access_token_response()
    print("Token refreshed successfully")
except requests.exceptions.RequestException as e:
    print(f"Token refresh failed: {e}")

Error Handling

Rate Limiting (429 Responses)

The authenticator implements exponential backoff for rate limiting:

# Automatic retry with backoff for rate limiting
# - Initial delay: 1 second
# - Maximum retries: 3 attempts  
# - Backoff multiplier: 2x
# - Maximum delay: 60 seconds

Server Errors (500+ Responses)

Server errors are handled with retry logic:

# Retry for server errors (500, 502, 503, 504)
# - Same backoff strategy as rate limiting
# - Distinguishes between client (4xx) and server (5xx) errors
# - Only retries recoverable server errors

Authentication Errors (401 Responses)

# Token expiry and invalid credentials handling:
# - Automatic token refresh on 401 responses
# - Fallback to refresh_token if access_token is invalid
# - Error reporting for invalid refresh_token

Security Considerations

Credential Management

  • Client Secrets: Never logged or exposed in error messages
  • Tokens: Stored securely and rotated automatically
  • Refresh Tokens: Single-use pattern prevents token replay attacks
  • HTTPS Only: All authentication requests use secure HTTPS

Token Storage

# Secure token handling practices:
TokenSecurity = {
    "access_token": "Treated as secret, never logged",
    "refresh_token": "Single-use, invalidated after refresh", 
    "client_secret": "Never included in request bodies",
    "token_expiry": "Validated before each request"
}

Xero API Requirements

OAuth2 Configuration

To use this authenticator, you need:

  1. Xero Developer Account: Register at https://developer.xero.com
  2. Custom Connection: Create a custom connection (production) or demo company (testing)
  3. OAuth2 App: Configure OAuth2 application with appropriate scopes
  4. Credentials: Obtain client_id, client_secret, and initial tokens

Required Scopes

The connector requires these OAuth2 scopes for full functionality:

RequiredScopes = [
    "accounting.transactions",     # Access to financial transactions
    "accounting.contacts",         # Access to contact information  
    "accounting.settings",         # Access to account settings
    "accounting.attachments"       # Access to document attachments
]

API Limitations

  • Rate Limits: 60 API calls per minute per tenant
  • Token Lifetime: Access tokens expire after 30 minutes
  • Refresh Tokens: Single-use, must be stored after each refresh
  • Tenant Scope: Each token set is limited to one Xero organization

Install with Tessl CLI

npx tessl i tessl/pypi-source-xero

docs

data-utilities.md

full-refresh-streams.md

incremental-streams.md

index.md

oauth-authentication.md

source-configuration.md

tile.json