Closed fjcaetano closed 5 months ago
There is no easy setting for this, yet, though it can be realized as follows:
You should let is_open_for_signup
on the account adapter always return False
. The counterpart in the socialaccount adapter should always return True
.
Then, in your templates simply do not include the local signup/login forms.
Furthermore, make sure validate_disconnect
(SocialAccountAdapter) does not allow to disconnect the last social account.
There's a potential security loophole here.
If you setup allauth to use a social auth provider as the signup/signin process.
Then use a custom SocialAccountAdapter to check the domain of email address to remove the need for email confirmation (set to optional) to remove friction from the process. You can do this with the pre_social_login
method of DefaultSocialAccountAdapter
As the DefaultAccountAdapter always returns True
from the is_open_for_signup
method, someone can just hit the /accounts/signup/
page and create an account which then doesn't need to be verified.
Whilst this is obviously down to the developer to configure correctly, it's not so clear that this would be the case.
I think adding a configuration option to turn on/off the standard account signup page would be sensible and maybe even setting everything to off by default might also be wise, then developers can just switch on what they need.
I support this feature request! :-)
I believe direct sign up, should be defined as another optional backends
here is my adapters as @pennersr mentioned in his comment:
class AccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
if request.path.rstrip("/") == reverse("account_signup").rstrip("/"):
return False
return True
class SocialAccountAdapter(DefaultSocialAccountAdapter):
def validate_disconnect(self, account, accounts):
raise ValidationError("Can not disconnect")
but there is another problem here, It's with EmailView
(accounts/email) in this view users can add/remove email addresses to their account - I think if you want completly use providers (disable direct login/singup) email management may cause some problems
also password
mangement doesn't make sense. (set password/change password/reset password)
I too think configuring social login + registration only should be made easier. I'd even prefer that the Account
models/tables were not populated/created at all. In fact, let's look at the setup:
INSTALLED_APPS = [
# ...
'allauth',
'allauth.account',
'allauth.socialaccount',
# ...
]
I always had the impression it is actually by design that there is a separation of account
and socialaccount
. Hence, if I would omit 'allauth.account'
from INSTALLED_APPS
I would get a django-allauth setup without regular, email-based registration + login. Unfortunately, this does not seem to be the case.
@pennersr Is there an important reason why this is not strictly separated that way?
Even if you would go the social-only route, it typically still makes sense to store the user email address for user communication purposes. So, if you would architect things differently you would either need to dupe email storage (& handling) in both apps, or, you would need to introduce a third base app dedicated to this purpose.
This makes sense---in some way.
Though, really, if I want to communicate with a user who has authenticated with my application, say, 6 months ago, and has, potentially, changed their email address twice in that time span ... how much worth is the email address stored in my Django model? - Not much, right? Maybe zero.
In theory, at least, I should be able to query the social provider again and ask for an updated version of the user details when I need them. Everything else to me seems like a second-class solution. I must admit I don't know if it's actually possible to query the provider at any time. I hope it is.
here is my adapters as @pennersr mentioned in his comment:
class AccountAdapter(DefaultAccountAdapter): def is_open_for_signup(self, request): if request.path.rstrip("/") == reverse("account_signup").rstrip("/"): return False return True class SocialAccountAdapter(DefaultSocialAccountAdapter): def validate_disconnect(self, account, accounts): raise ValidationError("Can not disconnect")
but there is another problem here, It's with
EmailView
(accounts/email) in this view users can add/remove email addresses to their account - I think if you want completly use providers (disable direct login/singup) email management may cause some problemsalso
password
mangement doesn't make sense. (set password/change password/reset password)
Your solution was quite good for me.
I decided to override forms to disable password and email managements as mentioned:
class MemberChangePasswordForm(allauthforms.ChangePasswordForm):
def clean(self):
raise forms.ValidationError(_('You cannot change password.'))
class MemberSetPasswordForm(allauthforms.SetPasswordForm):
def clean(self):
raise forms.ValidationError(_('You cannot set password.'))
class MemberResetPasswordForm(allauthforms.ResetPasswordForm):
def clean(self):
raise forms.ValidationError(_('You cannot reset password.'))
class MemberAddEmailForm(allauthforms.AddEmailForm):
def clean(self):
raise forms.ValidationError(_('You cannot add an email.'))
and I set their template page as blank.
I believe that it will be okay if we not provide hyperlinks to password/email management pages. Even if some malicious users access those pages directly, the functions won't work.
Linking the following PR here since it relates:
FYI I also want exactly this: enable social login, disable regular login. I am reading this thread, and upvoting the related PR.
Thanks for your work on this project.
I decided to override forms to disable password and email managements as mentioned:
@pincoin - how did you hook these objects into the django-allauth flow?
What do you all think of overriding the URLs themselves? So for example, adding entries like these to urls.py before including allauth.urls:
from django.urls import path, re_path
from django.views.generic import TemplateView, RedirectView
urlpatterns = [
# These URLs shadow django-allauth URLs to shut them down:
path('password/change/', RedirectView.as_view(url='/')),
path('password/set/', RedirectView.as_view(url='/')),
path('password/reset/', RedirectView.as_view(url='/')),
path('password/reset/done/', RedirectView.as_view(url='/')),
re_path('^password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$',
RedirectView.as_view(url='/')),
path('password/reset/key/done/', RedirectView.as_view(url='/')),
path('email/', RedirectView.as_view(url='/')),
path('confirm-email/', RedirectView.as_view(url='/')),
re_path('^confirm-email/(?P<key>[-:\\w]+)/$',
RedirectView.as_view(url='/')),
]
Maybe combine both signup form templates?
I should be able to query the social provider again and ask for an updated version of the user details when I need them
That can be done with userinfo/ url for each provider (maybe not Facebook).
So at this point, the best solution is to just not include all the allauth URLs and include the social login URLs? for instance for Google and Facebook we do:
So my views.py
would be:
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from rest_auth.registration.views import SocialLoginView
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
and my urls would be:
urls = [
...
path('rest-auth/facebook/', FacebookLogin.as_view(), name='fb_login'),
path('rest-auth/google/', GoogleLogin.as_view(), name='google_login')
...
]
source: https://medium.com/@pratique/social-login-with-react-and-django-i-c380fe8982e2
In my project, I want to only allow google socail login/signup for member system. I don't want the users to access any pages exposed by default allauth setting. This is my example to import partial url.
## setting.py from allauth.account.views import logout from allauth.socialaccount.providers.google.views import oauth2_login, oauth2_callback urlpatterns = [ # path('accounts/', include('allauth.urls')), ## don't include all allauth urls path('accounts/logout/', logout, name="account_logout"), path('accounts/google/login/', oauth2_login, name="google_login"), path('accounts/google/login/callback/', oauth2_callback, name="google_callback"), ... ]
It could also block other urls such as signup, reset_passwrod...
Close as discussion
What do you all think of overriding the URLs themselves? So for example, adding entries like these to urls.py before including allauth.urls:
from django.urls import path, re_path from django.views.generic import TemplateView, RedirectView urlpatterns = [ # These URLs shadow django-allauth URLs to shut them down: path('password/change/', RedirectView.as_view(url='/')), path('password/set/', RedirectView.as_view(url='/')), path('password/reset/', RedirectView.as_view(url='/')), path('password/reset/done/', RedirectView.as_view(url='/')), re_path('^password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$', RedirectView.as_view(url='/')), path('password/reset/key/done/', RedirectView.as_view(url='/')), path('email/', RedirectView.as_view(url='/')), path('confirm-email/', RedirectView.as_view(url='/')), re_path('^confirm-email/(?P<key>[-:\\w]+)/$', RedirectView.as_view(url='/')), ]
@dfrankow , can you elaborate why we need to override the 3 email URLs? Is it because it's more clean / secure / others? What if we don't override them?
@dfrankow , can you elaborate why we need to override the 3 email URLs? Is it because it's more clean / secure / others? What if we don't override them?
It's been awhile, I don't remember the details.
I think I just picked every URL I thought was associated with creating an account. I probably thought the email links were confirmation emails for signing up for an account. Why allow account confirmation emails if you can't sign up for an account?
If you don't override them, I suppose you would leave some email functionality in place that might or might not work because there are no local accounts. Seems like a small hole that could enable some security vulnerabilities if there are any.
This thread is a long meandering discussion going nowhere.
I've been working to implement django-two-factor-auth
alongside allauth
. I am using this package instead of django-allauth--2fa
because we want to offer email and text message-based 2FA, and django-allauth--2fa
only supports authenticator apps.
Everything is working fine except that I need to ensure only the django-two-factor-auth
login url is the one used for signing in when using username/password. It would be great if there was simply a settings
variable such as DISABLE_ALLAUTH_LOGIN_URL (default=False) which, if True, simply prevented account_login
from being included in the allauth
urlconf.
@dfrankow
What do you all think of overriding the URLs themselves? So for example, adding entries like these to urls.py before including allauth.urls:
I think that disabling the URLs that are not needed is indeed the best way forward, but, it is rather messy to do set this up properly. Therefore, I think it indeed makes sense to offer a setting to have this functionality builtin.
What I plan on doing is:
SOCIALACCOUNT_ONLY = True
@DanielSwain What you are describing is actually a little bit different from the scope of this issue. Ideally. the SMS functionality would be integrated into allauth.mfa
. But for now, I would recommend to not use the default allauth.urls
and put together the urls
that you are after.
See #3739
Closing -- #3739 is merged.
Closing -- #3739 is merged.
Nice!
Hey guys, first of all, thank you for this app. It's amazing and it has already saved me a bunch of time.
I have the following problem because I couldn't find anything in the docs, so I thought it would be better to ask.
How can I completely disable form login/signup and enable only the providers I set? In my business model, the user can only login/signup with Facebook and, whenever I use the @login_required decorator, it redirects the user to a login page where he can input emal/password.
Thanks in advance.