- en
- Language: ru
- Documentation version: 0.1
Добавление нового бэкенда¶
Adding new backends is quite easy. Usually just all that’s required is to add
a class with a couple settings and method overrides to retrieve user data
from a services API. Follow the details below:
Общие атрибуты¶
First, let’s check the common attributes for all backend types.
name = ''Any backend needs a name, usually the popular name of the service is used, like
facebook,twitter, etc. It must be unique, otherwise another backend can take precedence if it’s listed before in theAUTHENTICATION_BACKENDSsetting.ID_KEY = NoneDefines the attribute in the service response that identifies the user as unique to the service, the value is later stored in the
uidattribute in theUserSocialAuthinstance.REQUIRES_EMAIL_VALIDATION = FalseФлаг бэкенда для принудительной проверки электронной почты во время конвейера (если соответствующий конвейер
social_core.pipeline.mail.mail_validationбыл включен).EXTRA_DATA = NoneDuring the auth process some basic user data is returned by the provider or retrieved by the
user_data()method which usually is used to call some API on the provider to retrieve it. This data will be stored in theUserSocialAuth.extra_dataattribute, but to make it accessible under some common names on different providers, this attribute defines a list of tuples in the form(name, alias)wherenameis the key in the user data (which should be adictinstance) andaliasis the name to store it onextra_data.ACCESS_TOKEN_METHOD = 'GET'Specifying the method type required to retrieve your access token if it’s not the default GET request.
OAuth¶
OAuth1 and OAuth2 provide some common definitions based on the shared
behavior during the auth process. For example, a successful API response from
AUTHORIZATION_URL usually returns some basic user data like a user Id.
OAuth2¶
Бэкенды OAuth2 довольно просты в реализации: всего несколько настроек, переопределение метода, и все готово к работе.
Ключевыми моментами этого бэкенда являются:
AUTHORIZATION_URLT
ACCESS_TOKEN_URLДолжен указывать на конечную точку API, которая предоставляет
access_token, необходимую для аутентификации от имени пользователя при последующих вызовах API.REFRESH_TOKEN_URLНекоторые провайдеры предоставляют возможность обновлять
access_token, так как они обычно ограничены по времени, по истечении этого времени токен становится недействительным и больше не может быть использован. Этот атрибут должен указывать на конечную точку API.RESPONSE_TYPEТип ответа, ожидаемый в процессе auth, значение по умолчанию
code, как диктует определение OAuth2. Переопределите его, если значение по умолчанию не подходит для реализации провайдера.STATE_PARAMETEROAuth2 определяет, что параметр
stateможет быть передан для подтверждения процесса, это своего рода проверка CSRF, чтобы избежать атак типа «человек посередине». Некоторые не распознают его или не возвращают, что сделает процесс auth недействительным. В этом случае установите этот атрибут вFalse.REDIRECT_STATEДля тех провайдеров, которые не распознают параметр
state, приложение может добавить аргументredirect_stateкredirect_uri, чтобы имитировать его. Установите значениеFalse, если провайдеру нравится проверять значениеredirect_uriи этот параметр делает эту проверку недействительной.
Пример кода:
from social_core.backends.oauth import BaseOAuth2
class GitHubOAuth2(BaseOAuth2):
"""GitHub OAuth authentication backend"""
name = 'github'
AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize'
ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token'
ACCESS_TOKEN_METHOD = 'POST'
SCOPE_SEPARATOR = ','
EXTRA_DATA = [
('id', 'id'),
('expires', 'expires')
]
def get_user_details(self, response):
"""Return user details from GitHub account"""
return {'username': response.get('login'),
'email': response.get('email') or '',
'first_name': response.get('name')}
def user_data(self, access_token, *args, **kwargs):
"""Loads user data from service"""
url = 'https://api.github.com/user?' + urlencode({
'access_token': access_token
})
return self.get_json(url)
OAuth1¶
Процесс OAuth1 немного сложнее, Twitter Docs объясняет его достаточно хорошо. Помимо атрибутов AUTHORIZATION_URL и ACCESS_TOKEN_URL, необходим третий, используемый при запуске процесса.
REQUEST_TOKEN_URL = ''Во время процесса авторизации для начала процесса необходим неавторизованный токен, который впоследствии обменивается на
access_token. Этот параметр указывает на конечную точку API, где можно получить этот неавторизованный токен.
Пример кода:
from xml.dom import minidom
from social_core.backends.oauth import ConsumerBasedOAuth
class TripItOAuth(ConsumerBasedOAuth):
"""TripIt OAuth authentication backend"""
name = 'tripit'
AUTHORIZATION_URL = 'https://www.tripit.com/oauth/authorize'
REQUEST_TOKEN_URL = 'https://api.tripit.com/oauth/request_token'
ACCESS_TOKEN_URL = 'https://api.tripit.com/oauth/access_token'
EXTRA_DATA = [('screen_name', 'screen_name')]
def get_user_details(self, response):
"""Return user details from TripIt account"""
try:
first_name, last_name = response['name'].split(' ', 1)
except ValueError:
first_name = response['name']
last_name = ''
return {'username': response['screen_name'],
'email': response['email'],
'fullname': response['name'],
'first_name': first_name,
'last_name': last_name}
def user_data(self, access_token, *args, **kwargs):
"""Return user data provided"""
url = 'https://api.tripit.com/v1/get/profile'
request = self.oauth_request(access_token, url)
content = self.fetch_response(request)
try:
dom = minidom.parseString(content)
except ValueError:
return None
return {
'id': dom.getElementsByTagName('Profile')[0].getAttribute('ref'),
'name': dom.getElementsByTagName(
'public_display_name')[0].childNodes[0].data,
'screen_name': dom.getElementsByTagName(
'screen_name')[0].childNodes[0].data,
'email': dom.getElementsByTagName(
'is_primary')[0].parentNode.getElementsByTagName(
'address')[0].childNodes[0].data,
}
OpenID¶
OpenID is far simpler that OAuth since it’s used for authentication rather than authorization (regardless it’s used for authorization too).
Обычно требуется один атрибут - конечная точка URL-адреса аутентификации.
URL = ''OpenID endpoint where to redirect the user.
Sometimes the URL is user dependant, like in myOpenID where the URL is
https://<user handler>.myopenid.com. For those cases where the user must
input it’s handle (or full URL). The backend must override the openid_url()
method to retrieve it and return a full URL to where the user will be
redirected.
Пример кода:
from social_core.backends.open_id import OpenIdAuth
from social_core.exceptions import AuthMissingParameter
class LiveJournalOpenId(OpenIdAuth):
"""LiveJournal OpenID authentication backend"""
name = 'livejournal'
def get_user_details(self, response):
"""Generate username from identity url"""
values = super(LiveJournalOpenId, self).get_user_details(response)
values['username'] = values.get('username') or \
urlparse.urlsplit(response.identity_url)\
.netloc.split('.', 1)[0]
return values
def openid_url(self):
"""Returns LiveJournal authentication URL"""
if not self.data.get('openid_lj_user'):
raise AuthMissingParameter(self, 'openid_lj_user')
return 'http://%s.livejournal.com' % self.data['openid_lj_user']
Auth APIs¶
Для других типов аутентификации определен класс BaseAuth. Эти пользовательские методы аутентификации должны переопределять методы auth_url() и << 2 >>>.
Пример кода:
from google.appengine.api import users
from social_core.backends.base import BaseAuth
from social_core.exceptions import AuthException
class GoogleAppEngineAuth(BaseAuth):
"""GoogleAppengine authentication backend"""
name = 'google-appengine'
def get_user_id(self, details, response):
"""Return current user id."""
user = users.get_current_user()
if user:
return user.user_id()
def get_user_details(self, response):
"""Return user basic information (id and email only)."""
user = users.get_current_user()
return {'username': user.user_id(),
'email': user.email(),
'fullname': '',
'first_name': '',
'last_name': ''}
def auth_url(self):
"""Build and return complete URL."""
return users.create_login_url(self.redirect_uri)
def auth_complete(self, *args, **kwargs):
"""Completes login process, must return user instance."""
if not users.get_current_user():
raise AuthException('Authentication error')
kwargs.update({'response': '', 'backend': self})
return self.strategy.authenticate(*args, **kwargs)