From 13da8299a1a834a0430a2bf2c392161ca6f401d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 6 Oct 2026 14:21:14 +0200 Subject: [PATCH] docs(oidc): explain nonce lifetime and storage cleanup Explain how to configure bounded login attempts and schedule cleanup so abandoned authentication records do not accumulate. Document the legacy grace period and coordinated upgrade requirements for storage integrations. Refs python-social-auth/social-app-django#918 --- docs/backends/oidc.rst | 25 +++++++++++++++++++++++++ docs/configuration/django.rst | 23 +++++++++++++++++++++++ docs/storage.rst | 24 ++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/docs/backends/oidc.rst b/docs/backends/oidc.rst index 32473e09..f1eef6b4 100644 --- a/docs/backends/oidc.rst +++ b/docs/backends/oidc.rst @@ -31,6 +31,31 @@ The remaining configuration will be auto-detected, by fetching:: This class can be used standalone, but is also the base class for some other backends. +Nonce lifetime +-------------- + +Each login attempt stores a one-use nonce with its creation time and lifetime. +The nonce expires after 30 minutes by default. Configure a different positive +integer duration in seconds with:: + + SOCIAL_AUTH_OIDC_NONCE_LIFETIME = 1800 + +For other backends inheriting from this class, replace ``OIDC`` with the backend's +settings prefix. This duration bounds the time from starting authentication to +validating the ID token; it is independent of ``ID_TOKEN_MAX_AGE``, which limits +the age of the ID token itself. An expired nonce rejects the login, so the user +must start again. Successful validation consumes the nonce. Refresh requests +and resumed partial pipelines do not reuse it. + +LinkedIn OpenID Connect does not create or send nonces because its backend does +not validate them. Custom subclasses can set the class attribute ``USE_NONCE`` +to ``False`` when their protocol-specific validation does not use server-created +nonces; this is not a deployment setting. + +Failed and abandoned attempts can leave nonce records behind. Django deployments +should regularly run ``manage.py clearsocial``; see +:doc:`/configuration/django` for cleanup and upgrade guidance. + IdP Setup --------- diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 4589178e..74c3b1d2 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -1120,3 +1120,26 @@ to the user model by definig the following setting:: .. _django@dc43fbc: https://github.com/django/django/commit/dc43fbc2f21c12e34e309d0e8a121020391aa03a .. _SOUTH_MIGRATION_MODULES: http://south.readthedocs.org/en/latest/settings.html#south-migration-modules .. _pypi: http://pypi.python.org/pypi/social-auth-app-django/ + + +Authentication storage cleanup +------------------------------ + +Schedule ``manage.py clearsocial`` regularly, for example hourly. It removes +unused verification codes and partial pipelines older than ``--age`` days +(default: 14), and expired OpenID associations and OpenID Connect nonces. +Association expiry uses each record's ``issued`` timestamp and ``lifetime``; +``--age`` does not change this policy. Active associations and linked user +accounts are preserved. + +Applications with their own scheduled tasks can call +``social_django.models.Association.cleanup_expired()`` directly. Expiry is +checked during OIDC login validation even when scheduled cleanup has not run. + +When upgrading to nonce lifetime enforcement, stop old login-serving processes, +apply the social-auth-app-django migrations, and start processes with the +coordinated social-auth-core and social-auth-app-django versions. The migration +gives existing OIDC nonce records with an empty secret, ``issued=0``, and +``lifetime=0`` a 30-minute grace period. After that period, validation rejects +them and the next cleanup removes them. Other storage integrations need their +own equivalent upgrade conversion; see :doc:`/storage`. diff --git a/docs/storage.rst b/docs/storage.rst index f7d8b014..82077173 100644 --- a/docs/storage.rst +++ b/docs/storage.rst @@ -134,8 +134,28 @@ the needed methods:: Association ----------- -Another OpenID helper class, it stores basic data to keep the OpenID -association. Like Nonce_ this is for internal use only. +This internal model stores OpenID protocol associations and temporary OpenID +Connect login nonces. It is separate from the model linking a user to a provider. +For OIDC, ``handle`` stores the nonce, ``secret`` is empty, and ``assoc_type`` +stores OAuth state. ``issued`` is the integer Unix creation timestamp and +``lifetime`` is the duration in seconds. + +``AssociationMixin.is_expired(now=None)`` returns whether the lifetime has +elapsed, including the exact expiry boundary. Nonpositive lifetimes are expired. +OIDC validation removes matching expired records and rejects the login. + +Storage integrations must preserve ``issued`` and ``lifetime``. During upgrade, +existing OIDC nonce records with an empty secret and both values zero can receive +the upgrade timestamp and a 1,800-second grace period. Other unbounded records +are rejected. Stop old login-serving processes before this conversion so they +do not create additional undated nonces afterward. + +Django provides ``Association.cleanup_expired(now=None)``, which deletes expired +OpenID associations and OIDC nonces and returns the deleted record count. +``now`` is an optional integer Unix timestamp; it defaults to the current time. +Other integrations should implement scheduled cleanup using the same expiry +rule. OpenID's existing on-demand cleanup only visits associations for the +provider being queried. When implementing this model, it must inherits from AssociationMixin_, and override the needed methods::