|
| 1 | +# apps/users/adapters.py |
1 | 2 | from allauth.socialaccount.adapter import DefaultSocialAccountAdapter |
2 | | -from django.shortcuts import redirect |
3 | | -from allauth.account.utils import user_username |
4 | | -from django.utils.text import slugify |
5 | | -from django.contrib.auth import get_user_model |
6 | 3 | from allauth.exceptions import ImmediateHttpResponse |
7 | | -from django.contrib import messages |
8 | 4 | from allauth.socialaccount.models import SocialAccount |
| 5 | +from django.contrib.auth import get_user_model |
| 6 | +from django.shortcuts import redirect |
| 7 | +from django.contrib import messages |
| 8 | +from django.utils.text import slugify |
| 9 | +import uuid |
9 | 10 |
|
10 | 11 | User = get_user_model() |
11 | 12 |
|
12 | 13 | class MySocialAccountAdapter(DefaultSocialAccountAdapter): |
13 | 14 | def pre_social_login(self, request, sociallogin): |
| 15 | + """ |
| 16 | + - 로그인된 상태면 통과 |
| 17 | + - 동일 이메일의 기존 유저가 있는데 현재 provider가 아니면 차단(중복 가입 방지) |
| 18 | + """ |
14 | 19 | if request.user.is_authenticated: |
15 | | - return redirect('/') |
| 20 | + return |
16 | 21 |
|
17 | | - email = sociallogin.account.extra_data.get("email") |
18 | | - provider = sociallogin.account.provider # ex: 'google', 'kakao', 'naver' |
| 22 | + extra = sociallogin.account.extra_data or {} |
| 23 | + email = extra.get("email") or extra.get("kakao_account", {}).get("email") |
| 24 | + provider = sociallogin.account.provider # "kakao" / "google" / "naver" ... |
19 | 25 |
|
20 | | - if email: |
21 | | - try: |
22 | | - existing_user = User.objects.get(email=email) |
| 26 | + if not email: |
| 27 | + # 이메일 없이 들어오면 폼으로 가지 않게 즉시 리다이렉트 |
| 28 | + messages.error(request, "카카오 이메일 제공에 동의해야 로그인할 수 있어요.") |
| 29 | + raise ImmediateHttpResponse(redirect("/accounts/login/")) |
23 | 30 |
|
24 | | - # ✅ 이 이메일의 소셜 계정이 현재 provider인지 확인 |
25 | | - if not SocialAccount.objects.filter(user=existing_user, provider=provider).exists(): |
26 | | - messages.error(request, "이미 해당 이메일로 가입된 계정이 있습니다. 소셜 로그인할 수 없습니다.") |
27 | | - raise ImmediateHttpResponse(redirect("/accounts/login/")) |
| 31 | + try: |
| 32 | + existing = User.objects.get(email__iexact=email) |
| 33 | + # 같은 이메일의 유저가 있는데, 해당 provider로 연결되지 않았다면 막기 |
| 34 | + if not SocialAccount.objects.filter(user=existing, provider=provider).exists(): |
| 35 | + messages.error(request, "이미 해당 이메일로 가입된 계정이 있어 소셜 로그인할 수 없습니다.") |
| 36 | + raise ImmediateHttpResponse(redirect("/accounts/login/")) |
| 37 | + except User.DoesNotExist: |
| 38 | + pass # 없으면 계속 진행 |
28 | 39 |
|
29 | | - except User.DoesNotExist: |
30 | | - pass # 없으면 자동가입 진행 |
| 40 | + def is_auto_signup_allowed(self, request, sociallogin): |
| 41 | + """ |
| 42 | + 이메일이 있을 때만 추가 폼 없이 자동가입 허용. |
| 43 | + (없으면 pre_social_login에서 이미 차단됨) |
| 44 | + """ |
| 45 | + extra = sociallogin.account.extra_data or {} |
| 46 | + email = extra.get("email") or extra.get("kakao_account", {}).get("email") |
| 47 | + return bool(email) |
31 | 48 |
|
32 | | - def save_user(self, request, sociallogin, form=None): |
33 | | - user = super().save_user(request, sociallogin, form) |
| 49 | + def populate_user(self, request, sociallogin, data): |
| 50 | + """ |
| 51 | + 소셜 데이터로 User 필수 필드 채우기 (email/username). |
| 52 | + """ |
| 53 | + user = super().populate_user(request, sociallogin, data) |
| 54 | + |
| 55 | + extra = sociallogin.account.extra_data or {} |
| 56 | + kakao_account = extra.get("kakao_account", {}) |
| 57 | + profile = kakao_account.get("profile", {}) or extra.get("properties", {}) |
| 58 | + |
| 59 | + # 이메일 채우기 (필수) |
| 60 | + email = data.get("email") or kakao_account.get("email") |
| 61 | + if email and not getattr(user, "email", None): |
| 62 | + user.email = email |
34 | 63 |
|
35 | | - nickname = sociallogin.account.extra_data.get("name", "") |
36 | | - if nickname: |
37 | | - user.username = slugify(nickname) |
| 64 | + # username 자동 생성 (없으면 닉네임/이메일 앞부분/랜덤) |
| 65 | + if not getattr(user, "username", None): |
| 66 | + base = ( |
| 67 | + data.get("username") |
| 68 | + or data.get("name") |
| 69 | + or (profile.get("nickname") if isinstance(profile, dict) else None) |
| 70 | + or (email.split("@")[0] if email else "") |
| 71 | + ) |
| 72 | + user.username = slugify(base or f"user-{uuid.uuid4().hex[:10]}") |
| 73 | + return user |
38 | 74 |
|
39 | | - if sociallogin.account: |
| 75 | + def save_user(self, request, sociallogin, form=None): |
| 76 | + """ |
| 77 | + 저장 시 provider / provider_uid도 기록(있을 때만). |
| 78 | + """ |
| 79 | + user = super().save_user(request, sociallogin, form) |
| 80 | + if hasattr(user, "provider"): |
40 | 81 | user.provider = sociallogin.account.provider |
| 82 | + if hasattr(user, "provider_uid"): |
41 | 83 | user.provider_uid = sociallogin.account.uid |
42 | | - |
43 | 84 | user.save() |
44 | 85 | return user |
0 commit comments