ATOM Documentation

← Back to App

OAuth Integration Fixes - Template for All Integrations

Overview

This document describes the OAuth flow fixes implemented for WhatsApp that should be applied to all other integrations (Slack, Zoom, Discord, Google, etc.) to prevent crashes and improve reliability.

Problems Fixed

1. **HTML Response Handling (Critical Bug)**

**Problem:** OAuth providers (Meta, Google, etc.) sometimes return HTML error pages instead of JSON when:

  • Invalid credentials
  • Rate limiting
  • Service errors
  • Configuration issues

**Symptom:** JSONDecodeError crashes the OAuth flow, leaving users stuck with no error message.

**Solution:** Added check_html_response() utility in core/oauth_utils.py that:

  • Checks Content-Type header for text/html
  • Attempts JSON parsing to validate response
  • Returns user-friendly error messages

**Usage:**

from core.oauth_utils import check_html_response

# Before (CRASHES):
token_data = await response.json()

# After (SAFE):
is_html, html_error = check_html_response(response)
if is_html:
    return error_response(html_error)
token_data = await response.json()

2. **Automatic Historical Sync**

**Problem:** Users had to manually start historical sync after connecting an integration.

**Solution:** Added trigger_historical_sync_on_connection() in core/oauth_utils.py that:

  • Automatically starts N-month historical sync after successful OAuth
  • Uses existing HistoricalSyncService
  • Gracefully handles failures (doesn't break OAuth if sync fails)

**Usage:**

from core.oauth_utils import trigger_historical_sync_on_connection

# In OAuth callback after saving connection:
job_id = await trigger_historical_sync_on_connection(
    integration_id="slack",  # or "zoom", "discord", etc.
    user_id=user_id,
    tenant_id=tenant_id,
    db=db,
    sync_months=3  # Default: 3 months
)

3. **Consistent Error Messages**

**Problem:** Different integrations had inconsistent error message formats.

**Solution:** Added format_oauth_error_message() for consistent error formatting.

**Usage:**

from core.oauth_utils import format_oauth_error_message

error_msg = format_oauth_error_message(
    provider="Slack",
    error="access_denied",
    error_description="User denied access"
)
# Returns: "Slack OAuth failed: access_denied - User denied access"

4. **State Parameter Management**

**Problem:** OAuth state parameters were inconsistent across integrations.

**Solution:** Added build_oauth_state() and parse_oauth_state() utilities.

**Usage:**

from core.oauth_utils import build_oauth_state, parse_oauth_state

# Build state for OAuth URL:
state = build_oauth_state(
    user_id="user-123",
    tenant_id="tenant-456",
    redirect_path="/integrations"
)

# Parse state in callback:
state_data = parse_oauth_state(state)
# Returns: {"user_id": "...", "tenant_id": "...", "redirect_path": "..."}

Implementation Checklist

For each OAuth integration, apply these fixes:

✅ High Priority (Prevents Crashes)

  • [ ] **Import check_html_response from core.oauth_utils**
  • [ ] **Add HTML checks before all .json() calls**
  • [ ] **Add HTML checks in callback handler**

✅ Medium Priority (Improves UX)

  • [ ] **Add automatic historical sync trigger**

# After saving connection successfully:

job_id = await trigger_historical_sync_on_connection(

integration_id="YOUR_INTEGRATION_ID",

user_id=user_id,

tenant_id=tenant_id,

db=db,

sync_months=3

)

```

  • [ ] **Use consistent error formatting**

error = format_oauth_error_message(

provider="YourProvider",

error=error_code,

error_description=error_desc

)

```

  • [ ] **Use state parameter utilities**

# In authorize endpoint:

state = build_oauth_state(user_id, tenant_id, redirect_path)

# In callback endpoint:

state_data = parse_oauth_state(state)

```

Integrations That Need Updates

Critical (HTML Response Crashes)

  1. **Zoom** - integrations/auth_handler_zoom.py
  • exchange_code_for_token() - Line 104
  • refresh_access_token() - Line 148
  1. **Slack** - integrations/slack_enhanced_service.py
  • exchange_code_for_tokens() - Check all JSON parsing
  1. **Google Chat** - integrations/google_chat_enhanced_service.py
  • exchange_code_for_tokens() - Check all JSON parsing
  1. **Discord** - integrations/discord_enhanced_service.py
  • exchange_code_for_tokens() - Check all JSON parsing
  1. **Teams** - integrations/teams_enhanced_service.py
  • exchange_code_for_tokens() - Check all JSON parsing

High Priority (Missing Historical Sync)

  1. **Slack** - Add auto-sync after OAuth callback
  2. **Zoom** - Add auto-sync after OAuth callback
  3. **Discord** - Add auto-sync after OAuth callback
  4. **Google Calendar** - Add auto-sync after OAuth callback
  5. **Notion** - Add auto-sync after OAuth callback
  6. **Asana** - Add auto-sync after OAuth callback
  7. **Trello** - Add auto-sync after OAuth callback
  8. **Monday.com** - Add auto-sync after OAuth callback
  9. **Dropbox** - Add auto-sync after OAuth callback
  10. **HubSpot** - Add auto-sync after OAuth callback

Testing Checklist

After applying fixes to an integration:

  • [ ] Test OAuth flow with valid credentials
  • [ ] Test OAuth flow with invalid credentials (should show error, not crash)
  • [ ] Test OAuth flow when provider returns HTML error page
  • [ ] Verify historical sync starts automatically after connection
  • [ ] Check error messages are user-friendly
  • [ ] Verify state parameter is correctly passed through

Example: Updating Zoom Integration

Before (Crashes on HTML errors):

async def exchange_code_for_token(self, code: str):
    async with aiohttp.ClientSession() as session, session.post(
        self.token_url, headers=headers, data=data
    ) as response:
        if response.status != 200:
            error_text = await response.text()
            raise HTTPException(status_code=400, detail=error_text)

        token_data = await response.json()  # CRASHES HERE IF HTML
        return token_data

After (Safe with HTML errors):

from core.oauth_utils import check_html_response

async def exchange_code_for_token(self, code: str):
    async with aiohttp.ClientSession() as session, session.post(
        self.token_url, headers=headers, data=data
    ) as response:
        if response.status != 200:
            error_text = await response.text()
            raise HTTPException(status_code=400, detail=error_text)

        # Check for HTML error page
        is_html, html_error = check_html_response(response)
        if is_html:
            raise HTTPException(
                status_code=400,
                detail=html_error or "OAuth provider returned error page"
            )

        token_data = await response.json()
        return token_data

Summary

The WhatsApp integration now serves as the template for all OAuth integrations:

✅ **Fixed:** HTML response crashes

✅ **Added:** Automatic 3-month historical sync

✅ **Improved:** Error messages and state management

✅ **Created:** Shared utilities in core/oauth_utils.py

Apply these same fixes to all other OAuth integrations to:

  • Prevent crashes from HTML error pages
  • Automatically backfill historical data
  • Provide consistent user experience
  • Reduce code duplication
  • backend-saas/core/oauth_utils.py - Shared OAuth utilities
  • backend-saas/api/routes/integrations/whatsapp_oauth_routes.py - Reference implementation
  • backend-saas/core/historical_sync_service.py - Historical sync service
  • src/app/integrations/page.tsx - UI for sync progress and history