diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index d9a0cba6..78c68438 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -164,6 +164,55 @@ Django user. It is not a separate authentication user model. By default, ``UserSocialAuth.user`` references ``settings.AUTH_USER_MODEL``, so social login and Django's username/password login use the same user model. +.. _django-manual-disconnection: + +Disconnecting social accounts in code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +With the default Django ORM storage, you can remove social account associations +directly through the user's ``social_auth`` related manager. Given a saved +Django ``user`` instance:: + + # Remove all social account associations for this user. + user.social_auth.all().delete() + + # Remove only this user's GitHub associations. + user.social_auth.filter(provider="github").delete() + +These operations preserve the Django user and application data related only +to that user. Application models that reference ``UserSocialAuth`` follow +their own relationship's ``on_delete`` behavior: ``CASCADE`` deletes dependent +rows, while ``PROTECT`` or ``RESTRICT`` can prevent deletion. Check those +relationships before removing associations. + +To remove a single association using the storage API:: + + from social_django.models import UserSocialAuth + + association = user.social_auth.get(pk=association_id) + UserSocialAuth.disconnect(association) + +Direct deletion bypasses the :ref:`disconnection-pipeline`, including the check +that the user has another login method, optional provider token revocation, +and any custom pipeline steps. Use it when your application intentionally +handles these responsibilities itself. For user-facing disconnection, use the +normal disconnection view so the configured pipeline runs. + +Deleting a Django user +^^^^^^^^^^^^^^^^^^^^^^ + +The ``UserSocialAuth.user`` foreign key uses ``on_delete=models.CASCADE``. +Deleting a user through Django's ORM, for example with ``user.delete()``, +automatically deletes their social account associations. You do not need to +disconnect those associations in a user-deletion hook first. Cascading deletion +does not run the disconnection pipeline or revoke provider tokens. + +Other application models that reference the user follow their own deletion +rules. If user deletion raises a foreign key error naming another application's +table, inspect that model's relationship, migrations, and deletion code. +Removing social account associations does not remove references from unrelated +tables. + Custom authentication user models ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/logging_out.rst b/docs/logging_out.rst index d0b50f95..39a20624 100644 --- a/docs/logging_out.rst +++ b/docs/logging_out.rst @@ -27,5 +27,8 @@ check that the user account is in a valid state for disconnection (it has at least one more social account associated, or a password, etc). This behavior can be overridden by changing the `Disconnection Pipeline`_. +For Django code that removes associations directly, and for user deletion, +see :ref:`django-manual-disconnection`. + .. _logout_user(): https://github.com/maxcountryman/flask-login/blob/a96de342eae560deec008a02179f593c3799b3ba/flask_login.py#L718-L739 .. _Disconnection Pipeline: pipeline.html#disconnection-pipeline diff --git a/docs/pipeline.rst b/docs/pipeline.rst index 60eaabee..d1699575 100644 --- a/docs/pipeline.rst +++ b/docs/pipeline.rst @@ -272,6 +272,8 @@ such as ``SOCIAL_AUTH_TWITTER_PIPELINE``. Backend-specific pipelines will overri the default and ``SOCIAL_AUTH_PIPELINE`` settings. +.. _disconnection-pipeline: + Disconnection Pipeline ---------------------- diff --git a/docs/storage.rst b/docs/storage.rst index 82077173..e17e01b1 100644 --- a/docs/storage.rst +++ b/docs/storage.rst @@ -101,8 +101,8 @@ an arbitrary row when stored provider data matches multiple associations. raise NotImplementedError('Implement in subclass') @classmethod - def disconnect(cls, name, user, association_id=None): - """Disconnect the social account for the given user""" + def disconnect(cls, entry): + """Remove the given social account association""" raise NotImplementedError('Implement in subclass')