Авторизация в социальных сетях для 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, вышлю нашу конфигурацию.