42

Я создаю API, используя Django Rest Framework. Позже этот API будет потребляться устройствами iOS и Android. Я хочу разрешить моим пользователям регистрироваться с oauth2-провайдерами, такими как Facebook и Google. В этом случае им не нужно создавать учетную запись на моей платформе вообще. Но пользователи также должны иметь возможность регистрироваться, если у вас нет учетной записи Facebook/Google, для которой я использую django-oauth-toolkit, поэтому у меня есть собственный поставщик oauth2.Как использовать Django OAuth Toolkit с Python Social Auth?

Для внешних поставщиков я использую python-social-auth, который отлично работает и автоматически создает пользовательские объекты.

Я хочу, чтобы клиенты выполняли аутентификацию с помощью токенов-носителей, что отлично подходит для пользователей, подписавшихся на мой провайдер (django-oauth-toolkit предоставляет схему аутентификации и классы разрешений для Django REST Framework).
Однако python-social-auth реализует аутентификацию на основе сеанса, поэтому нет простого способа сделать аутентифицированные запросы API от имени пользователей, зарегистрированных внешним поставщиком oauth2.

Если я использую access_token, который был сгенерирован Джанго-OAuth-инструментарием, делая запрос, как это работает:

curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/ 

Однако следующий не работает, так как нет никакой соответствующей схемы аутентификации для Django REST Framework и AUTHENTICATION_BACKENDS предоставляемого питонов-социально-Идент работой только для сеанса аутентификации на основе:

curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/ 

Использования листаемого API предоставляемого Django REST Framework после аутентификации с питона-социальной-AUTH работают только е ine, только вызовы 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#signup-by-oauth-access-token
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 от выполнения всего 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, в модель Django-oauth-инструментария AccessToken, которая как-то кажется неправильной, но, вероятно, будет самым простым способом.

Есть ли у кого-нибудь какие-либо возражения против этого или, возможно, решить эту проблему по-другому? Упускаю ли я что-то очевидное и делаю свою жизнь труднее, чем необходимо? Если кто-то уже интегрировал django-oauth-toolkit с python-social-auth и внешними поставщиками oauth2, я был бы очень благодарен за некоторые указания или мнения.

ответ

79

Многие трудности с внедрением OAuth сводятся к пониманию того, как должен работать поток полномочий. Это в основном потому, что это «начальная точка» для входа в систему, а при работе с сторонним бэкэндом (с использованием чего-то вроде Python Social Auth) вы на самом деле делают это дважды: один раз для вашего API и один раз для третьего API-партнеры.

Выдавшая запросы, используя свой API и третьих сторон бэкенд

Процесс аутентификации, что вам нужно будет пройти через это:

Sequence diagram for option A

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 (адрес электронной почты/имя пользователя и пароль), а также сторонний логин.

Option A without a third-party backend

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 и социальными учетными записями пользователей.

Sequence diagram with your API as the gatekeeper

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.

Sequence diagram for bypassing your 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, связанный с вашим запросом.

How OAuth tokens are validated

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 или Джанго системы аутентификации. Для работы с API API требуется User, и ваш провайдер OAuth должен иметь возможность обрабатывать аутентификацию и проверку токена.

Это не сильно отличается от того, как среда Django REST аутентифицирует пользователя при использовании аутентификации с поддержкой сеанса.

Sequence diagram for authenticating using sessions

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 дает вам.

Using a native SDK can cause issues

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, чтобы сделать предположение о маркере, который передается. Но есть определенно случаи, когда этот риск может стоить, поэтому вы должны оценить это, прежде чем выбросить его. Это компромисс между быстрыми и родными входами для вашего пользователя и , которые могут обрабатывать плохие или вредоносные токены.

+0

Привет, Кевин, большое спасибо за ваш ответ. Я, вероятно, не дал достаточно ясного представления, но я хочу, чтобы мои пользователи могли регистрироваться с помощью таких поставщиков, как Facebook или Google, как это делает stackoverflow. Я не хочу получать какую-либо информацию от этих поставщиков, только разрешать пользователям входить в систему с ними. Если они регистрируются, например, Google, им не нужно вообще регистрироваться у моего провайдера. Ваше предложение использовать django-oauth-toolkit как прокси-сервер имеет большой смысл, мне нужно продумать его и вернемся к вам позже. – jeverling

+0

Существует только один API, и django-oauth-toolkit должен быть только одним из множества вариантов для регистрации, да. Я думал только о предоставлении внешних поставщиков для регистрации, но хотел бы, чтобы пользователи, у которых нет учетной записи с Google или Facebook, также не хотят связывать ее с моей платформой, чтобы зарегистрироваться. – jeverling

+0

Это должно работать, но в этом случае у меня все еще есть проблема, как выполнять вызовы API, например. приложение iOS, не используя что-то вроде cookie-jar. С django-oauth-toolkit я могу просто использовать заголовок авторизации с токеном-носителем, который теперь не представляется возможным с помощью 'python-social-auth'. – jeverling

8

Я решил это, используя ваш вариант A.

Что я делаю, это регистрация пользователей, которые используют третью сторону для регистрации по их токену доступа к третьей стороне.

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" 

Я просто не уверен в том, как я генерирую токен, это хорошая практика? Ну, в то же время, это работает!

+0

Где, или лучше: как вы получаете access_token? Кто его отправляет? Я использую спутник, и я так много борюсь. – Igor

+0

Вы получаете токен доступа с вашего интерфейса. для приложения iOS и facebook, это будет Facebook iOS SDK –

+0

Большое спасибо. – Igor

4

Возможно, django-rest-framework-social-oauth2 - это то, что вы ищете. Этот пакет зависит от python-social-auth и django-oauth-toolkit, которые вы уже используете. Я быстро просмотрел документацию, и, похоже, она реализует только то, что вы пытаетесь сделать.

+1

Это лучший вариант. Протестировал его, и он отлично работает! –

Смежные вопросы