Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to disable form login/signup? #345

Closed
fjcaetano opened this issue Jul 31, 2013 · 25 comments
Closed

How to disable form login/signup? #345

fjcaetano opened this issue Jul 31, 2013 · 25 comments

Comments

@fjcaetano
Copy link

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.

@pennersr
Copy link
Owner

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.

@markunsworth
Copy link

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.

@wetneb
Copy link
Contributor

wetneb commented Aug 27, 2015

I support this feature request! :-)

@aliva
Copy link

aliva commented Mar 11, 2016

I believe direct sign up, should be defined as another optional backends

@aliva
Copy link

aliva commented Mar 12, 2016

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)

@bittner
Copy link
Contributor

bittner commented Jan 3, 2017

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?

@pennersr
Copy link
Owner

pennersr commented Jan 3, 2017

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.

@bittner
Copy link
Contributor

bittner commented Jan 3, 2017

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.

@pincoin
Copy link

pincoin commented Aug 1, 2020

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)

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.

@9mido
Copy link
Contributor

9mido commented Jan 10, 2021

Linking the following PR here since it relates:

#2181

@dfrankow
Copy link

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.

@dfrankow
Copy link

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?

@dfrankow
Copy link

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='/')),
]

@Andrew-Chen-Wang
Copy link
Contributor

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).

@ashmlk
Copy link

ashmlk commented May 8, 2021

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

@rocmewtwo
Copy link

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...

@derek-adair
Copy link

Close as discussion

@shawnngtq
Copy link

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
Copy link

@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.

@derek-adair
Copy link

This thread is a long meandering discussion going nowhere.

  1. @pennersr has clearly laid out a way to do this.
  2. @markunsworth's security loophole is a corner case that can be fixed by simply making your own account adapter that returns false always.
  3. This project was never intended to be used for pure social auth and would be a major undertaking to support such a request for very little gain. If the solutions proposed by @pennersr are not to your liking, there are other social auth applications you can implement that are just as high quality as this one.

@DanielSwain
Copy link
Contributor

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.

@pennersr
Copy link
Owner

@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:

  • Introduce a setting: SOCIALACCOUNT_ONLY = True
  • When enabled, password and email urls are no longer active.
  • As the name implies, this will be social account only, meaning, the newly introduced login-by-code (email), or email verification are also out of scope.

@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.

@pennersr
Copy link
Owner

See #3739

@pennersr
Copy link
Owner

Closing -- #3739 is merged.

@dfrankow
Copy link

Closing -- #3739 is merged.

Nice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests