私は、Djangoサイトのユーザーに可能な限り最も簡単なログインエクスペリエンスを作成することを目指しています。次のようなものを想像します:
わかりました、この部分は簡単です-- Django-allauth をインストールして設定するだけです。
しかし、ローカルユーザーでサイトを使用するオプションも提供したいと思います。それには別のステップがあります:
はい、デフォルトの認証とallauthの両方で実行できます。しかし、今は百万ドルの問題です。
ログイン方法を変更した場合、どうすれば自動的にGoogle、FB、ローカルアカウントを関連付けることができますか?
彼らがログインするどのような方法でも、私は彼らのメールアドレスを持っていることを確認してください。 Django-allauthを使用してそれを行うことは可能ですか? ユーザー介入 でそれができることを知っています。今日のデフォルトの動作は、メールがすでに登録されているというログインを拒否することです。
構成だけでは不可能な場合は、このワークフローをサポートするためにallauthコードをどのように変更すればよいかについての方向性を示す答えを受け入れます。
これを行う理由はたくさんあります。ユーザーは認証に使用した方法を忘れ、Google、FB、ローカルユーザーアカウントを使用することもあります。すでに多くのローカルユーザーアカウントがあり、ソーシャルアカウントが新しい機能になります。ユーザーに自分のアイデンティティを維持してほしい。私はユーザーの友達リストを尋ねる可能性を想定しているので、もし彼らがグーグルを使ってログインしたなら、私も彼らのFBアカウントを持ちたいと思っています。
これは趣味のサイトであり、大きなセキュリティ要件はないので、これは賢明なセキュリティ実装ではないと答えないでください。
後で、メールをログインIDとして持つ カスタムユーザーモデル を作成します。しかし、私は、必要なユーザー名を持つデフォルトのユーザーモデルのアカウントを自動的に関連付けることができるという回答で満足します。
Django == 1.5.4とDjango-allauth == 0.13.0を使用しています
Socialloginアダプター、具体的には pre_social_login
メソッドをオーバーライドする必要があります。このメソッドは、ソーシャルプロバイダーによる認証の後、このログインがallauthによって処理される前に呼び出されます。
my_adapter.py
では、次のようにします
from Django.contrib.auth.models import User
from allauth.account.models import EmailAccount
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class MyAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
# This isn't tested, but should work
try:
user = User.objects.get(email=sociallogin.email)
sociallogin.connect(request, user)
# Create a response object
raise ImmediateHttpResponse(response)
except User.DoesNotExist:
pass
そして、設定で、ソーシャルアダプターをアダプターに変更します
SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MyAdapter`
この方法で、複数のソーシャルアカウントを1人のユーザーに接続できるはずです。
メモ(2018-10-23):これはもう使用していません。マジックが多すぎます。代わりに、SOCIALACCOUNT_EMAIL_REQUIRED
と'facebook': { 'VERIFIED_EMAIL': False, ... }
を有効にしました。したがって、allauthは、ソーシャルサインアップフォームのソーシャルログインをリダイレクトして、有効なメールアドレスを入力します。すでに登録されている場合は、最初にログインしてからアカウントに接続するとエラーが表示されます。私にとっては十分公正なATMです。
私はこの種のユースケースを改善しようとしていて、次の解決策を考え出しました:
from allauth.account.models import EmailAddress
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class SocialAccountAdapter(DefaultSocialAccountAdapter):
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
(and before the pre_social_login signal is emitted).
We're trying to solve different use cases:
- social account already exists, just go on
- social account has no email or email is unknown, just go on
- social account's email exists, link social account to existing user
"""
# Ignore existing social accounts, just do this stuff for new ones
if sociallogin.is_existing:
return
# some social logins don't have an email address, e.g. facebook accounts
# with mobile numbers only, but allauth takes care of this case so just
# ignore it
if 'email' not in sociallogin.account.extra_data:
return
# check if given email address already exists.
# Note: __iexact is used to ignore cases
try:
email = sociallogin.account.extra_data['email'].lower()
email_address = EmailAddress.objects.get(email__iexact=email)
# if it does not, let allauth take care of this new social account
except EmailAddress.DoesNotExist:
return
# if it does, connect this new social login to the existing user
user = email_address.user
sociallogin.connect(request, user)
私がテストできる限り、それはうまくいくようです。しかし、入力や提案は大歓迎です!
この関連スレッド に関するbabusコメントに従って、提案された回答はこの前に投稿されました( 1 、 2 )は、allauthドキュメントに記載されている大きなセキュリティホールを導入します。
「アカウントが確認されたという事実が電子メールアドレスも確認されていることを意味するかどうかは、Facebookのドキュメントからは明らかではありません。たとえば、確認は電話またはクレジットカードで行うこともできます。一方、デフォルトでは、Facebookからの電子メールアドレスは未確認として扱われます。」
そう言って、あなたのメールIDでFacebookにサインアップするか、Facebookで私のメールをあなたのものに変更し、ウェブサイトにログインしてアカウントにアクセスすることができます。
これを考慮して、@ ssprossの回答に基づいて、私のアプローチは、ユーザーをログインページにリダイレクトし、重複をユーザーに通知し、他のアカウントでログインしてリンクするように招待することですいったんログインすると、元の質問とは異なりますが、そうすることでセキュリティホールは生じません。
したがって、私のアダプターは次のようになります。
from Django.contrib.auth.models import User
from allauth.account.models import EmailAddress
from allauth.exceptions import ImmediateHttpResponse
from Django.shortcuts import redirect
from Django.contrib import messages
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class MyAdapter(DefaultSocialAccountAdapter):
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
(and before the pre_social_login signal is emitted).
We're trying to solve different use cases:
- social account already exists, just go on
- social account has no email or email is unknown, just go on
- social account's email exists, link social account to existing user
"""
# Ignore existing social accounts, just do this stuff for new ones
if sociallogin.is_existing:
return
# some social logins don't have an email address, e.g. facebook accounts
# with mobile numbers only, but allauth takes care of this case so just
# ignore it
if 'email' not in sociallogin.account.extra_data:
return
# check if given email address already exists.
# Note: __iexact is used to ignore cases
try:
email = sociallogin.account.extra_data['email'].lower()
email_address = EmailAddress.objects.get(email__iexact=email)
# if it does not, let allauth take care of this new social account
except EmailAddress.DoesNotExist:
return
# if it does, bounce back to the login page
account = User.objects.get(email=email).socialaccount_set.first()
messages.error(request, "A "+account.provider.capitalize()+" account already exists associated to "+email_address.email+". Log in with that instead, and connect your "+sociallogin.account.provider.capitalize()+" account through your profile page to link them together.")
raise ImmediateHttpResponse(redirect('/accounts/login'))
私はソースコードでこのコメントを見つけました:
if account_settings.UNIQUE_EMAIL:
if email_address_exists(email):
# Oops, another user already has this address. We
# cannot simply connect this social account to the
# existing user. Reason is that the email adress may
# not be verified, meaning, the user may be a hacker
# that has added your email address to his account in
# the hope that you fall in his trap. We cannot check
# on 'email_address.verified' either, because
# 'email_address' is not guaranteed to be verified.
したがって、設計で行うことは不可能です。
ログイン方法を変更した場合、Google、FB、ローカルアカウントを自動的に関連付けるにはどうすればよいですか?
可能ですが、セキュリティの問題に注意する必要があります。チェックシナリオ:
しかし、あなたはそれを修正することができます。チケットの解決策について説明します https://github.com/pennersr/Django-allauth/issues/1149
幸せなシナリオは: