• en
  • Language: ru
  • Documentation version: 0.1

Случаи использования

Некоторые опции и варианты использования python-social-auth.

Возврат пользователя на исходную страницу

Существует распространенный сценарий возврата пользователя на исходную страницу, с которой он запросил вход в систему. Для этого используется обычный аргумент запроса-строки next. Значение этого параметра будет сохранено в сессии и позже будет использовано для перенаправления пользователя, если вход в систему был успешным.

Чтобы использовать его, просто определите его в своей ссылке. Например, при использовании Django:

<a href="{% url 'social:begin' 'facebook' %}?next={{ request.path }}">Login with Facebook</a>

Передача пользовательских параметров GET/POST и их получение при аутентификации

В некоторых случаях вам может потребоваться отправить данные по URL и получить их в процессе обработки постфактум. Например, для условного выполнения кода в пользовательских конвейерах.

В таких случаях добавьте его к SOCIAL_AUTH_FIELDS_STORED_IN_SESSION.

В настройках:

SOCIAL_AUTH_FIELDS_STORED_IN_SESSION = ['key']

В шаблоне:

<a href="{% url 'social:begin' 'facebook' %}?key={{ value }}">Login with Facebook</a>

В своем пользовательском конвейере получите его с помощью:

strategy.session_get('key')

Извлечь друзей Google+

Google предоставляет API People API endpoint для получения информации о людях в ваших кругах в Google+. Чтобы получить доступ к этому API, сначала нам нужно определить необходимую область видимости:

SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [
    'https://www.googleapis.com/auth/plus.login'
]

Получив access token, мы можем вызвать API следующим образом:

import requests

user = User.objects.get(...)
social = user.social_auth.get(provider='google-oauth2')
response = requests.get(
    'https://www.googleapis.com/plus/v1/people/me/people/visible',
    params={'access_token': social.extra_data['access_token']}
)
friends = response.json()['items']

Объединение пользователей по электронной почте

Иногда желательно, чтобы социальные учетные записи автоматически ассоциировались, если электронная почта уже соответствует учетной записи пользователя.

For example, if a user signed up with their Facebook account, then logged out and next time tries to use Google OAuth2 to login, it could be nice (if both social sites have the same email address configured) that the user gets into their initial account created by Facebook backend.

Этот сценарий возможен при включении функции конвейера associate_by_email, например, так:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.social_auth.associate_by_email',  # <--- enable this one
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

Эта функция отключена по умолчанию, поскольку автоматизировать этот процесс со всеми бэкендами не на 100% безопасно. Не все провайдеры будут проверять вашу учетную запись электронной почты, и другие пользователи могут воспользоваться этим.

Take for instance User A registered in your site with the email foo@bar.com. Then a malicious user registers into another provider that doesn’t validate their email with that same account. Finally this user will turn to your site (which supports that provider) and sign up to it, since the email is the same, the malicious user will take control over the User A account.

Регистрация по OAuth access_token

Это распространенный сценарий, когда мобильные приложения используют SDK для регистрации пользователя в приложении, но эта регистрация не будет отражена python-social-auth, пока не будут созданы соответствующие записи в базе данных. Чтобы сделать это, можно создать представление / маршрут, который создает эти записи по заданному access_token. Возьмем, например, следующий код (код следует соглашениям Django, но версии для других фреймворков могут быть легко реализованы):

from django.contrib.auth import login

from social_django.utils import psa

# Define an URL entry to point to this view, call it passing the
# access_token parameter like ?access_token=<token>. The URL entry must
# contain the backend, like this:
#
#   url(r'^register-by-token/(?P<backend>[^/]+)/$',
#       'register_by_access_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.
    token = request.GET.get('access_token')
    user = request.backend.do_auth(token)
    if user:
        login(request, user)
        return 'OK'
    else:
        return 'ERROR'

Приведенный выше фрагмент довольно прост, он не возвращает JSON, и обычно этот вызов выполняется с помощью AJAX. Он не возвращает информацию о пользователе, но это то, что можно расширить и наполнить в соответствии с проектом, в котором он будет использоваться.

Примечание: при работе с OAuth1, access_token является

фактически запрос-строка, состоящая из oauth_token и oauth_token_secret, python-social-auth ожидает, что это будет dict с этими ключами, но если будет обнаружена строка, он будет рассматривать ее как строку запроса в форме oauth_token=123&oauth_token_secret=456.

Множественные диапазоны для каждого поставщика

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

  1. Переопределение get_scope() метода:

    from social_core.backends.facebook import FacebookOAuth2
    
    
    class CustomFacebookOAuth2(FacebookOauth2):
        def get_scope(self):
            scope = super(CustomFacebookOAuth2, self).get_scope()
            if self.data.get('extrascope'):
                scope = scope + [('foo', 'bar')]
            return scope
    

    Этот метод довольно прост, он переопределяет метод, возвращающий значение области видимости в бэкенде (get_scope()) и добавляет дополнительные значения в список, если они были указаны параметром в данных GET или <<2 >>> (POST).

    Поместите этот новый бэкенд в какое-то место в вашем проекте и замените оригинальный FacebookOAuth2 в AUTHENTICATION_BACKENDS на эту новую версию.

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

    scope = super(CustomFacebookOAuth2, self).get_scope()
    scope += ['foo', 'bar']
    

    Вместо этого сделайте это следующим образом:

    scope = super(CustomFacebookOAuth2, self).get_scope()
    scope = scope + ['foo', 'bar']
    
  2. Можно сделать то же самое, определив второй бэкенд, который расширяет первоначальный, но переопределяет имя, это подразумевает новые URL, а также новые настройки для нового бэкенда (поскольку имя используется для построения имен настроек), это также подразумевает новое приложение в провайдере, поскольку не все провайдеры дают вам возможность определять несколько URL перенаправления. Чтобы сделать это, просто добавьте бэкенд, например:

    from social_core.backends.facebook import FacebookOAuth2
    
    
    class CustomFacebookOAuth2(FacebookOauth2):
        name = 'facebook-custom'
    

    Поместите этот новый бэкенд в какое-то место в вашем проекте, сохранив исходный FacebookOAuth2 в AUTHENTICATION_BACKENDS. Теперь будет функционировать новый набор URL:

    /login/facebook-custom
    /complete/facebook-custom
    /disconnect/facebook-custom
    

    А также новый набор настроек:

    SOCIAL_AUTH_FACEBOOK_CUSTOM_KEY = '...'
    SOCIAL_AUTH_FACEBOOK_CUSTOM_SECRET = '...'
    SOCIAL_AUTH_FACEBOOK_CUSTOM_SCOPE = [...]
    

    Когда потребуются дополнительные разрешения, просто перенаправьте пользователя на /login/facebook-custom, а затем получите запись social auth для этого нового бэкенда с user.social_auth.get(provider='facebook-custom') и используйте в ней access_token.

Enable a user to choose a username from their World of Warcraft characters

Если вы хотите регистрировать новых пользователей на своем сайте через battle.net, вы можете позволить этим пользователям выбирать имя пользователя из их собственных персонажей World-of-Warcraft. Для этого используйте бэкенд battlenet-oauth2 вместе с небольшой формой для выбора имени пользователя.

Форма выводится через частичный элемент конвейера следующим образом:

@partial
def pick_character_name(backend, details, response, is_new=False, *args, **kwargs):
    if backend.name == 'battlenet-oauth2' and is_new:
        data = backend.strategy.request_data()
        if data.get('character_name') is None:
            # New user and didn't pick a character name yet, so we render
            # and send a form to pick one. The form must do a POST/GET
            # request to the same URL (/complete/battlenet-oauth2/). In this
            # example we expect the user option under the key:
            #   character_name
            # you have to filter the result list according to your needs.
            # In this example, only guild members are allowed to sign up.
            char_list = [
                c['name'] for c in backend.get_characters(response.get('access_token'))
                    if 'guild' in c and c['guild'] == '<guild name>'
            ]
            return render_to_response('pick_character_form.html', {'charlist': char_list, })
        else:
            # The user selected a character name
            return {'username': data.get('character_name')}

Не забудьте добавить в трубопровод частичный:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'path.to.pick_character_name',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

Он должен быть где-то перед create_user, потому что partial будет изменять имя пользователя в соответствии с выбором пользователя.

Повторно предложите пользователям Google OAuth2 обновить refresh_token.

Значение refresh_token также истекает, значение refresh_token может быть потеряно, но они также могут быть обновлены (или повторно установлены), если вы правильно попросите Google. Для этого установите следующие параметры:

SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {
    'access_type': 'offline',
    'approval_prompt': 'auto'
}

Затем свяжите пользователей с /login/google-oauth2?approval_prompt=force. Если вы хотите обновить refresh_token только для тех пользователей, у которых его нет, сделайте это с помощью конвейерной функции:

def redirect_if_no_refresh_token(backend, response, social, *args, **kwargs):
    if backend.name == 'google-oauth2' and social and \
       response.get('refresh_token') is None and \
       social.extra_data.get('refresh_token') is None:
        return redirect('/login/google-oauth2?approval_prompt=force')

Установите этот конвейер после social_user:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'path.to.redirect_if_no_refresh_token',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

Improve unicode cleanup from usernames

It’s possible to improve the username cleanup by using an external library like Unidecode or Text-Unicode. You can integrate these by using the SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION documented at Username Generation section. For instance, this will do the work:

SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION = 'unidecode.unidecode'