From 6f2c76ea13aea6a49a6c8bae3359725308916338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Mon, 5 Oct 2026 15:05:54 +0200 Subject: [PATCH 1/3] docs(auth): explain fallback error pages and customization Help applications handle authentication failures without an error redirect and understand the available status, template, and reporting overrides. Clarify setting precedence and LOGIN_URL fallback behavior to avoid misconfiguring exception handling. Refs python-social-auth/social-app-django#208 --- docs/configuration/django.rst | 130 ++++++++++++++++++++++++++------ docs/configuration/settings.rst | 10 ++- 2 files changed, 116 insertions(+), 24 deletions(-) diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 4f8de2cf..8d1e3fa9 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -401,27 +401,110 @@ settings are only relevant while running those legacy migrations. Exceptions Middleware --------------------- -A base middleware is provided that handles ``SocialAuthBaseException`` by -providing an error message to the user via configured transport mechanisms (Django -messages framework, redirect URL query parameters, or both), and then -responding with a redirect to a URL defined in one of the middleware methods. +A base middleware handles ``SocialAuthBaseException`` by redirecting to a +configured error URL or rendering an error page. It supports both synchronous +and asynchronous Django request handlers. Add it to ``MIDDLEWARE``, after your +session, authentication, and message middleware: -The middleware is at ``social_django.middleware.SocialAuthExceptionMiddleware``. -Any method can be overridden, but for simplicity these two are recommended: +.. code-block:: python + + MIDDLEWARE = [ + # ... + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'social_django.middleware.SocialAuthExceptionMiddleware', + ] + +To redirect failures to an application error page, configure: .. code-block:: python - get_message(request, exception) - get_redirect_uri(request, exception) + SOCIAL_AUTH_LOGIN_ERROR_URL = '/login-error/' + SOCIAL_AUTH_RAISE_EXCEPTIONS = False + +The redirect uses the configured error transports described below. The default +message is the safe exception message. A backend attached by the ``psa()`` +decorator is available at ``request.backend``; backend-specific settings take +precedence over global settings. + +Without this middleware, Social Auth exceptions are left to Django's exception +handling and can produce an HTTP 500 response. + +Fallback error page +^^^^^^^^^^^^^^^^^^^ + +When ``SOCIAL_AUTH_LOGIN_ERROR_URL`` is unset, ``None``, or an empty string, the +middleware renders ``social_django/error.html`` directly. It does not use flash +messages or query parameters, so the page works even when session cookies are +unavailable. Explicit exception propagation still takes precedence, as described +under exception raising below. + +The response status depends on the failure: + +.. list-table:: + :header-rows: 1 + + * - Exception family or reason + - HTTP status + * - ``AuthInputError`` + - 400 + * - ``AuthSessionError``, ``AuthCredentialError``, ``AuthPolicyError``, ``AuthCanceled`` + - 403 + * - ``AuthAssociationError`` + - 409 + * - ``AuthResponseError`` + - 502 + * - ``AuthProviderError``: connection, unavailability, rate limit, or custom codes + - 503 + * - ``AuthProviderError``: ``timeout`` + - 504 + * - ``AuthProviderError``: ``tls_error`` or ``http_error`` + - 502 + * - Configuration errors, unknown errors, or other base exceptions + - 500 + +The reason codes ``response_expired`` and ``nonce_mismatch`` override the family +status with 403; ``invalid_expiry`` overrides it with 500. Other custom codes +inherit the family's status. Provider HTTP statuses are not forwarded directly. + +The bundled page shows the safe message and guidance selected from the suggested +recovery action. Only ``session_context_missing`` adds a hint about session expiry, +cookies, and restarting login in the same browser and container. These are possible +causes, not a diagnosis. The page does not automatically retry authentication. + +Override ``social_django/error.html`` in your application's templates to customize +its presentation. The middleware supplies ``message``, ``error_code``, +``error_source``, ``error_stage``, and ``error_recovery``; it does not supply the raw +exception, provider diagnostics, or identifying context. Normal Django template +context processors still apply. The response includes headers preventing caching. + +You can also subclass the middleware and replace its ``MIDDLEWARE`` entry with +your subclass. The following methods accept ``request`` and ``exception``: + +* ``get_message()`` customizes the message for redirects and rendered pages. +* ``get_redirect_uri()`` selects the error redirect URL. +* ``get_error_status()`` selects the rendered response's HTTP status. +* ``render_error()`` customizes the rendered response and its reporting. + +For example, an application can change the status used for explicit cancellation: -By default, the message is the exception message and the URL for the redirect -is the location specified by the ``LOGIN_ERROR_URL`` setting. The middleware -supports both synchronous and asynchronous Django request handlers. +.. code-block:: python -If a valid backend was detected by ``strategy()`` decorator, it will be -available at ``request.strategy.backend`` and ``process_exception()`` will -use it to build a backend-dependent redirect URL but fallback to default if not -defined. + from social_core.exceptions import AuthCanceled + from social_django.middleware import SocialAuthExceptionMiddleware + + class CustomExceptionMiddleware(SocialAuthExceptionMiddleware): + def get_error_status(self, request, exception): + if isinstance(exception, AuthCanceled): + return 400 + return super().get_error_status(request, exception) + +Rendered 4xx failures are logged at warning level and 5xx failures at error level +using safe classification fields. Rendering a 500 handles the exception instead +of propagating it, so exception-based monitoring may no longer receive it. +Configure monitoring for the logs or override ``render_error()`` to integrate your +reporting. Unrelated exceptions still propagate through Django normally. Error Transports ^^^^^^^^^^^^^^^^ @@ -571,15 +654,18 @@ different authentication providers, such as showing a custom error page for cert providers or raising exceptions for debugging specific backends while keeping others in production mode. -Exception processing is disabled if any of these settings is defined with a -``True`` value: +Exception processing is disabled when the effective ``RAISE_EXCEPTIONS`` +setting is true. Settings are checked in this order, and the first configured +value wins: -.. code-block:: python +1. ``SOCIAL_AUTH__RAISE_EXCEPTIONS`` (uppercase backend name, with + hyphens replaced by underscores). +2. ``SOCIAL_AUTH_RAISE_EXCEPTIONS``. +3. ``RAISE_EXCEPTIONS``. +4. ``DEBUG`` as the default when none of these settings is configured. - _SOCIAL_AUTH_RAISE_EXCEPTIONS = True - SOCIAL_AUTH_RAISE_EXCEPTIONS = True - RAISE_EXCEPTIONS = True - DEBUG = True +For example, ``SOCIAL_AUTH_RAISE_EXCEPTIONS = False`` enables handling even with +``DEBUG = True``; a backend-specific true value overrides that global false value. Structured authentication errors diff --git a/docs/configuration/settings.rst b/docs/configuration/settings.rst index 144fcf29..7d9db220 100644 --- a/docs/configuration/settings.rst +++ b/docs/configuration/settings.rst @@ -68,10 +68,16 @@ results and others for error situations. value of the ``next`` request parameter is used if it was present ``SOCIAL_AUTH_LOGIN_ERROR_URL = '/login-error/'`` - URL where the user will be redirected in case of an error + URL where the user will be redirected in case of an error. With Django's + ``SocialAuthExceptionMiddleware``, leaving this unset or empty renders the + bundled error page instead. See :doc:`django` for status mappings and + customization. The Django exception middleware does not fall back to + ``SOCIAL_AUTH_LOGIN_URL``. ``SOCIAL_AUTH_LOGIN_URL = '/login-url/'`` - Is used as a fallback for ``LOGIN_ERROR_URL`` + Fallback login URL used by authentication actions when a more specific + redirect URL is unavailable. It is not a fallback for Django's exception + middleware. ``SOCIAL_AUTH_NEW_USER_REDIRECT_URL = '/new-users-redirect-url/'`` Used to redirect new registered users, will be used in place of From e880489925523b1f2842ddb4f3bbeec6283c6395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Mon, 5 Oct 2026 15:10:02 +0200 Subject: [PATCH 2/3] docs(django): recommend exception middleware in quickstart Include error handling in the standard setup so expired sessions and canceled logins show useful recovery guidance. Explain the default debug behavior and link to fallback page customization. Refs python-social-auth/social-app-django#208 --- docs/configuration/django.rst | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 8d1e3fa9..546602d8 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -34,14 +34,34 @@ working in your Django project. 'social_django', ) -**2. Configure authentication backends** (example for Google OAuth2):: +**2. Add the exception middleware**: + +Add ``SocialAuthExceptionMiddleware`` to your existing ``MIDDLEWARE`` list, +after the session, authentication, and message middleware:: + + MIDDLEWARE = [ + ... + 'social_django.middleware.SocialAuthExceptionMiddleware', + ] + +This is recommended so expected authentication failures, such as an expired +login session or declined authorization, show a useful error page instead of +an HTTP 500 response. No error URL is required; configure +``SOCIAL_AUTH_LOGIN_ERROR_URL`` if you prefer a redirect to your own error page. +See :ref:`django-exception-middleware` for customization and reporting behavior. + +With ``DEBUG = True``, exceptions propagate by default to aid debugging. Set +``SOCIAL_AUTH_RAISE_EXCEPTIONS = False`` to preview the error page during local +development. + +**3. Configure authentication backends** (example for Google OAuth2):: AUTHENTICATION_BACKENDS = ( 'social_core.backends.google.GoogleOAuth2', 'django.contrib.auth.backends.ModelBackend', # Keep for username/password login ) -**3. Add OAuth credentials to settings.py**: +**4. Add OAuth credentials to settings.py**: This is where you configure your ``client_id``, ``client_secret``, and ``scope`` for each provider:: @@ -64,20 +84,20 @@ For other providers, the pattern is ``SOCIAL_AUTH__KEY``, SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = os.environ.get('GOOGLE_OAUTH2_KEY') SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = os.environ.get('GOOGLE_OAUTH2_SECRET') -**4. Add URLs to urls.py**:: +**5. Add URLs to urls.py**:: urlpatterns = [ ... path('', include('social_django.urls', namespace='social')), ] -**5. Configure redirect URLs**:: +**6. Configure redirect URLs**:: LOGIN_URL = '/login/' LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/' -**6. Run migrations**:: +**7. Run migrations**:: python manage.py migrate @@ -87,7 +107,7 @@ command. The Django migration adds a blank ``id_key`` to existing social associations; social-core then migrates those rows according to the policy in :ref:`the configurable user ID key documentation `. -**7. Add login form in template**:: +**8. Add login form in template**::
{% csrf_token %} @@ -398,6 +418,8 @@ compatibility. The historical ``SOCIAL_AUTH_JSONFIELD_ENABLED``, settings are only relevant while running those legacy migrations. +.. _django-exception-middleware: + Exceptions Middleware --------------------- From efac78dc5954f9662f7da0f0bc271eaac7710c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Mon, 5 Oct 2026 15:25:20 +0200 Subject: [PATCH 3/3] docs(auth): document fallback traceback reporting Clarify that rendered server errors preserve their original tracebacks in logs so integrations can configure monitoring without relying on exception propagation. Expected client failures retain metadata-only warning logs. Refs python-social-auth/social-app-django#1131 --- docs/configuration/django.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 546602d8..ca0561a5 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -522,11 +522,14 @@ For example, an application can change the status used for explicit cancellation return 400 return super().get_error_status(request, exception) -Rendered 4xx failures are logged at warning level and 5xx failures at error level -using safe classification fields. Rendering a 500 handles the exception instead -of propagating it, so exception-based monitoring may no longer receive it. -Configure monitoring for the logs or override ``render_error()`` to integrate your -reporting. Unrelated exceptions still propagate through Django normally. +Rendered 4xx failures are logged at warning level using safe classification +fields, without tracebacks. Rendered 5xx failures are logged at error level with +the original exception and traceback so server-side defects remain diagnosable. +Tracebacks are included only in server logs, not in the rendered page. +Rendering a 500 handles the exception instead of propagating it, so +exception-based monitoring may no longer receive it. Configure monitoring for +the logs or override ``render_error()`` to integrate your reporting. Unrelated +exceptions still propagate through Django normally. Error Transports ^^^^^^^^^^^^^^^^