iMerica / dj-rest-auth

Authentication for Django Rest Framework
https://dj-rest-auth.readthedocs.io/en/latest/index.html
MIT License
1.66k stars 308 forks source link

OAuth2Provider.get_scope() takes 1 positional argument but 2 were given #639

Open shennnj opened 4 months ago

shennnj commented 4 months ago

Getting this error when sending a post request to SocialLoginView. The body of post request contains "code" only. Having this problem on google/facebook/github login.

Similar problem also asked in https://stackoverflow.com/questions/78477908/dj-rest-auth-with-google-login-typeerror-oauth2provider-get-scope-takes-1-po

Did I do any mistake in setting this up?

The error: Happens during validation in SocialLoginSerializer

  File "/home/shen/.local/lib/python3.12/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/shen/.local/lib/python3.12/site-packages/dj_rest_auth/views.py", line 125, in post
    self.serializer.is_valid(raise_exception=True) <br>
  File "/home/shen/.local/lib/python3.12/site-packages/rest_framework/serializers.py", line 223, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
  File "/home/shen/.local/lib/python3.12/site-packages/rest_framework/serializers.py", line 445, in run_validation
    value = self.validate(value)
            ^^^^^^^^^^^^^^^^^^^^ 
  File "/home/shen/.local/lib/python3.12/site-packages/dj_rest_auth/registration/serializers.py", line 122, in validate
    scope = provider.get_scope(request)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^

view.py

from dj_rest_auth.registration.views import SocialLoginView
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client

class GoogleLogin(SocialLoginView):
    adapter_class = GoogleOAuth2Adapter
    client_class = OAuth2Client

    @property
    def callback_url(self):
        return f"http://localhost:3000/"

url.py

from django.urls import path, include
from . import views
from .views import GoogleLogin, google_callback
from allauth.socialaccount.providers.google import views as google_views

urlpatterns = [
    path("auth/", include("dj_rest_auth.urls")),
    path("auth/google/url/", google_views.oauth2_login, name="google_auth"), # redirect to google authentication page
    path("auth/google/callback/", google_callback, name="google_callback"), # callback from authentication page, record the authorization code
    path("auth/google/", GoogleLogin.as_view(), name="google_login"), # use authorization code to login
]

views.py

from dj_rest_auth.registration.views import SocialLoginView
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client

class GoogleLogin(SocialLoginView):
    adapter_class = GoogleOAuth2Adapter
    client_class = OAuth2Client

    @property
    def callback_url(self):
        return f"http://localhost:3000/"

from django.shortcuts import redirect
def google_callback(request):
    return redirect(f"http://localhost:3000/")

Post request Post to /auth/google/ with body of { "code": "<code_received_after_user_authorize>" }

shennnj commented 4 months ago

Seems like django-allauth update to 0.62.0 changes how get_scope is implemented, will downgrade django-allauth to 0.61.1 at the moment to have dj-rest-auth work together.

https://github.com/pennersr/django-allauth/blob/0.61.1/allauth/socialaccount/providers/oauth2/provider.py

    def get_scope(self, request):
        settings = self.get_settings()
        scope = list(settings.get("SCOPE", self.get_default_scope()))
        dynamic_scope = request.GET.get("scope", None)
        if dynamic_scope:
            scope.extend(dynamic_scope.split(","))
        return scope

https://github.com/pennersr/django-allauth/blob/0.62.0/allauth/socialaccount/providers/oauth2/provider.py

    def get_scope(self):
        """
        Returns the scope to use, taking settings `SCOPE` into consideration.
        """
        settings = self.get_settings()
        scope = list(settings.get("SCOPE", self.get_default_scope()))
        return scope

    def get_scope_from_request(self, request):
        """
        Returns the scope to use for the given request.
        """
        scope = self.get_scope()
        dynamic_scope = request.GET.get("scope", None)
        if dynamic_scope:
            scope.extend(dynamic_scope.split(","))
        return scope
Te0SX commented 3 months ago

My setup with django-allauth==0.61.1 and dj-rest-auth==5.0.2 was working fine for like months. Before a few days ago I started getting errors like the above and "allauth.socialaccount.providers.oauth2.client.OAuth2Error: Invalid id_token".

Check this reply by one of the main dev of allauth: https://github.com/iMerica/dj-rest-auth/issues/503#issuecomment-1932655997

Going back to django-allauth==0.57.1 solved my issues.

ThukuWakogi commented 2 months ago

I came across this issue while using Github as a provider and @shennnj's solution worked for me.

However, while using dj-rest-auth 6.0.0 and django-allauth 0.63.2, I noticed that the client class in dj-rest-auth is being instantiated with an extra scope argument.

in the validate method of SocialLoginSerializer, the client is instantiated with an extra scope argument.

https://github.com/iMerica/dj-rest-auth/blob/429a27032922a6077c3e8209ca4f1caa929ecf80/dj_rest_auth/registration/serializers.py#L121-L134

This is not needed in the instantiation of a new client class

https://github.com/pennersr/django-allauth/blob/40117a711746be888528af69029cc5ed2692a7b2/allauth/socialaccount/providers/oauth2/client.py#L13-L38

So to address this problem, I inherited SocialLoginSerializer and removed the scope argument

class CstmSocialLoginSerializer(SocialLoginSerializer):
    def validate(self, attrs):
            ...
            client = self.client_class(
                request,
                app.client_id,
                app.secret,
                adapter.access_token_method,
                adapter.access_token_url,
                self.callback_url,
                scope_delimiter=adapter.scope_delimiter,
                headers=adapter.headers,
                basic_auth=adapter.basic_auth,
            )
            ...

Then added the serializer to my GithubLoginView

class GitHubLogin(SocialLoginView):
    adapter_class = GitHubOAuth2Adapter
    callback_url = "..."
    client_class = OAuth2Client
    serializer_class = CstmSocialLoginSerializer

This solved my problem and I didn't get the error.

YDA93 commented 2 months ago

We are encountering this issue with Apple login despite it previously functioning correctly.

YDA93 commented 2 months ago

We are encountering this issue with Apple login despite it previously functioning correctly.

Downgrading to django-allauth to 0.61.1 Fixes the issue.

trackers153 commented 1 month ago

@YDA93 - could you share your dj_rest_auth version that works with django-allauth 0.61.1 for social auth via Apple?

YDA93 commented 1 month ago

@trackers153 Sure dj-rest-auth==6.0.0

trackers153 commented 1 month ago

Thanks vm, @YDA93