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-Typeheader fortext/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_responsefromcore.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)
- **Zoom** -
integrations/auth_handler_zoom.py
exchange_code_for_token()- Line 104refresh_access_token()- Line 148
- **Slack** -
integrations/slack_enhanced_service.py
exchange_code_for_tokens()- Check all JSON parsing
- **Google Chat** -
integrations/google_chat_enhanced_service.py
exchange_code_for_tokens()- Check all JSON parsing
- **Discord** -
integrations/discord_enhanced_service.py
exchange_code_for_tokens()- Check all JSON parsing
- **Teams** -
integrations/teams_enhanced_service.py
exchange_code_for_tokens()- Check all JSON parsing
High Priority (Missing Historical Sync)
- **Slack** - Add auto-sync after OAuth callback
- **Zoom** - Add auto-sync after OAuth callback
- **Discord** - Add auto-sync after OAuth callback
- **Google Calendar** - Add auto-sync after OAuth callback
- **Notion** - Add auto-sync after OAuth callback
- **Asana** - Add auto-sync after OAuth callback
- **Trello** - Add auto-sync after OAuth callback
- **Monday.com** - Add auto-sync after OAuth callback
- **Dropbox** - Add auto-sync after OAuth callback
- **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_dataAfter (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_dataSummary
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
Related Files
backend-saas/core/oauth_utils.py- Shared OAuth utilitiesbackend-saas/api/routes/integrations/whatsapp_oauth_routes.py- Reference implementationbackend-saas/core/historical_sync_service.py- Historical sync servicesrc/app/integrations/page.tsx- UI for sync progress and history