django-rest-auth: социальный вход в Google
В документации django-rest-auth обсуждается интеграция с Facebook, которая меня не интересует - моя задача - обеспечить социальный вход через Google. Я пробовал это в течение достаточно долгого времени, и мне интересно, есть ли у кого-нибудь еще документация о том, как они это сделали... даже простой набросок был бы полезен. До сих пор я не нашел никаких результатов для этого поиска. Я почти у цели, но не могу заставить его работать с браузерным API Django rest framework (DRF).
Вот что у меня получилось: я начал с демонстрационного проекта, представленного на странице github django-rest-auth, и изменил HTML-страницу шаблона социального входа, чтобы требовать только ввод "кода", а не "кода" И "access_token", Когда я предоставляю действительный код (полученный отдельным запросом к конечной точке аутентификации Google), это работает нормально; API с возможностью просмотра отображает обычную веб-страницу с ключом (токен API моего приложения для пользователя) в ответе. Проверка администратора django, все работает - пользователь вошел в систему, электронная почта аутентифицирована и т. Д. Пока все хорошо.
Проблема в том, что отправная точка для предоставления "кода" - и как я получаю этот код от Google в первую очередь. Когда я ранее (успешно) использовал пакет allauth, я мог просто щелкнуть ссылку, которая "незаметно" выполняла бы весь поток OAuth2 (то есть запрашивать код, использовать этот код для получения токена доступа и использовать токен доступа для получить информацию об учетной записи Google).
Чтобы воссоздать этот непрерывный поток (т. Е. НЕ начинать с кода), я решил, что могу прервать поток OAuth2 и "перехватить" код, возвращенный из Google, а затем отправить этот код в API-интерфейс для входа в социальную сеть rest-auth. Для этого я создал обычай 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
Обычно этот метод вызывается, когда Google отправляет GET-запрос на обратный вызов, который вы изначально предоставляете конечной точке Google Auth. С этим переопределением я могу успешно проанализировать код, возвращенный из Google в этом обратном вызове. Запрос POST работает и имеет ключ пользователя в поле resp._content. Тем не менее, в конечном итоге он не может создать намеченное представление в API с возможностью просмотра DRF.
Основываясь на погружении в стек вызовов, я обнаружил, что 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
), но тогда этот хак становится еще более клуджем.
Спасибо за любую помощь, вы можете предоставить!
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})