Django Social Auh и авторизация в приложениях для ВКонтакте
Кроме авторизации в приложениях для Facebook в ходе работы над проектом была решена еще одна задача – авторизация в приложениях для ВКонтакте. Как и в случае с Facebook, у меня было сильное желание не делать два раза одну и ту же работу, а использовать одну и ту же базу пользователей и один и тот же механизм для авторизации через сайт ВКонтакте и для авторизации в iframe-приложении ВКонтакте. Это удалось решить без особых сложностей, и вот наконец пришло время сорвать покров тайны с некоторых загадочных настроек.
Особенности ВКонтакте
В отличие от Facebook, у ВКонтакте есть одна принципиальная особенность, которая несколько затрудняет решение этой задачи, а именно: вы не можете использовать одно и то же приложение для авторизации через OAuth и для авторизации в iframe-приложении. Вам необходимо зарегистрировать iframe-приложение для ВКонтакте отдельно и получить для него id и secret.
Другая особенность, которая, наоборот, упрощает работу, заключается в том, что ВКонтакте не делает POST-запрос при переходе на URL вашего приложения, а использует метод GET. То есть, минус одна заморочка с CSRF.
В целом для ВКонтакте используется тот же принцип, что и для Facebook, а именно:
- При обращении к view приложения происходит проверка: через какой backend авторизован пользователь;
- Если это ВКонтакте, то мы выбираем из базы access_token и используем его для работы с API;
- Если нет – мы используем полученный запрос для авторизации;
- Доработанный Backend для ВКонтакте разбирает запрос и производит авторизацию с учетом того, что могла использоваться как авторизация через сайт, так и авторизация через приложение.
Начнем по порядку.
Проверка авторизации
Чтобы убедиться, что пользователь нашего view авторизован через нужный нам backend, используется функция is_complete_authorization
# Checks the completeness of current user authentication; complete = logged via VKontakte backend def is_complete_authentication(request): return request.user.is_authenticated() and VKontakteOAuth2Backend.__name__ in request.session.get(BACKEND_SESSION_KEY, '')
Декоратор для view приложения
Декоратор выглядит чуть проще, чем для Facebook, так как не нужно разбирать signed_request.
def vkontakte_intro(func): def wrapper(request, *args, **kwargs): # User must me logged via VKontakte backend in order to ensure we talk about the same person if not is_complete_authentication(request): try: social_complete(request, VKontakteOAuth2Backend.name) except (ValueError, AttributeError): pass # Need to re-check the completion if is_complete_authentication(request): kwargs.update({'access_token': get_access_token(request.user)}) else: request.user = AnonymousUser() return func(request, *args, **kwargs) return wrapper
Назначение у функции get_access_token: вытащить из базы или из кэша access_token для указанного пользователя. Пример:
VK_AT_CACHE_PREFIX = 'VK_AT_%s' # Returns cached access token for the user; loads it from db if needed def get_access_token(user): key = VK_AT_CACHE_PREFIX % str(user.id) access_token = cache.get(key) # If cache is empty read the database if access_token is None: try: social_user = user.social_user if hasattr(user, 'social_user') else UserSocialAuth.objects.get(user=user.id, provider=VKontakteOAuth2Backend.name) except UserSocialAuth.DoesNotExist: return None if social_user.extra_data: access_token = social_user.extra_data.get('access_token') expires = social_user.extra_data.get('expires') cache.set(key, access_token, int(expires) if expires is not None else 0) return access_token
Подключение к приложению
Для работы некоторых методов API вы должны запросить у пользователя установку вашего приложения и нужные вам права. Это две разные операции, первая из которых запускается при вызове функции startConnect, а вторая – следом за ней, в функции requestRights.
{% block head %} <script src="http://vkontakte.ru/js/api/xd_connection.js?2" type="text/javascript"></script> {% endblock %} {% block js %} <script type="text/javascript"> VK.init(function() { // any of your code here } ); function startConnect() { VK.callMethod('showInstallBox'); } function requestRights() { VK.callMethod('showSettingsBox', 1 + 2); // 1+2 is just an example } function onSettingsChanged(settings) { window.location.reload(); } $(document).ready( function(){ VK.addCallback("onApplicationAdded", requestRights); VK.addCallback("onSettingsChanged", onSettingsChanged); }); </script> {% endblock %}
Необходимые настройки
Для того чтобы авторизация через приложение начала работать, нужно сделать следующие настройки:
VKONTAKTE_APP_AUTH={'key':'iframe_app_secret_key', 'user_mode': 2, 'id':'iframe_app_id'}
Параметр user_mode может принимать значения 0, 1 или 2, он влияет на проверку того, подключился пользователь к вашему приложению или нет. Если поставить user_mode в 0, то никакой проверки произведено не будет и авторизация успешно пройдет в любом случае. При user_mode=1 будет проведена проверка на значение параметра is_app_user. Этот параметр приходит в GET-запросе, который присылает ВКонтакте на URL вашего приложения при начале работы с ним. Авторизация будет успешной, только если пользователь подключен к вашему приложению. Однако может случиться так, что пользователь подключился к вашему приложению позже, а запрос уже был проанализирован, is_app_user был 0, а в браузере пользователь уже давно ушел на другую страницу. Для этого используется значение 2, которое делает запрос к API и проверяет, подключил пользователь ваше приложение или нет. Лично я пользуюсь последним вариантом.
Значение key используется для проверки авторизации, id – для выполнения запросов к API. Оно имеет более высокий приоритет по отношению к VKONTAKTE_APP_ID, потому что именно для него вы получаете права от пользователя в интерфейсе.
Если вы не хотите использовать авторизацию через приложение, просто не используйте VKONTAKTE_APP_AUTH совсем.
Update
Пример использования добавлен в главную ветку 23 августа 2012.
Ответы на: Django Social Auh и авторизация в приложениях для ВКонтакте
anonymous пишет:
10 ноября, 2011
При подключении к контакту через oauth2 выдаётся 400 ошибка. В чём может быть дело?
Firefox can't find the file at http://api.vkontakte.ru/oauth/authorize?scope=&redirect_uri=http://dev.mysite.com:8008/complete/vkontakte-oauth2/&response_type=code&client_id=myclientid.
Собственно только домен и клиент id изменил.
krvss пишет:
10 ноября, 2011
Да много причин может быть. Я не совсем понял, что значит «только домен и client id изменил» — раньше работало, а теперь перестало?
anonymous пишет:
10 ноября, 2011
Имеется в виду в моём комментарии. Нет, раньше тоже не работало. Много причин? А какие наиболее вероятные?
anonymous пишет:
10 ноября, 2011
Прошу прощения. На заметил, что неправильный код ввёл. Спасибо за быстрый ответ.
hades пишет:
18 декабря, 2011
А почему модули для вконтактика, однокамерников и пр. не попали в основной репозиторий?
krvss пишет:
18 декабря, 2011
Основной репозиторий сделан англоязычными людьми для англоязычных сервисов, им наши сервисы совсем никак.
hades пишет:
18 декабря, 2011
Это политика автора? Или ты просто не предлагал?
krvss пишет:
18 декабря, 2011
Когда я сделал первый backend (это был ЖЖ), библиотека была почти не расширяемой, и автор специально сделал механизм подключаемых модулей. Я как-то не видел особого смысла добавлять туда наши сервисы, да в общем и сейчас не особо горю — меньше нужно согласований на какие-то изменения в основных библиотеках.
Igor пишет:
28 декабря, 2011
Взял поример с репы, доибавил вконтаките бекенд. Получаю вот такую ошибку на срвеице /done/
__init__() takes exactly 3 arguments (1 given) (vkontakte-oauth2)
Сранно то что Фейсбук тоже ламается.
Посмотрел трасбек:
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/views.py", line 66, in wrapper
return func(request, backend, *args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/views.py", line 95, in complete
return complete_process(request, backend, *args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/views.py", line 152, in complete_process
user = auth_complete(request, backend, *args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/views.py", line 195, in auth_complete
return backend.auth_complete(*args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/backends/contrib/vkontakte.py", line 147, in auth_complete
auth_result = super(VKontakteOAuth2, self).auth_complete(*args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/social_auth/backends/__init__.py", line 591, in auth_complete
return authenticate(*args, **kwargs)
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/django/contrib/auth/__init__.py", line 53, in authenticate
for backend in get_backends():
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/django/contrib/auth/__init__.py", line 44, in get_backends
backends.append(load_backend(backend_path))
File "/Users/polinom/Envs/website/lib/python2.6/site-packages/django/contrib/auth/__init__.py", line 38, in load_backend
return cls()
TypeError: __init__() takes exactly 3 arguments (1 given)
Есть идеи в чем может быть проблема?
krvss пишет:
28 декабря, 2011
Что значит «добавил вконтакте бэкэнд»? Только что проверил — ВКонтакте и Facebook работают нормально в примере из репозитария. Видимо что-то в настройках.
Надо проверить:
FACEBOOK_APP_ID
FACEBOOK_API_SECRET
VKONTAKTE_APP_ID
VKONTAKTE_APP_SECRET
Вот кроме них какие значения у меня в local_settings:
SOCIAL_AUTH_CREATE_USERS = True
SOCIAL_AUTH_FORCE_RANDOM_USERNAME = False
SOCIAL_AUTH_DEFAULT_USERNAME = ‘socialauth_user’
SOCIAL_AUTH_COMPLETE_URL_NAME = ‘socialauth_complete’
LOGIN_ERROR_URL = ‘/error/’
LOGIN_REDIRECT_URL = ‘/done/’
SOCIAL_AUTH_ERROR_KEY = ‘social_auth_error’
Igor пишет:
28 декабря, 2011
В AUTHENTICATION_BACKENDS не правильно бекенд прописал.
В место ‘social_auth.backends.contrib.vkontakte.VKontakteOAuth2Backend’,
написал ‘social_auth.backends.contrib.vkontakte.VKontakteOAuth2’,
Так что все гуд.
Только Гугл так и не роботает.
Когда гугл редиректит меня обратно бросает на LOGIN_ERROR_URL,
krvss пишет:
29 декабря, 2011
Ага. А который из именно из Гуглов?
Igor пишет:
29 декабря, 2011
google-oauth2
krvss пишет:
30 декабря, 2011
Да, у меня тоже самое — ошибка 400. Проблема в том, что я не пользуюсь Google OAuth 2 🙂 Но я посмотрю в праздники где может быть проблема. Отпишусь здесь.
krvss пишет:
4 января, 2012
Проверил сегодня — оказалось был сам неправ, потерял последний символ в CLIENT SECRET. Как только вписал правильные параметры — все заработало.
Андрей пишет:
11 января, 2012
Есть ли способ избавиться от лишней страницы при обычной авторизации через ВКонтакте (без приложения)? Имеется ввиду страница из example.
В суть работы oauth2 не вникал, но если поставить код из vkontakte.html к остальным ссылкам авторизации, то выдается Authentication cancelled (vkontakte-oauth2). Не хватает token-а?
krvss пишет:
11 января, 2012
vkontakte.html это для OpenAPI авторизации, не для OAuth. А токен нужен по любому 🙂
Андрей пишет:
17 января, 2012
Да, точно. Не заметил, что есть два способа авторизации через вконтакт. Спасибо!
aklyuchev пишет:
23 июля, 2012
Всем привет!
Пробую авторизоватьcя через vkontakte
после вызова функции VK.auth.login из vkontakte.html
вызывается функци vkontakte.py:auth_complete:
вываливаеется все вот тут
if not ‘id’ in self.request.GET or \
not app_cookie in self.request.COOKIES:
raise ValueError(‘VKontakte authentication is not completed’)
Вывел print self.request.COOKIES :
{‘csrftoken’: ‘YyoN77mUYColn1wbtJ1LFlVRPL7faTTt’}
Помогите пожалуйста разобраться
krvss пишет:
23 июля, 2012
Привет! Я в запарке, смогу посмотреть ближе к середине-концу недели. Пример кстати работает?
aklyuchev пишет:
24 июля, 2012
Это из примера как раз
aklyuchev пишет:
24 июля, 2012
ззапрос получается такой http://192.168.1.3:8000/complete/vkontakte/?first_name=%D0%90%D0%BB%D0%B5%D0%BA%D1%81%D0%B0%D0%BD%D0%B4%D1%80&last_name=%D0%9A%D0%BB%D1%8E%D1%87%D0%B5%D0%B2&nickname=&id=41424480
Т.е данные из контакта валидные получил но при этом cookies почему то не содержит
это строчки
‘vk_app_’ + self.APP_ID
krvss пишет:
27 июля, 2012
Полез проверять авторизацию через OpenAPI и оказалось, что она была сломана — правда не в том месте, а дальше. Обновил у себя, сделал pull request в главный бранч. Сейчас пример работает корректно.
nonamenix пишет:
18 октября, 2012
вечер добрый,
что-то ошибка с записью app_cookie в cookies так до сих пор и выскакивает, не подскажите как пофиксить можно?
krvss пишет:
22 октября, 2012
если бы у меня выскакивало тоже, то может и посказал бы 🙂 напомни пожалуйста, как ее можно воспроизвести.
aklyuchev пишет:
27 июля, 2012
обновился отсюда
https://github.com/krvss/django-social-auth.git
таже самая проблема с куки
Видимо это локальная проблема.
Что делать?
Запускаюсь на локальной машине без веб сервера
из каталока exmaple
python manage.py runserver 0.0.0.0:8000
krvss пишет:
28 июля, 2012
Не, тут надо запускаться на том домене, который прописан в настройках приложения ВКонтакте. То есть, если там указан mysite.net, то в etc/hosts нужно вписать 127.0.0.1 mysite.net и запускать sudo python manage.py runserver mysite.net:80
aklyuchev пишет:
28 июля, 2012
Дак так и есть :
в настройка приложения вконтакте
адрес сайте и базовый домен — http://192.168.1.3 — мой айпишник
и запускаюсь как sudo python manage.py runserver 0.0.0.0:80
те запросы к моему сервесу возможны через адрес http://192.168.1.3
krvss пишет:
29 июля, 2012
Даже не знаю 🙂 А приложение в браузере ты на каком адресе открываешь — на 192.168.1.3? И вообще, чем отличается твой вариант от примера? Если ничем, то можем сравнить настройки приложения ВКонтакте.
Авторизация для сайта через VKontakte backend пишет:
30 июля, 2012
[…] 30.07.2012 0 0 Попробуйте вот этот: https://github.com/krvss/django-social-authВот подробности: http://www.ikrvss.ru/2011/11/08/django-social-auh-and-vkontakte-application/ […]
aklyuchev пишет:
1 августа, 2012
В сеттингс добавил
VKONTAKTE_APP_ID = » — APP_ID моего VK приложения
VKONTAKTE_API_SECRET = » — Secret Key моего приложения
SOCIAL_AUTH_CREATE_USERS = True
SOCIAL_AUTH_FORCE_RANDOM_USERNAME = False
SOCIAL_AUTH_DEFAULT_USERNAME = ‘socialauth_user’
SOCIAL_AUTH_COMPLETE_URL_NAME = ‘socialauth_complete’
LOGIN_ERROR_URL = ‘/error/’
LOGIN_REDIRECT_URL = ‘/done/’
SOCIAL_AUTH_ERROR_KEY = ‘social_auth_error’
+ заменил
DATABASES = {
…
}
на свою базу.
А в настройка VK приложения же посути Задается пользователем только адрес сайта и базовый домен — у меня в обоих полях прописано http://192.168.1.3
aklyuchev пишет:
1 августа, 2012
Может вечером в скайпе спишемся ? Думаю так будет проще разобраться
Буду очень признателен
krvss пишет:
1 августа, 2012
Насчет скайпа я не против, но сегодня и возможно завтра меня вечером в сети не будет. Напишу, когда. Какой твой часовой пояс — Москва?
aklyuchev пишет:
1 августа, 2012
угу, я из Питера
Vceslava пишет:
7 декабря, 2012
Здравствуйте, у меня к Вам вопрос. Когда я использовала регистрацию на вконтакте через обычное приложение, то никаких проблем не возникало. Однако, когда я пробую сделать регистрацию через iframe приложение, у меня возникает следующая ошибка: Incorrect authentication service «vkontakte-oauth2»
В настройках, я добавила нужные поля, но проблема осталась.
AUTHENTICATION_BACKENDS = (
…
‘social_auth.backends.contrib.vkontakte.VKontakteOAuth2Backend’,
…
‘django.contrib.auth.backends.ModelBackend’,
)
VKONTAKTE_APP_ID=’…’
VKONTAKTE_APP_SECRET=’…’
VKONTAKTE_APP_AUTH = {
‘key’: ‘…’,
‘user_mode’: 2,
‘id’: ‘…’
}
Подскажите пожалуйста, что делать.
krvss пишет:
7 декабря, 2012
Вообще причин может быть масса. Сначала нужно убедиться в двух вещах: 1. что вы запускаете приложение не на localhost, а от имени того сайта, который указан в приложении; 2. что вы используете не то же самое приложение, которое использовали для обычной авторизации — для iframe тип приложения в ВКонтакте другой, точно не помню какой именно, но не подключенный сайт как для обычного варианта.
Vceslava пишет:
7 декабря, 2012
Я точно не использую локалхост, так как работаю через сервер, и я указываю данные о новом приложении, плюс добавила VKONTAKTE_APP_AUTH. причем регистрация через твиттер и фейсбук работает. а вот контакт…
krvss пишет:
10 декабря, 2012
Ну, регистрация через Twitter и Facebook (если это обыный Facebook, а не iframe-приложение для него) работают совсем по другой схеме. У Вконтакте вообще уникальная в этом случае ситуация, в том смысле, что нужно иметь 2 разных приложения — одно для oauth, а второе для iFrame.
Если проблема возникает, и локальный хост не используется, то проблема в настройках. Я бы посоветовал проверить значения настроек VK_APP_ID, VK_API_SECRET и убедиться что правильно указаны VKONTAKTE_APP_AUTH={‘key’:’iframe_app_key’, ‘user_mode’: 2, ‘id’:’iframe_app_id’} и использовать для проверки пример из самого Django-Social-Auth, чтобы гарантированно использовать рабочий код.