Авторизация в социальных сетях для Django
Зачем
Когда в нашем проекте возникла необходимость реализовать авторизацию через социальные сети, я по совету Ромы Ворушина просмотрел несколько готовых решений, чтобы не изобретать уже многократно изобретенный велосипед. Оказалось, что не все велосипеды умеют ездить туда, куда нам нужно, и в точности так, как именно нам нужно:
- Кроме крупных мировых сетей хотелось также работать с LiveJournal, Yandex и ВКонтакте.
- Нужна была возможность контролировать действия системы после успешной авторизации, то есть управлять процессом обработки полученной от сервиса информации о пользователях.
Я остановился на 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, вышлю нашу конфигурацию.