<a id="adding-a-new-backend"></a>

# Adding a new backend

Adding new backends is quite easy.  Usually just all that’s required is to add
a `class` with a couple of settings and method overrides to retrieve user data
from a services API. Follow the details below:

<a id="common-attributes"></a>

## Common attributes

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 the
  `AUTHENTICATION_BACKENDS` setting.

`ID_KEY = None`
: For mapping-based backends, defines the field in the service response that
  identifies the user as unique to the service. The value is later stored in
  the `uid` attribute in the `UserSocialAuth` instance. This can be
  overridden per-backend via the
  `SOCIAL_AUTH_<BACKEND_NAME>_ID_KEY` setting (see
  [Configurable User ID Key](../configuration/settings.html#configurable-user-id-key)).

`REQUIRES_EMAIL_VALIDATION = False`
: Flags the backend to enforce email validation during the pipeline (if the
  corresponding pipeline `social_core.pipeline.mail.mail_validation` was
  enabled).

`EXTRA_DATA = None`
: During 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 the
  `UserSocialAuth.extra_data` attribute, but to make it accessible under
  some common names on different providers, this attribute defines a list of
  tuples in the form `(name, alias)` where `name` is the key in the user
  data (which should be a `dict` instance) and `alias` is the name to
  store it on `extra_data`.

`ACCESS_TOKEN_METHOD = 'GET'`
: Specifying the method type required to retrieve your access token if it’s not
  the default GET request.

<a id="initiating-authentication"></a>

## Initiating authentication

Framework integrations that have the current local user can pass it to
`social_core.actions.do_auth(backend, user=current_user)`. The optional
`user` argument is passed to `BaseAuth.prepare_auth(user=None)` after
the normal redirect and session-field handling, immediately before
`start()`. The default hook does nothing, so integrations that omit the
user retain the existing behavior.

Backends can override `prepare_auth()` to validate the local user or
prepare backend-specific state before authentication starts. An exception
from the hook stops initiation. The user object is passed only to the hook;
a backend that needs to bind a later callback to the user must decide whether
and how to persist a stable identifier.

<a id="oauth"></a>

## 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.

<a id="shared-attributes"></a>

### Shared attributes

`name`
: This defines the backend name and identifies it during the auth process.
  The name is used in the URLs `/login/<backend name>` and
  `/complete/<backend name>`.

`ID_KEY = 'id'`
: The default key name where the user identification field is defined, it’s used
  in the auth process when some basic user data is returned. This Id is stored
  in the `UserSocialAuth.uid` field and this, together with the
  `UserSocialAuth.provider` field, is used to uniquely identify a user
  association.

`SCOPE_PARAMETER_NAME = 'scope'`
: The scope argument is used to tell the provider the API endpoints you want to
  call later, it’s a permissions request granted over the `access_token`
  later retrieved. The default value is `scope` since that’s usually the name
  used in the URL parameter, but can be overridden if needed.

`DEFAULT_SCOPE = None`
: Some providers give nothing about the user but some basic data like the user
  Id or an email address. The default scope attribute is used to specify a
  default value for the `scope` argument to request those extra bits.

`SCOPE_SEPARATOR = ' '`
: The `scope` argument is usually a list of permissions to request, the
  list is joined with a separator, usually just a blank space, but this can differ
  from provider to provider.  Override the default value with this attribute
  if it differs.

<a id="oauth2"></a>

### OAuth2

OAuth2 backends are fairly simple to implement; just a few settings, a method
override and it’s mostly ready to go.

The key points on these backends are:

`AUTHORIZATION_URL`
: This is the entry point for the authorization mechanism, users must be
  redirected to this URL, used on `auth_url` method which builds the
  redirect address with `AUTHORIZATION_URL` plus some arguments
  (`client_id`, `redirect_uri`, `response_type`, and `state`).

`ACCESS_TOKEN_URL`
: Must point to the API endpoint that provides an `access_token` needed to
  authenticate in users behalf on future API calls.

`REFRESH_TOKEN_URL`
: Some providers give the option to renew the `access_token` since they are
  usually limited in time, once that time runs out, the token is invalidated
  and cannot be used anymore. This attribute should point to that API
  endpoint.

`get_refresh_token(extra_data) -> str | None`
: Selects the stored credential passed to `refresh_token()`. The default
  implementation returns a nonempty string from `extra_data['refresh_token']`
  or `None` when it is unavailable. Custom backends that renew by exchanging
  an access token must override this hook; storage no longer falls back to
  the access token automatically. For example:
  <br/>
  ```default
  def get_refresh_token(self, extra_data):
      token = extra_data.get('access_token')
      return token if isinstance(token, str) and token else None
  ```
  <br/>
  This selects the credential only. The backend’s `refresh_token_params()`
  must still construct the provider’s required exchange request. Facebook
  uses this hook with its `fb_exchange_token` grant. See
  [Token renewal](oauth.html.md#oauth-token-renewal) for missing-credential behavior.

`RESPONSE_TYPE`
: The response type expected on the auth process, default value is `code`
  as dictated by OAuth2 definition. Override it if default value doesn’t fit
  the provider implementation.

`STATE_PARAMETER`
: OAuth2 defines that a `state` parameter can be passed in order to
  validate the process, it’s kind of a CSRF check to avoid man in the middle
  attacks. Some don’t recognise it or don’t return it which will make the
  auth process invalid. Set this attribute to `False` in that case.

`REDIRECT_STATE`
: For those providers that don’t recognise the `state` parameter, the app
  can add a `redirect_state` argument to the `redirect_uri` to mimic it.
  Set this value to `False` if the provider likes to verify the
  `redirect_uri` value and this parameter invalidates that check.

Example code:

```default
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 '',
                'fullname': 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)
```

<a id="oauth2-with-pkce"></a>

### OAuth2 with PKCE

This is simply an extension of OAuth2 adding [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) which provides security against authorization code interception attack.

Use the `BaseOAuth2PKCE` class as a drop-in replacement for `BaseOAuth2` for implementing backends that support PKCE. For reference, you may refer to [Bitbucket Data Center OAuth2](https://github.com/python-social-auth/social-core/blob/master/social_core/backends/bitbucket_datacenter.py) and [Twitter OAuth2](https://github.com/python-social-auth/social-core/blob/master/social_core/backends/twitter_oauth2.py) as example implementations.

Only a single key attribute is needed on these backends:

`PKCE_DEFAULT_CODE_CHALLENGE_METHOD`
: Depends on which code challenge method is supported by the provider.
  The possible values for this are `s256` and `plain`.
  By default, `s256` is set.

<a id="oauth1"></a>

### OAuth1

OAuth1 process is a bit more trickier, [Twitter Docs](https://dev.twitter.com/docs/auth/implementing-sign-twitter) explains it quite well.
Besides the `AUTHORIZATION_URL` and `ACCESS_TOKEN_URL` attributes, a third
one is needed used when starting the process.

`REQUEST_TOKEN_URL = ''`
: During the auth process an unauthorized token is needed to start the
  process, later this token is exchanged for an `access_token`. This
  setting points to the API endpoint where that unauthorized token can be
  retrieved.

Example code:

```default
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"""
        return {'username': response['screen_name'],
                'email': response['email'],
                'fullname': response['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,
        }
```

<a id="openid"></a>

## OpenID

OpenID is far simpler than OAuth since it’s used for authentication rather
than authorization (regardless it’s used for authorization too).

A single attribute is usually needed, the authentication URL endpoint.

`URL = ''`
: OpenID endpoint where to redirect the user.

Sometimes the URL is user dependent, like in [myOpenID](https://www.myopenid.com/) 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.

Example code:

```default
from social_core.backends.open_id import OpenIdAuth
from social_core.exceptions import AuthInputError


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 AuthInputError(self, code="missing_parameter", parameter="openid_lj_user", stage="begin")
        return 'http://%s.livejournal.com' % self.data['openid_lj_user']
```

<a id="auth-apis"></a>

## Auth APIs

For others authentication types, a `BaseAuth` class is defined to help. Those
custom auth methods must override the `auth_url()` and `auth_complete()`
methods.

Example code:

```default
from google.appengine.api import users

from social_core.backends.base import BaseAuth
from social_core.exceptions import AuthUnknownError


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 AuthUnknownError(self, code="unknown_error", stage="callback")
        kwargs.update({'response': '', 'backend': self})
        return self.strategy.authenticate(*args, **kwargs)
```

<a id="common-backend-methods"></a>

## Common backend methods

All backends inherit from `BaseAuth` which provides several methods that can be
overridden to customize behavior. Here are some key methods:

`process_error(data, *, stage="callback")`
: Detects provider errors in callbacks and successful HTTP responses. OAuth2
  backends also call this hook during token exchange and refresh. Overrides
  must accept the keyword-only `stage` argument, pass it to the superclass,
  and use it when constructing structured exceptions. See [Exceptions](../exceptions.html.md).

`id_key()`
: Returns the ID key to use for this backend. By default, this method checks
  if the `ID_KEY` has been configured via settings (using
  `SOCIAL_AUTH_<BACKEND_NAME>_ID_KEY`) and returns that value if present,
  otherwise it falls back to the `ID_KEY` class attribute.
  <br/>
  Most backends should not need to override this method unless they have
  special logic for determining the ID key. Instead, use the
  `SOCIAL_AUTH_<BACKEND_NAME>_ID_KEY` setting to configure it.

`get_user_id(details, response)`
: Returns a unique ID for the current user from the provider’s response or
  from the details dict. This method uses `id_key()` to determine which
  field to extract from the response. The default implementation checks
  both `details` and `response` dicts for the configured ID key.
  <br/>
  Override this method if you need custom logic for extracting the user ID,
  such as reading a nested object, combining multiple fields, or performing
  transformations. Mapping-based overrides must use `id_key()` for the
  selectable leaf field while retaining any required scoping or validation.
  <br/>
  Example of custom user ID retrieval:
  <br/>
  ```default
  def get_user_id(self, details, response):
      """Custom user ID retrieval"""
      user_id = self.get_user_id_from_sources(response.get("user"))
      return f"{response['tenant']}:{user_id}"
  ```
  <br/>
  If a backend retains an older identifier selector, an explicitly configured
  `ID_KEY` should take precedence and the older setting should be used
  only when `ID_KEY` is unset.
  <br/>
  Protocol-derived identifiers do not always correspond to a mapping field.
  For example, generic OpenID and Steam use a validated identity URL, while
  SAML uses its per-IdP permanent-ID mapping. Such backends may intentionally
  ignore `ID_KEY`, but the behavior must be documented by the backend.

`get_user_details(response)`
: Extracts user details (username, email, first_name, last_name, fullname)
  from the provider’s API response. This method should return a dictionary
  with the extracted values. Return provider-supplied names without splitting
  or joining them; the `social_names` pipeline step performs
  [Name normalization](../pipeline.html.md#name-normalization). `BaseAuth.get_user_names()` is deprecated.
