Авторизация в социальных сетях для Django

22 января 2011

Зачем

Когда в нашем проекте возникла необходимость реализовать авторизацию через социальные сети, я по совету Ромы Ворушина просмотрел несколько готовых решений, чтобы не изобретать уже многократно изобретенный велосипед. Оказалось, что не все велосипеды умеют ездить туда, куда нам нужно, и в точности так, как именно нам нужно:

  1. Кроме крупных мировых сетей хотелось также работать с LiveJournal, Yandex и ВКонтакте.
  2. Нужна была возможность контролировать действия системы после успешной авторизации, то есть управлять процессом обработки полученной от сервиса информации о пользователях.

Я остановился на https://github.com/omab/django-social-auth: просмотрев исходники, я увидел, что их немного, а написано все понятно. Изначально библиотека поддерживала Facebook, Twitter, Google, OpenID и OpenAuth и процесс работы с БД был немного не такой, как хотелось бы. Форка было не избежать, правда, в скором времени мы с автором исходной библиотеки немного подружились, в результате чего большая часть моих изменений ушла в основную ветку, а в моем форке остались только Yandex и ВКонтакте.

После всех изменений результат мне пришелся очень по сердцу: в случае необходимости можно просто добавлять движки для новых сервисов, а работа с БД достаточно проста, чтобы не слишком о ней думать. Также вне зависимости от того, как происходит авторизация (о, иногда это достаточно замысловатый процесс, поверьте мне на слово), работа с данными пользователя унифицирована, что тоже очень удобно.

Как это работает

Для каждого сервиса есть два класса – auth и backend. Напрямую вы с ними работаете, только если вам нужно добавить свой сервис, в противном случае вы просто вызываете метод auth библиотеки и скармливаете ему имя сервиса и нужные параметры. После того как авторизация завершена, вызывается метод complete. Что происходит внутри, описано ниже.

1. Класс auth отвечает за авторизацию на сайте сервиса и убеждается, что все прошло успешно (см. рисунок).

2. Класс backend отвечает за авторизацию собственно в вашем приложении (через стандартный authenticate) и за работу с таблицей пользователей на основании тех данных, которыми сервис соизволил с вами поделиться. Некоторые сервисы (не будем показывать пальцем, хотя это был ЖЖ) не дают никакой информации, кроме «наш человек/не наш человек».

Для backend можно задавать ту модель пользователя, которая используется в вашем приложении, если вы не используете стандартный класс User. В менеджере этой модели можно, например, принимать решение, когда создавать нового пользователя, а когда – использовать уже имеющегося.

Для того чтобы понять, нужно ли обновлять информацию в таблице пользователей, используя данные, полученные из социальной сети (делая это на автомате, вы рискуете привести пользователя в ярость, переписывая данные в его профиле), в системе есть специальный сигнал pre_update. Его вы можете обработать и ответить, нужно ли вносить изменения.

При успешной авторизации в специальной таблице сохраняется информация о том, какой пользователь связан с какими (1:N) сервисами авторизации. Для привязки уже залогиненного пользователя используются методы библиотеки associate.

«Просто, понятно, легко запомнить», как говорил капитан Д. Воробей.

Что брать и на что обратить внимание

Итак, мой форк здесь: https://github.com/krvss/django-social-auth

В существующую архитектуру довольно легко вписался ВКонтакте – его backend сейчас, кроме авторизации, проверяет также «подпись» данных, чтобы чего не вышло.

Когда вы, авторизуясь на Yandex через OpenId, ставите галочку «передать дополнительные параметры», в Django почему-то возникает нарушение целостности или потеря csrf-информации. Чтобы не возникала ошибка, можно указать свою функцию для завершения авторизации (параметр SOCIAL_AUTH_COMPLETE_URL_NAME), которая будет отличаться только наличием декоратора csrf_exempt_view и состоять из вызова social_auth.complete.

Upd: Причины такого поведения Yandex объясняются в комментариях к этой записи.

Ответы на: Авторизация в социальных сетях для Django

  • Петр Елагин пишет:

    23 января, 2011

    А тут реализовать нельзя было ) чтоб посмотреть на результаты (((((

    • krvss пишет:

      24 января, 2011

      Привет, Петр! Да тут негде просто 🙂 Нету Питона, Джанги и всего прочего — хотя пожалуй стоит подумать о том, чтобы поднять.

  • Иван Сагалаев пишет:

    23 января, 2011

    > Когда вы, авторизуясь на Yandex через OpenId, ставите галочку «передать дополнительные параметры», в Django почему-то возникает нарушение целостности или потеря csrf-информации.

    Если дополнительную информацию просить через Attribute Exchange, там получается довольно много данных (из-за кучи алиасов неймспейсов). Если объём данных превышает некий предел, наш сервер вместо GET-запроса с данным в query-строке делает POST-запрос с данными в теле. А джанговская CsrfMiddleware по умолчанию отказывается принимать любые POST-запросы, не подписанные CSRF-токеном. В данном случае csrf_exуmpt — это самое правильное решение, потому что подлинность запроса в любом случае обеспечивается OpenID-протоколом. Так что лучше всего повесить этот декоратор в самой библиотеке на вьюху, принимающую OpenID-запросы.

    • krvss пишет:

      24 января, 2011

      Иван, да, я уже обратил внимание сколько там сыплется информации в AX. Так и сделал в результате — повесил exempt_view на функцию во вью, принимающую запросы. И именно потому что запрос проверяется на уровне протокола не особо запариваюсь.
      Спасибо за комментарий!

      • Василий пишет:

        13 февраля, 2011

        Дмитрий, Иван, а можно об этом поподробнее? что это за проблема и как с ней бороться? Сейчас делаю себе блог на django1.3 и хочу использовать этот форк для авторизации через соцсети, каких ошибок ожидать и как их лечить?

      • Василий пишет:

        13 февраля, 2011

        Станислав, извиняюсь, перепутал твоё имя 🙂

        • krvss пишет:

          14 февраля, 2011

          Василий,

          Проблема в том, что при возвращении с сервера обратно на твой сервис Джанго начнет ругаться на нарушение целостности csrf. Причину Иван указал выше.

          Бороться с этим надо именно так как я написал в последнем абзаце — через настройку в settings, у меня вот так:

          SOCIAL_AUTH_COMPLETE_URL_NAME = ‘social_login_complete’

          и декоратор

          @csrf_view_exempt
          def social_login_complete(request, backend):
          return social_auth_complete(request, backend)

          Реальных проблем с безопасностью это за собой не повлечет, так как внутри протокола OpenID проверка ответа все равно происходит.

          • Захар пишет:

            2 июня, 2011

            Можно обойтись и без отдельной вьюхи, просто добавить в urls.py:

            from django.views.decorators.csrf import csrf_exempt
            from social_auth.views import complete
            ...

            urlpatterns = patterns('',
            ...
            url(r'^social-auth/complete/(?P[^/]+)/$', csrf_exempt(complete), name='complete'),
            url(r'^social-auth/', include('social_auth.urls')),

  • Dmitri пишет:

    23 января, 2011

    Спасибо за форк. Два дня назад смотрел оригинальный репозиторий, ничего подобного еще не было.
    Вы проделали очень полезную работу. Счастья вам!

    • krvss пишет:

      24 января, 2011

      Дмитрий, пожалуйста 🙂 Это я просто привыкаю к опен сорс, раз сам пользуешься, так и делиться стоит иногда.

  • гена пишет:

    23 января, 2011

    а как вы относитесь к таким сервисам, как loginza.ru?

    • krvss пишет:

      24 января, 2011

      Гена, нормально отношусь. Просто мне удобнее и интереснее (да и проще в чем-то) было именно на уровне библиотеки все решить. Ну что с нас, с программеров взять – хлебом не корми, дай покодировать.

    • vixh пишет:

      21 февраля, 2011

      Для логинзы есть https://github.com/vgarvardt/django-loginza

      • Гена пишет:

        15 марта, 2011

        На момента поста комментария не было =)

  • vixh пишет:

    21 февраля, 2011

    Позволяет ваш модуль писать на стену фейсбука(контакта), твитить в твитере пользователя залогинившегося через django-social-auth ?

    • krvss пишет:

      21 февраля, 2011

      django-social-auth — только для авторизации.

  • cwer пишет:

    12 марта, 2011

    А для mail.ru с его oauth нет?

    • krvss пишет:

      15 марта, 2011

      Нет пока, хотя oauth там поддерживается, нужно написать просто дополнительный backend специально для mail.ru. Хорошая тема, возможно сделаю это.

  • Django Social Auth (v0.3.8) не работает авторизация через yandex пишет:

    29 марта, 2011

    […] http://www.ikrvss.ru/2011/01/22/django-social-auth/ вот ведь блог автора и он поясняет ошибку с yandex […]

  • Fedoresko пишет:

    26 мая, 2011

    Очень полезная библиотека. Но есть одно но! Зачем был выбран питон для её написания? Я бы предпочел видеть такую библиотеку на JavaScript. Это позволило бы встраивать авторизацию через сторонние сервисы прямо в динамическую структуру сайта.

    • krvss пишет:

      26 мая, 2011

      Об изначальной причине лучше всего спросить Матиаса, автора основного проекта 🙂 Я же делал от него форк для нужных в нашем проекте сервисов.
      Сам Django Social Auth был выбран потому, что наш проект на Django, и натурально, нужна была интеграция со всей остальной платформой. Я посмотрел аналоги, поговорил с друзьями и выбрал DSA как наиболее подходящий, о чем совершенно не пришлось жалеть — очень удачная архитектура, хорошая поддержка, а сейчас и коммьюнити неплохой образовался.

  • Иван пишет:

    23 ноября, 2011

    пробую дефолтный пример с google openid. Оно хочет /accounts/login (согласно декоратору login_required). Что ему там дать, что бы попасть на done и увидеть что все действительно работает?.

    • krvss пишет:

      23 ноября, 2011

      Не понял, когда именно хочет? OpenId работает весь одинаково — открывается страница на провайдере (в данном случае — Google), ты вводишь там логин-пароль, если еще не авторизовался, а потом тебя перекидывают обратно. Это работает и с localhost в том числе.

      • Иван пишет:

        23 ноября, 2011

        Это я пытался дефолтный пример запустить. Удалось после подключения всяких полей (даже пустых) из local settings. На момент прошлого поста не получалось из-за того, что django-social-auth хочет даже те переменные, которые не объявлены.

        К сожалению документация недостаточно (по-крайней мере для меня) подробная. Буду писать тут вопросы по ходу возникновения 🙂

        PS: год в футере неактуальный 🙂

        • krvss пишет:

          23 ноября, 2011

          Да, насчет local_settings такое вполне возможно — я сразу же делаю свою версию из template.

          Про год — спасибо! 🙂

          • Иван пишет:

            16 декабря, 2011

            Зачем бэкэнду vkontakte шаблон? и что в этом шаблоне должно быть?

          • krvss пишет:

            17 декабря, 2011

            Шаблон содержит код на JavaScript для авторизации через ВКонтакте OpenAPI. Он используется исключительно в качестве примера для тех, кто хочет авторизовываться через OpenAPI. У OpenAPI есть одно большое преимущество — он задает на 1 вопрос меньше, чем OAuth, и два недостатка — нестандартность и отсутствие данных о фото. Для OAuth никаких шаблонов не надо.

  • Иван пишет:

    16 декабря, 2011

    Впрочем пофиг, использовал vkontakte-oauth2

  • Иван пишет:

    26 декабря, 2011

    После переезда с nginx + дефолтный джанговский девелопмент сервак на nginx + uwsgi yandex не хочет коннектится со следующей ошибкой:

    2011/12/26 12:53:34 [error] 23324#0: *236 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 94.179.162.139, server: someserver.net, request: «GET /complete/yandex/?janrain_nonce=…

    почему-то сей запрос обрезается. Вопрос, где что покрутить, что бы он не обрезался (на django-девелопменте запрос был длиннее)

    • krvss пишет:

      26 декабря, 2011

      Видимо что-то с nginx настройками — у нас на uwsgi все работает на ура, при том что в настройках библиотеки ничего не менялось. напиши мне на krvss@mail.ru, вышлю нашу конфигурацию.

Comments closed