Как я могу использовать Django OAuth Toolkit с Python Social Auth?
Я строю API, используя Django Rest Framework. Позже этот API должен использоваться устройствами iOS и Android. Я хочу, чтобы мои пользователи регистрировались с oauth2-провайдерами, такими как Facebook и Google. В этом случае им вообще не нужно создавать учетную запись на моей платформе. Но пользователи также должны иметь возможность зарегистрироваться, если у них нет учетной записи Facebook/Google, для которой я использую django-oauth-toolkit, поэтому у меня есть собственный oauth2-провайдер.
Для внешних провайдеров я использую python-social-auth, который отлично работает и автоматически создает пользовательские объекты.
Я хочу, чтобы клиенты проходили аутентификацию с использованием токенов на предъявителя, что прекрасно работает для пользователей, которые зарегистрировались у моего провайдера (django-oauth-toolkit предоставляет схему аутентификации и классы разрешений для Django REST Framework).
Однако python-social-auth реализует только аутентификацию на основе сеансов, поэтому не существует простого способа сделать аутентифицированные запросы API от имени пользователей, зарегистрированных внешним поставщиком oauth2.
Если я использую access_token, который был сгенерирован django-oauth-toolkit, выполнение запроса будет следующим:
curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/
Однако следующее не работает, так как для Django REST Framework не существует соответствующей схемы аутентификации, а AUTHENTICATION_BACKENDS, предоставляемый python-social-auth, работает только для аутентификации на основе сеансов:
curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/
Использование просматриваемого API, предоставляемого Django REST Framework после аутентификации с помощью python-social-auth, работает нормально, только вызовы API без cookie-файла сеанса не работают.
Мне интересно, как лучше всего подойти к этой проблеме. На мой взгляд, у меня есть два основных варианта:
A: Когда пользователь регистрируется у внешнего поставщика oauth2 (обрабатывается python-social-auth), подключитесь к процессу, чтобы создать oauth2_provider.models.AccessToken, и продолжайте использовать 'oauth2_provider.ext.rest_framework.OAuth2Authentication'
, теперь аутентификация также пользователей, которые зарегистрированы у внешнего провайдера. Этот подход предлагается здесь: https://groups.google.com/d/msg/django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ
B: Используйте python-social-auth для аутентификации запроса API. Я мог получить своих собственных пользователей в python-social-auth, написав собственный бэкэнд и используя register_by_access_token. Однако, поскольку вызовы API не могут использовать сеансы Django, это будет означать, что мне придется написать схему аутентификации для Django Rest Framework, которая использует данные, хранящиеся в python-social-auth. Некоторые указания о том, как это сделать, можно найти здесь:
http://psa.matiasaguirre.net/docs/use_cases.html
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-django.html
Однако, насколько я понимаю, python-social-auth проверяет токен только при входе в систему и впоследствии использует сеанс Django. Это означало бы, что мне нужно будет найти способ, чтобы python-social-auth не выполнял весь поток oauth2 для каждого запроса API без сохранения состояния, а скорее проверял бы данные, хранящиеся в БД, которые на самом деле не оптимизированы для запросов, поскольку хранится в формате JSON (хотя я мог бы использовать UserSocialAuth.objects.get(extra_data__contains=), хотя).
Я также должен был бы позаботиться о проверке областей действия токена доступа и использовать их для проверки разрешений, что-то, что django-oauth-toolkit уже делает (TokenHasScope
, required_scopes
так далее).
В данный момент я склоняюсь к использованию опции A, поскольку django-oauth-toolkit обеспечивает хорошую интеграцию с Django Rest Framework, и я получаю все необходимое из коробки. Единственный недостаток заключается в том, что я должен "внедрить" access_tokens, полученный python-social-auth, в модель AccessToken django-oauth-toolkit, которая как-то не так, но, вероятно, будет самым простым подходом.
У кого-нибудь есть какие-либо возражения по этому поводу или, возможно, эта проблема решается по-другому? Я упускаю что-то очевидное и усложняю жизнь? Если кто-нибудь уже интегрировал django-oauth-toolkit с python-social-auth и внешними oauth2-провайдерами, я был бы очень благодарен за некоторые советы или мнения.
3 ответа
Большая сложность в реализации OAuth сводится к пониманию того, как должен работать поток авторизации. Это происходит главным образом потому, что это "отправная точка" для входа в систему, и при работе со сторонним бэкэндом (использующим что-то вроде Python Social Auth) вы фактически делаете это дважды: один раз для своего API и один раз для стороннего. API.
Авторизация запросов с использованием вашего API и стороннего бэкэнда
Процесс аутентификации, который вам нужно пройти:
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
Я использую "Facebook" в качестве стороннего бэкэнда здесь, но процесс одинаков для любого бэкенда.
С точки зрения вашего мобильного приложения вы перенаправляете только на /authorize
URL предоставлен Django OAuth Toolkit. Оттуда мобильное приложение ждет, пока не будет достигнут URL обратного вызова, как в стандартном процессе авторизации OAuth. Почти все остальное (вход в Django, вход в социальную сеть и т. Д.) Обрабатывается Django OAuth Toolkit или Python Social Auth в фоновом режиме.
Это также будет совместимо практически со всеми библиотеками OAuth, которые вы используете, и процесс авторизации будет работать одинаково, независимо от того, какой сторонний бэкэнд используется. Он даже будет обрабатывать (общий) случай, когда вам нужно будет поддерживать бэкэнд аутентификации Django (электронная почта / имя пользователя и пароль), а также сторонний логин.
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
Здесь также важно отметить, что мобильное приложение (которым может быть любой клиент OAuth) никогда не получает токены OAuth от Facebook / сторонних производителей. Это невероятно важно, так как он гарантирует, что ваш API действует как посредник между клиентом OAuth и социальными учетными записями вашего пользователя.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name
В противном случае клиент OAuth сможет обойти ваш API и отправлять запросы от вашего имени сторонним API.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data
Вы заметите, что в этот момент вы потеряли бы контроль над сторонними токенами. Это особенно опасно, потому что большинство токенов могут получить доступ к широкому диапазону данных, что открывает путь к злоупотреблениям и в конечном итоге исчезает под вашим именем. Скорее всего, те, кто заходил на ваш API/ веб-сайт, не собирались делиться своей социальной информацией с клиентом OAuth и вместо этого ожидали, что вы сохраните эту информацию в тайне (насколько это возможно), но вместо этого вы предоставляете эту информацию всем.
Аутентификация запросов к вашему API
Когда мобильное приложение затем использует ваш токен OAuth для отправки запросов к вашему API, вся аутентификация происходит через Django OAuth Toolkit (или ваш поставщик OAuth) в фоновом режиме. Все, что вы видите, это то, что есть User
связан с вашим запросом.
Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back
Это важно, потому что после этапа авторизации это не должно иметь значения, если пользователь выходит из системы аутентификации Facebook или Django. Вашему API просто нужен User
работать, и ваш поставщик OAuth должен иметь возможность обрабатывать аутентификацию и проверку токена.
Это не сильно отличается от того, как Django REST Framework аутентифицирует пользователя при использовании аутентификации на основе сеанса.
Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data
Опять же, все это обрабатывается Django OAuth Toolkit и не требует дополнительной работы для реализации.
Работа с родным SDK
В большинстве случаев вы будете проходить аутентификацию пользователя через свой собственный веб-сайт и использовать Python Social Auth для обработки всего. Но примечательным исключением является использование собственного SDK, поскольку аутентификация и авторизация обрабатываются через собственную систему, что означает, что вы полностью обходите свой API. Это отлично подходит для приложений, которым необходимо войти в систему с третьей стороной, или приложений, которые вообще не используют ваш API, но это кошмар, когда оба они собираются вместе.
Это связано с тем, что ваш сервер не может проверить логин и вынужден предположить, что логин действителен и подлинен, что означает, что он обходит любую и всю безопасность, которую предоставляет вам Python Social Auth.
Mobile App -> Facebook SDK : Opens the authorization prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user
Вы заметите, что это пропускает ваш API во время фазы аутентификации, а затем вынуждает ваш API делать предположения о передаваемом токене. Но есть определенно случаи, когда этот риск может стоить того, так что вы должны оценить его до того, как выбрасывая это. Это компромисс между быстрым и собственным логином для вашего пользователя и потенциальной обработкой плохих или вредоносных токенов.
Я решил это, используя ваш вариант А.
Что я делаю, так это регистрирую пользователей, которые используют третье лицо, чтобы зарегистрироваться, используя свой сторонний токен доступа.
url(r'^register-by-token/(?P<backend>[^/]+)/$',
views.register_by_access_token),
Таким образом, я могу выдать запрос GET, как этот:
GET http://localhost:8000/register-by-token/facebook/?access_token=123456
А также register_by_access_token
вызывается. request.backend.do_auth
запросит информацию о пользователе у токена и волшебным образом зарегистрирует учетную запись пользователя с информацией или авторизирует пользователя, если он уже зарегистрирован.
Затем я вручную создаю токен и возвращаю его как JSON, чтобы позволить клиенту запрашивать мой API.
from oauthlib.common import generate_token
...
@psa('social:complete')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter, if it's needed,
# request.backend and request.strategy will be loaded with the current
# backend and strategy.
third_party_token = request.GET.get('access_token')
user = request.backend.do_auth(third_party_token)
if user:
login(request, user)
# We get our app!
app = Application.objects.get(name="myapp")
# We delete the old token
try:
old = AccessToken.objects.get(user=user, application=app)
except:
pass
else:
old.delete()
# We create a new one
my_token = generate_token()
# We create the access token
# (we could create a refresh token too the same way)
AccessToken.objects.create(user=user,
application=app,
expires=now() + timedelta(days=365),
token=my_token)
return "OK" # you can return your token as JSON here
else:
return "ERROR"
Я просто не уверен в том, как генерировать токен, это хорошая практика? Ну, а пока что работает!!
Может быть, https://pypi.python.org/pypi/django-rest-framework-social-oauth2 - это то, что вы ищете. Этот пакет зависит от python-social-auth
а также django-oauth-toolkit
, который вы уже используете. Я быстро просмотрел документацию, и она, кажется, реализует то, что вы пытаетесь сделать.
Я делал React Native с экспозицией, а Django с Django REST framework. Пост этого блога закончился тем, как я решил регистрацию (регистрацию) в Facebook https://medium.com/@gabriel_gamil/react-native-expo-django-facebook-authentication-sign-in-83625c49da7
tldr; используйте django-rest-auth https://django-rest-auth.readthedocs.io/en/latest/index.html
используйте Джанго-Аллаута https://django-allauth.readthedocs.io/en/latest/