authentik.stages.authenticator
Authenticator devices helpers
1"""Authenticator devices helpers""" 2 3from typing import TYPE_CHECKING 4 5from django.db import transaction 6 7if TYPE_CHECKING: 8 from authentik.core.models import User 9 10 11def verify_token(user, device_id, token): 12 """ 13 Attempts to verify a :term:`token` against a specific device, identified by 14 :attr:`~authentik.stages.authenticator.models.Device.persistent_id`. 15 16 This wraps the verification process in a transaction to ensure that things 17 like throttling polices are properly enforced. 18 19 :param user: The user supplying the token. 20 :type user: :class:`~django.contrib.auth.models.User` 21 22 :param str device_id: A device's persistent_id value. 23 24 :param str token: An OTP token to verify. 25 26 :returns: The device that accepted ``token``, if any. 27 :rtype: :class:`~authentik.stages.authenticator.models.Device` or ``None`` 28 29 """ 30 from authentik.stages.authenticator.models import Device 31 32 verified = None 33 with transaction.atomic(): 34 device = Device.from_persistent_id(device_id, for_verify=True) 35 if (device is not None) and (device.user_id == user.pk) and device.verify_token(token): 36 verified = device 37 38 return verified 39 40 41def match_token(user, token): 42 """ 43 Attempts to verify a :term:`token` on every device attached to the given 44 user until one of them succeeds. 45 46 .. warning:: 47 48 This originally existed for more convenient integration with the admin 49 site. Its use is no longer recommended and it is not guaranteed to 50 interact well with more recent features (such as throttling). Tokens 51 should always be verified against specific devices. 52 53 :param user: The user supplying the token. 54 :type user: :class:`~django.contrib.auth.models.User` 55 56 :param str token: An OTP token to verify. 57 58 :returns: The device that accepted ``token``, if any. 59 :rtype: :class:`~authentik.stages.authenticator.models.Device` or ``None`` 60 """ 61 with transaction.atomic(): 62 for device in devices_for_user(user, for_verify=True): 63 if device.verify_token(token): 64 break 65 else: 66 device = None 67 68 return device 69 70 71def devices_for_user(user: User, confirmed: bool | None = True, for_verify: bool = False): 72 """ 73 Return an iterable of all devices registered to the given user. 74 75 Returns an empty iterable for anonymous users. 76 77 :param user: standard or custom user object. 78 :type user: :class:`~django.contrib.auth.models.User` 79 80 :param bool confirmed: If ``None``, all matching devices are returned. 81 Otherwise, this can be any true or false value to limit the query 82 to confirmed or unconfirmed devices, respectively. 83 84 :param bool for_verify: If ``True``, we'll load the devices with 85 :meth:`~django.db.models.query.QuerySet.select_for_update` to prevent 86 concurrent verifications from succeeding. In which case, this must be 87 called inside a transaction. 88 89 :rtype: iterable 90 """ 91 if user.is_anonymous: 92 return 93 94 for model in device_classes(): 95 device_set = model.objects.devices_for_user(user, confirmed=confirmed) 96 if for_verify: 97 device_set = device_set.select_for_update() 98 99 yield from device_set 100 101 102def user_has_device(user, confirmed=True): 103 """ 104 Return ``True`` if the user has at least one device. 105 106 Returns ``False`` for anonymous users. 107 108 :param user: standard or custom user object. 109 :type user: :class:`~django.contrib.auth.models.User` 110 111 :param confirmed: If ``None``, all matching devices are considered. 112 Otherwise, this can be any true or false value to limit the query 113 to confirmed or unconfirmed devices, respectively. 114 """ 115 try: 116 next(devices_for_user(user, confirmed=confirmed)) 117 except StopIteration: 118 has_device = False 119 else: 120 has_device = True 121 122 return has_device 123 124 125def device_classes(): 126 """ 127 Returns an iterable of all loaded device models. 128 """ 129 from django.apps import apps 130 131 from authentik.stages.authenticator.models import Device 132 133 for config in apps.get_app_configs(): 134 for model in config.get_models(): 135 if issubclass(model, Device): 136 yield model
12def verify_token(user, device_id, token): 13 """ 14 Attempts to verify a :term:`token` against a specific device, identified by 15 :attr:`~authentik.stages.authenticator.models.Device.persistent_id`. 16 17 This wraps the verification process in a transaction to ensure that things 18 like throttling polices are properly enforced. 19 20 :param user: The user supplying the token. 21 :type user: :class:`~django.contrib.auth.models.User` 22 23 :param str device_id: A device's persistent_id value. 24 25 :param str token: An OTP token to verify. 26 27 :returns: The device that accepted ``token``, if any. 28 :rtype: :class:`~authentik.stages.authenticator.models.Device` or ``None`` 29 30 """ 31 from authentik.stages.authenticator.models import Device 32 33 verified = None 34 with transaction.atomic(): 35 device = Device.from_persistent_id(device_id, for_verify=True) 36 if (device is not None) and (device.user_id == user.pk) and device.verify_token(token): 37 verified = device 38 39 return verified
Attempts to verify a :term:token against a specific device, identified by
:attr:~authentik.stages.authenticator.models.Device.persistent_id.
This wraps the verification process in a transaction to ensure that things like throttling polices are properly enforced.
:param user: The user supplying the token.
:type user: :class:~django.contrib.auth.models.User
:param str device_id: A device's persistent_id value.
:param str token: An OTP token to verify.
:returns: The device that accepted token, if any.
:rtype: :class:~authentik.stages.authenticator.models.Device or None
42def match_token(user, token): 43 """ 44 Attempts to verify a :term:`token` on every device attached to the given 45 user until one of them succeeds. 46 47 .. warning:: 48 49 This originally existed for more convenient integration with the admin 50 site. Its use is no longer recommended and it is not guaranteed to 51 interact well with more recent features (such as throttling). Tokens 52 should always be verified against specific devices. 53 54 :param user: The user supplying the token. 55 :type user: :class:`~django.contrib.auth.models.User` 56 57 :param str token: An OTP token to verify. 58 59 :returns: The device that accepted ``token``, if any. 60 :rtype: :class:`~authentik.stages.authenticator.models.Device` or ``None`` 61 """ 62 with transaction.atomic(): 63 for device in devices_for_user(user, for_verify=True): 64 if device.verify_token(token): 65 break 66 else: 67 device = None 68 69 return device
Attempts to verify a :term:token on every device attached to the given
user until one of them succeeds.
.. warning::
This originally existed for more convenient integration with the admin
site. Its use is no longer recommended and it is not guaranteed to
interact well with more recent features (such as throttling). Tokens
should always be verified against specific devices.
:param user: The user supplying the token.
:type user: :class:~django.contrib.auth.models.User
:param str token: An OTP token to verify.
:returns: The device that accepted token, if any.
:rtype: :class:~authentik.stages.authenticator.models.Device or None
72def devices_for_user(user: User, confirmed: bool | None = True, for_verify: bool = False): 73 """ 74 Return an iterable of all devices registered to the given user. 75 76 Returns an empty iterable for anonymous users. 77 78 :param user: standard or custom user object. 79 :type user: :class:`~django.contrib.auth.models.User` 80 81 :param bool confirmed: If ``None``, all matching devices are returned. 82 Otherwise, this can be any true or false value to limit the query 83 to confirmed or unconfirmed devices, respectively. 84 85 :param bool for_verify: If ``True``, we'll load the devices with 86 :meth:`~django.db.models.query.QuerySet.select_for_update` to prevent 87 concurrent verifications from succeeding. In which case, this must be 88 called inside a transaction. 89 90 :rtype: iterable 91 """ 92 if user.is_anonymous: 93 return 94 95 for model in device_classes(): 96 device_set = model.objects.devices_for_user(user, confirmed=confirmed) 97 if for_verify: 98 device_set = device_set.select_for_update() 99 100 yield from device_set
Return an iterable of all devices registered to the given user.
Returns an empty iterable for anonymous users.
:param user: standard or custom user object.
:type user: :class:~django.contrib.auth.models.User
:param bool confirmed: If None, all matching devices are returned.
Otherwise, this can be any true or false value to limit the query
to confirmed or unconfirmed devices, respectively.
:param bool for_verify: If True, we'll load the devices with
:meth:~django.db.models.query.QuerySet.select_for_update to prevent
concurrent verifications from succeeding. In which case, this must be
called inside a transaction.
:rtype: iterable
103def user_has_device(user, confirmed=True): 104 """ 105 Return ``True`` if the user has at least one device. 106 107 Returns ``False`` for anonymous users. 108 109 :param user: standard or custom user object. 110 :type user: :class:`~django.contrib.auth.models.User` 111 112 :param confirmed: If ``None``, all matching devices are considered. 113 Otherwise, this can be any true or false value to limit the query 114 to confirmed or unconfirmed devices, respectively. 115 """ 116 try: 117 next(devices_for_user(user, confirmed=confirmed)) 118 except StopIteration: 119 has_device = False 120 else: 121 has_device = True 122 123 return has_device
Return True if the user has at least one device.
Returns False for anonymous users.
:param user: standard or custom user object.
:type user: :class:~django.contrib.auth.models.User
:param confirmed: If None, all matching devices are considered.
Otherwise, this can be any true or false value to limit the query
to confirmed or unconfirmed devices, respectively.
126def device_classes(): 127 """ 128 Returns an iterable of all loaded device models. 129 """ 130 from django.apps import apps 131 132 from authentik.stages.authenticator.models import Device 133 134 for config in apps.get_app_configs(): 135 for model in config.get_models(): 136 if issubclass(model, Device): 137 yield model
Returns an iterable of all loaded device models.