首页 文章

django-rest-auth:谷歌社交登录

提问于
浏览
11

django-rest-auth文档讨论了Facebook集成,我对此并不感兴趣 - 我关注的是通过Google提供社交登录 . 我已经尝试了很长一段时间了,我想知道是否有其他人有关于他们如何做到这一点的任何文件...即使只是粗略的草图也会有所帮助 . 到目前为止,我还没有找到任何搜索结果 . 我几乎在那里,但无法使用Django休息框架(DRF)可浏览的API .

以下是我到目前为止的内容:我从django-rest-auth github页面上提供的演示项目开始,修改了社交登录模板HTML页面,只需要'代码'输入,而不是'代码'和'access_token' . 当我提供有效的代码(通过单独的请求获得google的auth endpoints )时,这很好用;可浏览的API在响应中使用“密钥”(我的应用程序的用户API令牌)呈现通常的网页 . 检查django管理员,一切正常 - 用户已登录,电子邮件已通过身份验证等 . 目前为止很好 .

问题在于提供“代码”的起点 - 以及我如何从谷歌首先获得该代码 . 当我以前(成功)使用allauth包时,我只需单击一个链接,该链接将“无形地”执行整个OAuth2流程(即请求代码,使用该代码获取访问令牌,并使用访问令牌获取用户的谷歌帐户信息) .

为了重新创建无缝流(即不从代码开始),我想我可以中断OAuth2流和"intercept"从谷歌返回的代码,然后将该代码POST到rest-auth社交登录API . 为此,我通过覆盖调度方法创建了自定义 allauth.socialaccount.providers.oauth2.views.OAuth2CallbackView

class CustomOAuth2CallbackView(OAuth2CallbackView):
    def dispatch(self, request):
        # gets the code correctly:
        code = request.GET['code']

        # rp is of type requests.methods.Response
        rp = requests.post(<REST-AUTH API ENDPOINT>, data = {'code':code})
        return rp

通常,当谷歌向您最初提供给谷歌的auth endpoints 的回调uri发送GET请求时,会调用此方法 . 通过此覆盖,我能够成功解析在该回调中从谷歌返回的代码 . POST请求有效并且在resp._content字段中具有用户密钥 . 但是,它最终无法在DRF可浏览API中生成预期视图 .

基于在callstack中潜入,我发现 rest_framework.views.APIView.dispatch 返回 rest_framework.response.Response 类型的对象 . 但是,当上面使用的 requests.post 方法完成时,它返回 requests.models.Response 类型的实例 . 因此,它没有正确的属性,并且在django中间件中失败 . 例如,它没有 acceptable_renderer 属性,也没有'get'方法(在 django.middleware.clickjacking.py 中使用) . 可以想象,我可以将这些要求添加到 requests.models.Responserp )实例中,但随后这个黑客变成了一个更糟糕的问题 .

感谢您的任何帮助,您可以提供!

1 回答

  • 1

    https://github.com/st4lk/django-rest-social-auth

    class SocialLoginSignup(SocialSessionAuthView):
    """
        Mobile user social signup and login api view
    
        args:
            provider: name of the social network
            access_token: auth token got from the social sites
    """
    serializer_class = SocialSignUpSerializer
    authentication_classes = (TokenAuthentication,)
    
    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        provider_name = serializer.validated_data['provider']
    
        decorate_request(request, provider_name) # assign the provider class object in request
    
        authed_user = request.user if not request.user.is_anonymous() else None
    
        token = serializer.validated_data['access_token']
    
        if self.oauth_v1() and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in serializer.validated_data:
            request_token = parse_qs(request.backend.set_unauthorized_token())
            return Response(request_token)
    
        try:
            # authentication function get call from assign class object in request
            user = request.backend.do_auth(token, user=authed_user)
        except social_exceptions.AuthException as e:
            raise exceptions.ParseError({'error':str(e)})
        except social_exceptions.AuthTokenError as e:
            raise exceptions.ParseError({'error': str(e)})
        except social_exceptions.AuthAlreadyAssociated as e:
            raise exceptions.ParseError({'error': str(e)})
        except social_exceptions.AuthFailed as e:
            raise exceptions.ParseError({'error':str(e)})
        except social_exceptions.AuthUnknownError as e:
            raise exceptions.ParseError({'error': str(e)})
        except social_exceptions.WrongBackend as e:
            raise exceptions.ParseError({'error':str(e)})
        except Exception as e:
            raise exceptions.ParseError({'error': social_message.INVALID_AUTH_TOKEN})
    
        token, created = Token.objects.get_or_create(user=user)
        return Response({'auth_token':token.key})
    

相关问题