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::