Трубопровод

python-social-auth использует расширяемый механизм конвейера, в котором разработчики могут внедрять свои функции в потоки аутентификации, объединения и разъединения.

Функции будут получать переменный набор аргументов, относящихся к текущему процессу, общими аргументами являются текущий strategy, user (если есть) и request. Рекомендуется, чтобы все функции также определяли **kwargs в параметрах, чтобы избежать ошибок для неожиданных аргументов.

Каждый элемент конвейера может возвращать dict или None, любой другой тип возвращаемого значения рассматривается как экземпляр ответа и возвращается непосредственно клиенту, подробности смотрите в разделе Частичный конвейер ниже.

Если возвращается dict, значение в наборе будет объединено в аргумент kwargs для следующего входа в конвейер, None принимается, как если бы было возвращено {}.

Конвейер аутентификации

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

По умолчанию трубопровод состоит из:

(
    # Get the information we can about the user and return it in a simple
    # format to create the user instance later. In some cases the details are
    # already part of the auth response from the provider, but sometimes this
    # could hit a provider API.
    'social_core.pipeline.social_auth.social_details',

    # Get the social uid from whichever service we're authing thru. The uid is
    # the unique identifier of the given user in the provider.
    'social_core.pipeline.social_auth.social_uid',

    # Verifies that the current auth process is valid within the current
    # project, this is where emails and domains whitelists are applied (if
    # defined).
    'social_core.pipeline.social_auth.auth_allowed',

    # Checks if the current social-account is already associated in the site.
    'social_core.pipeline.social_auth.social_user',

    # Make up a username for this person, appends a random string at the end if
    # there's any collision.
    'social_core.pipeline.user.get_username',

    # Send a validation email to the user to verify its email address.
    # Disabled by default.
    # 'social_core.pipeline.mail.mail_validation',

    # Associates the current social details with another user account with
    # a similar email address. Disabled by default.
    # 'social_core.pipeline.social_auth.associate_by_email',

    # Create a user account if we haven't found one yet.
    'social_core.pipeline.user.create_user',

    # Create the record that associates the social account with the user.
    'social_core.pipeline.social_auth.associate_user',

    # Populate the extra_data field in the social record with the values
    # specified by settings (and the default ones like access_token, etc).
    'social_core.pipeline.social_auth.load_extra_data',

    # Update the user record with any changed info from the auth service.
    'social_core.pipeline.user.user_details',
)

Это можно отменить, определив настройку SOCIAL_AUTH_PIPELINE. Например, конвейер, который не будет создавать пользователей, а только принимать уже зарегистрированных, будет выглядеть следующим образом:

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.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

Обратите внимание, что здесь предполагается, что пользователь уже аутентифицирован, и поэтому ключ user в dict заполнен. В случаях, когда аутентификация является чисто внешней, должен быть предусмотрен конвейерный метод, который заполняет ключ 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',
    'myapp.pipeline.load_user',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

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

Каждая функция трубопровода получает следующие параметры:
  • Текущая стратегия (которая дает доступ к текущему магазину, бэкенду и запросу)

  • Идентификатор пользователя, предоставленный провайдером аутентификации

  • Данные пользователя, предоставленные провайдером аутентификации

  • is_new флаг (инициализируется как False)

  • Любые аргументы, переданные методу бэкенда auth_complete, представления по умолчанию передают эти аргументы:

    • текущий вошедший пользователь (если он вошел в систему, иначе None)

    • текущий запрос

Трубопровод отсоединения

Как и конвейер аутентификации, при необходимости можно определить конвейер разъединения.

Например, это может быть полезно на сайтах, где пользователь, отключивший все связанные с ним социальные аккаунты, должен ввести пароль для обеспечения процесса аутентификации в будущем. Этого можно достичь, переопределив стандартный конвейер отключения и установив функцию, которая проверяет, есть ли у пользователя пароль, если нет, то можно вернуть перенаправление на форму заполнения пароля и позже продолжить процесс отключения. Примите во внимание, что отключение по умолчанию обеспечивает метод POST, простой способ обеспечить это - сделать вашу форму POST на /disconnect/ и установить необходимый пароль в вашей функции конвейера. Посмотрите Частичный конвейер ниже.

Для того чтобы переопределить конвейер отключения, просто определите параметр:

SOCIAL_AUTH_DISCONNECT_PIPELINE = (
    # Verifies that the social association can be disconnected from the current
    # user (ensure that the user login mechanism is not compromised by this
    # disconnection).
    'social_core.pipeline.disconnect.allowed_to_disconnect',

    # Collects the social associations to disconnect.
    'social_core.pipeline.disconnect.get_entries',

    # Revoke any access_token when possible.
    'social_core.pipeline.disconnect.revoke_tokens',

    # Removes the social associations.
    'social_core.pipeline.disconnect.disconnect',
)

Backend specific disconnection pipelines can also be defined with a setting such as SOCIAL_AUTH_TWITTER_DISCONNECT_PIPELINE.

Частичный трубопровод

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

Для этого украсьте функцию, которая будет обрывать процесс, декоратором @partial, расположенным по адресу social/pipeline/partial.py.

Когда настанет время возобновить процесс, просто перенаправьте пользователя к виду /complete/<backend>/ или << 1 >>>. Конвейер возобновится в той же функции, которая прервала процесс.

@partial stores needed data into a database table name social_auth_partial. This table holds the needed information to resume it later from any browsers and drops the old dependency on browser sessions that made the move between browsers impossible.

The partial data is identified by a UUID token that can be used to store in the session or append to any URL using the partial_token parameter (default value). The lib will pick this value from the request and load the needed partial data to let the user continue the process.

The pipeline functions will get a current_partial instance that contains the partial token and the needed data that will be saved in the database.

Чтобы перенаправить бэкенд на любой социальный вид, просто сделайте следующее:

backend = current_partial.backend

Чтобы отменить имя параметра по умолчанию, просто определите:

SOCIAL_AUTH_PARTIAL_PIPELINE_TOKEN_NAME = '...'

Проверьте example applications для проверки базового использования.

Проверка электронной почты

Существует конвейер для проверки адресов электронной почты, но он во многом зависит от вашего проекта.

The pipeline is at social_core.pipeline.mail.mail_validation and it’s a partial pipeline, it will return a redirect to the URL defined by the EMAIL_VALIDATION_URL setting. For Django you can use a view name as the value for this setting. You can use this redirect to tell the users that an email validation was sent to them. If you want to mention the email address you can get it from the session under the key email_validation_address.

Для отправки валидации python-social-auth необходима функция, которая позаботится об этом, эта функция определяется разработчиком с помощью параметра SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION. Это должен быть путь импорта. Эта функция должна принимать четыре аргумента strategy, backend, code и partial_token.

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

code - это экземпляр модели, используемый для проверки адреса электронной почты, он содержит три поля:

code = '...'

Имеет значение uuid.uuid4() и является кодом, используемым для идентификации процесса валидации.

email = '...'

Адрес электронной почты пытается быть подтвержденным.

verified = True / False

Флаг, отмечающий, было ли письмо проверено или нет.

You should use the code in this instance to build the link for email validation which should go to /complete/email?verification_code=<code here>&partial_token=<token here>. If you are using Django, you can do it with:

from django.core.urlresolvers import reverse
url = strategy.build_absolute_uri(
    reverse('social:complete', args=(strategy.backend_name,))
) + '?verification_code=' + code.code + '&partial_token=' + partial_token

На фляжке:

from flask import url_for
url = url_for('social.complete', backend=strategy.backend_name,
              _external=True) + '?verification_code=' + code.code + '&partial_token=' + partial_token

Этот трубопровод можно использовать глобально с любым бэкендом, если задан этот параметр:

SOCIAL_AUTH_FORCE_EMAIL_VALIDATION = True

Или индивидуально, определяя настройки для каждого бэкенда, например SOCIAL_AUTH_TWITTER_FORCE_EMAIL_VALIDATION = True.

Расширение трубопровода

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

Расширение трубопровода подразумевает:

  1. Написание функции

  2. Размещение функции в доступном пути (доступном в том смысле, что она может быть импортирована)

  3. Переопределение определения трубопровода по умолчанию с помощью определения, включающего вновь созданную функцию.

Часть написания функции довольно проста. Однако, пожалуйста, будьте внимательны при размещении вашей функции в определении конвейера, потому что порядок в данном случае имеет значение! Порядок расположения функций в SOCIAL_AUTH_PIPELINE будет определять значение аргументов, которые получит каждая функция. Например, добавление вашей функции после social_core.pipeline.user.create_user гарантирует, что ваша функция получит экземпляр пользователя (созданный или уже существующий) вместо значения None.

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

strategy

Текущий экземпляр стратегии.

backend

Текущий экземпляр бэкенда.

uid

ID пользователя в провайдере, это uid должно идентифицировать пользователя в текущем провайдере.

response = {} or object()

The server user-details response, it depends on the protocol in use (and sometimes the provider implementation of such protocol), but usually it’s just a dict with the user profile details from the provider. Lots of information related to the user is provided here, sometimes the scope will increase the amount of information in this response on OAuth providers.

details = {}

Основные данные пользователя, генерируемые бэкендом, используемые для создания/обновления данных модели пользователя (этот dict будет содержать значения username, email, first_name, last_name и fullname).

user = None

Экземпляр пользователя (или None, если он еще не создан или не извлечен из базы данных).

social = None

Это связанный экземпляр UserSocialAuth для данного пользователя (или None, если он еще не был создан или извлечен из БД).

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

Вот пример простой конвейерной функции, которая создаст экземпляр класса Profile, связанный с текущим пользователем. Этот профиль будет хранить некоторые простые детали, возвращаемые провайдером (Facebook в данном примере). Обычный Facebook response выглядит следующим образом:

{
    'username': 'foobar',
    'access_token': 'CAAD...',
    'first_name': 'Foo',
    'last_name': 'Bar',
    'verified': True,
    'name': 'Foo Bar',
    'locale': 'en_US',
    'gender': 'male',
    'expires': '5183999',
    'email': 'foo@bar.com',
    'updated_time': '2014-01-14T15:58:35+0000',
    'link': 'https://www.facebook.com/foobar',
    'timezone': -3,
    'id': '100000126636010',
}

Допустим, мы заинтересованы в хранении ссылки на профиль пользователя, пола и часового пояса в нашей модели Profile:

def save_profile(backend, user, response, *args, **kwargs):
    if backend.name == 'facebook':
        profile = user.get_profile()
        if profile is None:
            profile = Profile(user_id=user.id)
        profile.gender = response.get('gender')
        profile.link = response.get('link')
        profile.timezone = response.get('timezone')
        profile.save()

Теперь все, что нужно, это сказать python-social-auth использовать нашу функцию в конвейере. Поскольку функция использует экземпляр пользователя, нам нужно поместить его после social_core.pipeline.user.create_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',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.user.create_user',
    'path.to.save_profile',  # <--- set the path to the function
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

Пока созданная нами функция возвращает None, что воспринимается так, как если бы было возвращено {}. Если вы хотите, чтобы объект profile был доступен следующей функции в конвейере, все, что вам нужно сделать, это вернуть {'profile': profile}.

Вернуться на верх