Django-rest-auth:使用Google进行社交登录

14
Django-rest-auth文档讨论了Facebook集成,但我对此不感兴趣——我的关注点是提供通过Google进行社交登录。我已经尝试了一段时间,想知道是否有其他人有关于如何做到这一点的文档...即使只是一个大致的草图也会很有帮助。到目前为止,我没有在这个搜索中找到任何结果。我已经接近成功,但无法与Django rest framework(DRF)可浏览API配合使用。
到目前为止,我所做的是: 我从django-rest-auth github页面提供的演示项目开始,并修改了社交登录模板HTML页面,只要求输入“code”,而不是“code”和“access_token”两者都要。当我提供一个有效的代码(通过单独请求谷歌的认证端点获得)时,这个方法可以正常工作;可浏览API呈现出通常的网页,并在响应中返回“key”(我的应用程序对用户的API令牌)。检查Django管理界面,一切都正常——用户已登录,电子邮件已经过身份验证等等。 到目前为止很好。
问题在于提供“code”的起点以及我如何首先从谷歌获取该代码。当我之前(成功地)使用allauth包时,我只需要点击一个链接,就可以“隐形地”执行整个OAuth2流程(即请求代码,使用该代码获取访问令牌,并使用访问令牌获取用户的Google账户信息)。
为了重新创建那个无缝的流程(即不从代码开始),我想我可以打断OAuth2流程并“拦截”从谷歌返回的代码,然后将该代码POST到rest-auth社交登录API。为此,我通过覆盖dispatch方法创建了一个自定义的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
通常情况下,当谷歌向您最初提供给谷歌的验证终点发送GET请求时,将调用此方法。使用此覆盖,我能够成功解析从谷歌返回的代码。POST请求有效,并在resp._content字段中具有用户密钥。但是,它最终无法在DRF浏览API中产生预期的视图。 通过深入调用栈,我发现rest_framework.views.APIView.dispatch返回rest_framework.response.Response类型的对象。 但是,当上面使用requests.post方法完成时,它返回requests.models.Response实例。因此,它没有正确的属性并在django中间件中失败。例如,它没有acceptable_renderer属性和get方法(用于django.middleware.clickjacking.py)。我可以想象将这些要求添加到requests.models.Response(rp)实例中,但那么这个hack将变得更加笨拙。 感谢您能提供的任何帮助!

2
你在这方面有什么进展了吗? - jfunk
很不幸。由于其他事情占用了我的时间,我忘记了这件事。幸运的是,这并不是一个关键问题,但我相信其他人可能对此感兴趣。 - blawney_dfci
1个回答

2

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接