Files
WYGIWYH/app/apps/users/adapters.py

76 lines
2.8 KiB
Python

import logging
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from django.contrib.auth import get_user_model
User = get_user_model()
logger = logging.getLogger(__name__)
class AutoConnectSocialAccountAdapter(DefaultSocialAccountAdapter):
"""
Custom adapter to automatically connect social accounts to existing users
with the same email address.
SECURITY WARNING:
This adapter automatically connects OIDC accounts to existing local accounts
based on email matching.
If your OIDC provider allows unverified emails, this could lead to
ACCOUNT TAKEOVER attacks where an attacker creates an OIDC account
with someone else's email and gains access to their account.
"""
def pre_social_login(self, request, sociallogin):
"""
Invoked just after a user successfully authenticates via a
social provider, but before the login is actually processed.
If a user with the same email already exists, connect the social
account to that existing user instead of creating a new account.
"""
# If the social account is already connected to a user, do nothing
if sociallogin.is_existing:
return
# Check if we have an email from the social provider
if not sociallogin.email_addresses:
logger.warning(
"OIDC login attempted without email address. "
f"Provider: {sociallogin.account.provider}"
)
return
# Get the email from the social login
email = sociallogin.email_addresses[0].email.lower()
# Try to find an existing user with this email
try:
user = User.objects.get(email__iexact=email)
# Log this connection for security audit trail
logger.info(
f"Auto-connecting OIDC account to existing user. "
f"Email: {email}, Provider: {sociallogin.account.provider}, "
f"User ID: {user.id}"
)
# Connect the social account to the existing user
sociallogin.connect(request, user)
except User.DoesNotExist:
# No user with this email exists, proceed with normal signup flow
logger.debug(
f"No existing user found for email {email}. "
"Proceeding with new account creation."
)
pass
except User.MultipleObjectsReturned:
# Multiple users with the same email (shouldn't happen with unique constraint)
logger.error(
f"Multiple users found with email {email}. "
"This should not happen with unique constraint. "
"Blocking auto-connect."
)
# Let the default behavior handle this
pass