diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 4f8de2cf..ca0561a5 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,30 +418,118 @@ compatibility. The historical ``SOCIAL_AUTH_JSONFIELD_ENABLED``, settings are only relevant while running those legacy migrations. +.. _django-exception-middleware: + 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 + + from social_core.exceptions import AuthCanceled + from social_django.middleware import SocialAuthExceptionMiddleware -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. + 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 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 ^^^^^^^^^^^^^^^^ @@ -571,15 +679,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