<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="user-details"></a>

### User details

`get_user_details()` returns provider-supplied names under `fullname`,
`first_name`, and `last_name`. Omit unavailable names or return `None`;
do not use empty strings as placeholders for missing information. Preserve
strings supplied by the provider, including explicitly empty strings.

The [Name normalization](../pipeline.html.md#name-normalization) pipeline step fills missing or blank name
representations when a nonempty value can be derived. It preserves unavailable
components when derivation produces nothing. Leave this conversion to the
pipeline rather than performing it in the backend.

During profile updates, omitted keys and `None` preserve existing user
fields, while empty strings can clear them. Custom pipelines consuming raw
backend details should handle `None` for unavailable names. During user
creation, unavailable configured name fields are omitted so the user model
can supply its defaults.

<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()}

    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_legacy_user_identifiers(details, response)`
: Returns previous `(id_key, uid)` pairs from built-in `LEGACY_ID_KEYS` and
  backend-scoped configuration. Missing claims are omitted; other provider
  response errors propagate. Override this hook when historical identifiers
  need special extraction or scoping. `get_legacy_user_ids()` remains a
  compatibility wrapper returning values only. The pipeline invokes both
  hooks and uses additional values from existing custom overrides to find
  empty-key associations, subject to the same evidence requirements. Prefer
  the keyed hook for new overrides and associations with recorded keys.

`get_stored_user_id_keys(id_key)`
: Returns stored extra-data fields containing evidence for the current
  identifier. The default returns `(id_key,)`. Historical OIDC subjects may
  also be stored under `id`. Only declare aliases known to represent the
  same identifier; every present evidence field must agree.

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