Further Customization for Drf-Social-Oauth2

This section is meant for further customization of the framework. Any idea is welcome and will be listed here.

Customize token expiration

To customize the expiration time for tokens, you can easily do so by adjusting the settings in your settings.py file.

Simply import the oauth2_provider settings and set the ACCESS_TOKEN_EXPIRE_SECONDS to your desired value, in seconds.

Here’s an example of how to set the expiration time to 6 months:

# in your settings.py file.
from oauth2_provider import settings as oauth2_settings

# expires in 6 months
oauth2_settings.DEFAULTS['ACCESS_TOKEN_EXPIRE_SECONDS'] = 1.577e7

By customizing the token expiration time, you can fine-tune the security and functionality of your application to suit your specific needs.

Refresh Token Rotation

Refresh token rotation is a security feature that issues a new refresh token each time a refresh token is used to obtain a new access token. This helps mitigate the risk of refresh token theft.

How it works:

  1. When a client uses a refresh token to get a new access token, a new refresh token is also issued.

  2. The old refresh token is invalidated and cannot be used again.

  3. If someone tries to reuse an old refresh token (indicating potential theft), all tokens in that “family” are revoked.

Configuration:

Add the following to your OAUTH2_PROVIDER settings in settings.py:

OAUTH2_PROVIDER = {
    # Enable refresh token rotation (default: True)
    'ROTATE_REFRESH_TOKEN': True,

    # Enable reuse protection - revokes all tokens if a used refresh token
    # is reused (default: True)
    'REFRESH_TOKEN_REUSE_PROTECTION': True,

    # Grace period in seconds - how long the old refresh token remains
    # valid after rotation to handle concurrent requests (default: 0)
    'REFRESH_TOKEN_GRACE_PERIOD_SECONDS': 30,

    # Refresh token lifetime in seconds (default: 14 days)
    'REFRESH_TOKEN_EXPIRE_SECONDS': 1209600,

    # Access token lifetime in seconds (default: 1 hour)
    'ACCESS_TOKEN_EXPIRE_SECONDS': 3600,
}

Settings explained:

  • ROTATE_REFRESH_TOKEN: When True, a new refresh token is issued each time a refresh token is used.

  • REFRESH_TOKEN_REUSE_PROTECTION: When True, if a refresh token that has already been used is used again, all refresh tokens for that user/application are revoked. This protects against token theft.

  • REFRESH_TOKEN_GRACE_PERIOD_SECONDS: The number of seconds the old refresh token remains valid after rotation. This helps handle race conditions when multiple requests use the same refresh token simultaneously.

  • REFRESH_TOKEN_EXPIRE_SECONDS: The absolute lifetime of refresh tokens in seconds.

Client-side considerations:

When refresh token rotation is enabled, your client application must:

  1. Store the new refresh token returned with each token refresh response.

  2. Replace the old refresh token with the new one.

  3. Handle the case where a refresh fails due to token reuse (re-authenticate the user).

Example refresh response:

{
    "access_token": "new_access_token_here",
    "refresh_token": "new_refresh_token_here",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "read write"
}

Security benefits:

  • Limits token lifetime: Even if a refresh token is stolen, it can only be used once.

  • Detects theft: If an attacker uses a stolen refresh token after the legitimate user has already used it, the reuse is detected and all tokens are revoked.

  • Reduces attack window: The grace period can be set to a small value to minimize the window of vulnerability.

Google Email Alias Normalization

Google treats @gmail.com and @googlemail.com as aliases of the same mailbox — the @googlemail.com form is still used in some regions (e.g. Germany, the UK). However, social_core’s default Google backends derive the social UID from the email address, so the same Google user signing in once with foo@gmail.com and once with foo@googlemail.com would otherwise produce two distinct UserSocialAuth records and two Django users.

drf-social-oauth2 normalizes @googlemail.com to @gmail.com before the UID is computed, so both forms map to the same Django user.

Automatic for GoogleIdentityBackend:

If you authenticate with drf_social_oauth2.backends.GoogleIdentityBackend (the OpenID Connect backend recommended in the Integrating Social Backends guide), normalization is applied automatically — no extra configuration is required.

Pipeline step for other Google backends:

If you use a stock python-social-auth Google backend such as social_core.backends.google.GoogleOAuth2, add the drf_social_oauth2.pipeline.normalize_google_email step to your SOCIAL_AUTH_PIPELINE, before social_core.pipeline.social_auth.social_uid:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    # Normalize @googlemail.com -> @gmail.com before the UID is computed.
    'drf_social_oauth2.pipeline.normalize_google_email',
    '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',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

The step is a no-op for non-Google backends (matched by a backend name starting with google) and for emails that don’t use the @googlemail.com alias, so it is safe to leave enabled in mixed-provider deployments.