Skip to content

Production OAuth Error Fix

Problem

Sentry Error: MultipleObjectsReturned in /api/auth/google/ endpoint

Root Cause: Database has duplicate SocialApp entries for Google OAuth provider. Django-allauth expects exactly one SocialApp per provider.

# Error in allauth/socialaccount/adapter.py:299
apps = [<SocialApp: Google>, <SocialApp: >]  # 2 Google apps found!
raise MultipleObjectsReturned  # Can't decide which to use

Solution: Management Command

Created automatic cleanup command: users/management/commands/fix_duplicate_social_apps.py

Features

  • Detects duplicates for all OAuth providers (Google, Facebook, Microsoft)
  • Keeps valid apps with credentials, deletes invalid/duplicate ones
  • Supports --dry-run mode for safe preview
  • Supports --provider flag for specific providers
  • Handles 3 scenarios intelligently:
  • One valid app: Keeps valid, deletes invalid duplicates
  • Multiple valid apps: Keeps first valid, deletes rest
  • No valid apps: Keeps first for admin to configure, deletes rest

How to Fix Production

Step 1: Preview changes (dry-run)

# SSH into production server
ssh [email protected]

# Navigate to project directory
cd /path/to/av

# Preview what would be deleted
python manage.py fix_duplicate_social_apps --dry-run

Example output:

Checking GOOGLE apps...
  ⚠ Found 2 google apps (should be 1)
    1. ID=1, Name='Google', Client ID=Yes, Secret=Yes - ✓ Valid
    2. ID=2, Name='', Client ID=No, Secret=No - ✗ Invalid/Empty

  Strategy: Keep valid app (ID=1), delete 1 invalid
    [DRY RUN] Would delete: ID=2, Name=''

============================================================
[DRY RUN] Would delete 1 duplicate apps
Run without --dry-run to actually delete

Step 2: Execute actual fix

# Run without --dry-run to apply changes
python manage.py fix_duplicate_social_apps

Expected output:

Checking GOOGLE apps...
  ⚠ Found 2 google apps (should be 1)
    1. ID=1, Name='Google', Client ID=Yes, Secret=Yes - ✓ Valid
    2. ID=2, Name='', Client ID=No, Secret=No - ✗ Invalid/Empty

  Strategy: Keep valid app (ID=1), delete 1 invalid
    Deleting: ID=2, Name=''

============================================================
✓ Deleted 1 duplicate apps

Step 3: Verify fix

# Check that only 1 Google app remains
python manage.py shell
>>> from allauth.socialaccount.models import SocialApp
>>> SocialApp.objects.filter(provider='google').count()
1  # Should be exactly 1
>>> exit()

# Test OAuth login
curl -X POST https://api.avai.maakeetoo.com/api/auth/google/ \
  -H "Content-Type: application/json" \
  -d '{"access_token": "valid-google-token"}'


Option 2: Django Admin UI

  1. Navigate to: https://api.avai.maakeetoo.com/admin/socialaccount/socialapp/
  2. Login with superuser credentials
  3. Look for duplicate Google entries (you'll see 2+ rows)
  4. Identify which one has valid Client ID and Secret
  5. Delete the invalid/duplicate entries (keep the valid one)

Option 3: Django Shell (Manual)

python manage.py shell
from allauth.socialaccount.models import SocialApp

# List all Google apps
google_apps = SocialApp.objects.filter(provider='google')
for app in google_apps:
    print(f"ID={app.id}, Name='{app.name}', ClientID={'Yes' if app.client_id else 'No'}")

# Delete specific invalid app by ID (replace 2 with actual ID)
SocialApp.objects.get(id=2).delete()

# Verify only 1 remains
print(SocialApp.objects.filter(provider='google').count())  # Should be 1

Why Local Test Showed No Duplicates

The management command dry-run in local Docker showed:

Checking GOOGLE apps...
  No google apps found.

Explanation: - Local environment: Uses SOCIALACCOUNT_PROVIDERS settings (environment variables), no database SocialApp entries - Production environment: Uses database SocialApp entries, which have duplicates

The error only exists in production database. The command works correctly - it just had nothing to clean in the local environment.


Prevention

To prevent future duplicates:

  1. Never create duplicate SocialApps: Use Django admin carefully
  2. Check before creating:
    SocialApp.objects.filter(provider='google').exists()
    
  3. Use fixtures for deployment: Create SocialApps via data migrations, not manual admin
  4. Consider using settings-based providers: Configure OAuth via SOCIALACCOUNT_PROVIDERS in settings.py instead of database entries

Post-Fix Actions

After fixing the duplicate:

  1. Test OAuth login - Verify users can authenticate with Google
  2. Monitor Sentry - Check that MultipleObjectsReturned error stops occurring
  3. Document credentials - Ensure Google OAuth credentials are documented for disaster recovery
  4. ⚠️ Review other providers - Check if Facebook/Microsoft have duplicates too

  • Management command: users/management/commands/fix_duplicate_social_apps.py
  • OAuth configuration: core/settings.py (lines 342-388)
  • Admin interface: Django admin at /admin/socialaccount/socialapp/
  • Sentry error: Check /api/auth/google/ endpoint errors