|
| 1 | +.. _family_app: |
| 2 | + |
| 3 | +Family app |
| 4 | +=========== |
| 5 | + |
| 6 | +The **Token Family** system provides a way to group refresh and access tokens into logical families, |
| 7 | +allowing developers to track, manage, and invalidate related tokens as a unit. |
| 8 | + |
| 9 | +This feature is especially useful in enhancing security and traceability in authentication flows. |
| 10 | +Each token family is identified by a unique ``family_id``, which is included in the token payload. |
| 11 | +This enables the system to: |
| 12 | + |
| 13 | +- Detect and respond to refresh token reuse by invalidating the entire token family. |
| 14 | +- Revoke all related tokens at once. |
| 15 | +- Enforce expiration policies at the family level via a ``family_exp`` claim. |
| 16 | + |
| 17 | +A new token family is automatically created every time a user successfully obtains a pair of tokens |
| 18 | +from the ``TokenObtainPairView`` (i.e., when starting a new session). From that point onward, as long as the user |
| 19 | +continues to refresh their tokens, the newly issued access and refresh tokens will retain the same |
| 20 | +``family_id`` and ``family_exp`` values. This means all tokens issued as part of a session are |
| 21 | +considered to belong to the same token family. |
| 22 | + |
| 23 | +This session-based grouping allows administrators or systems to treat the token family as the unit of trust. |
| 24 | +If suspicious activity is detected, the entire session can be invalidated at once by blacklisting the |
| 25 | +associated token family. |
| 26 | + |
| 27 | +The Token Family system is optional and customizable. It works best when paired with the |
| 28 | +:doc:`/blacklist_app`. |
| 29 | + |
| 30 | +By organizing tokens into families, you gain finer control over user sessions and potential compromises. |
| 31 | +For example, when the settings ``BLACKLIST_AFTER_ROTATION`` and ``TOKEN_FAMILY_BLACKLIST_ON_REUSE`` are |
| 32 | +set to ``True``, if a refresh token is stolen and used by both the valid user and the attacker, |
| 33 | +the system will detect the reuse of the token and automatically blacklist the associated family. |
| 34 | +This invalidates every token that shares the same ``family_id`` as the reused refresh token, effectively |
| 35 | +cutting off access without waiting for individual token expiration. |
| 36 | + |
| 37 | +------- |
| 38 | + |
| 39 | +Simple JWT includes an app that provides token family functionality. To use |
| 40 | +this app, include it in your list of installed apps in ``settings.py``: |
| 41 | + |
| 42 | +.. code-block:: python |
| 43 | +
|
| 44 | + # Django project settings.py |
| 45 | +
|
| 46 | + ... |
| 47 | +
|
| 48 | + INSTALLED_APPS = ( |
| 49 | + ... |
| 50 | + 'rest_framework_simplejwt.token_family', |
| 51 | + ... |
| 52 | + ) |
| 53 | +
|
| 54 | +Also, make sure to run ``python manage.py migrate`` to run the app's |
| 55 | +migrations. |
| 56 | + |
| 57 | +If the token family app is detected in ``INSTALLED_APPS`` and the setting |
| 58 | +``TOKEN_FAMILY_ENABLED`` is set to ``True``, Simple JWT will add a new family |
| 59 | +to the family list and will also add two new claims, "family_id" and |
| 60 | +"family_exp", to the refresh tokens. It will also check that the |
| 61 | +family indicated in the token's payload does not appear in a blacklist of |
| 62 | +families before it considers it as valid, and it will also check that the |
| 63 | +family expiration date ("family_exp") has not passed; if the family is expired, |
| 64 | +then the token will be considered as invalid. |
| 65 | + |
| 66 | +The Simple JWT family app implements its family and blacklisted family |
| 67 | +lists using two models: ``TokenFamily`` and ``BlacklistedTokenFamily``. Model |
| 68 | +admins are defined for both of these models. To add a family to the blacklist, |
| 69 | +find its corresponding ``TokenFamily`` record in the admin and use the |
| 70 | +admin again to create a ``BlacklistedTokenFamily`` record that points to the |
| 71 | +``TokenFamily`` record. |
| 72 | + |
| 73 | +Alternatively, you can blacklist a family by creating a ``FamilyMixin`` |
| 74 | +subclass instance and calling the instance's ``blacklist_family`` method: |
| 75 | + |
| 76 | +.. code-block:: python |
| 77 | +
|
| 78 | + from rest_framework_simplejwt.tokens import RefreshToken |
| 79 | +
|
| 80 | + token = RefreshToken(base64_encoded_token_string) |
| 81 | + token.blacklist_family() |
| 82 | +
|
| 83 | +Keep in mind that the ``base64_encoded_token_string`` should already |
| 84 | +contain a family ID claim in its payload. |
| 85 | + |
| 86 | +This will create a unique family and blacklist records for the token's |
| 87 | +"family_id" claim or whichever claim is specified by the ``TOKEN_FAMILY_CLAIM`` setting. |
| 88 | + |
| 89 | + |
| 90 | +In a ``urls.py`` file, you can also include a route for ``TokenFamilyBlacklistView``: |
| 91 | + |
| 92 | +.. code-block:: python |
| 93 | +
|
| 94 | + from rest_framework_simplejwt.views import TokenFamilyBlacklistView |
| 95 | +
|
| 96 | + urlpatterns = [ |
| 97 | + ... |
| 98 | + path('api/token/family/blacklist/', TokenFamilyBlacklistView.as_view(), name='token_family_blacklist'), |
| 99 | + ... |
| 100 | + ] |
| 101 | +
|
| 102 | +It allows API users to blacklist token families sending them to ``/api/token/family/blacklist/``, for example using curl: |
| 103 | + |
| 104 | +.. code-block:: bash |
| 105 | +
|
| 106 | + curl \ |
| 107 | + -X POST \ |
| 108 | + -H "Content-Type: application/json" \ |
| 109 | + -d '{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTc0NzI0OTU1MywiaWF0IjoxNzQ3MjQ0MTUzLCJqdGkiOiI1YmMzMjlmMjVkODE0OGFhOTY1ODI1YjgwNDQ1ZDQ5OCIsInVzZXJfaWQiOjIsImZhbWlseV9pZCI6ImMyZGYyM2M1YjU1NjRmYjNhNTA3MjFhYzVkMTljNThmIiwiZmFtaWx5X2V4cCI6MTc0NzI0OTE1M30.4oDOmtkgot_W2mXByKuCyJLi6_xeMZtDQJmHIBXZx98"}' \ |
| 110 | + http://localhost:8000/api/token/family/blacklist/ |
| 111 | +
|
| 112 | +The family app also provides a management command, ``flushexpiredfamilies``, |
| 113 | +which will delete any families from the token family list and family blacklist that have |
| 114 | +expired. The command will not affect families that have ``None`` as their expiration. |
| 115 | +You should set up a cron job on your server or hosting platform which |
| 116 | +runs this command daily. |
0 commit comments