authentik.stages.authenticator_static.models

Static Authenticator models

  1"""Static Authenticator models"""
  2
  3from base64 import b32encode
  4from os import urandom
  5
  6from django.conf import settings
  7from django.core.validators import MaxValueValidator
  8from django.db import models
  9from django.utils.translation import gettext_lazy as _
 10from django.views import View
 11from rest_framework.serializers import BaseSerializer
 12
 13from authentik.core.types import UserSettingSerializer
 14from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
 15from authentik.lib.models import SerializerModel
 16from authentik.stages.authenticator.models import Device, ThrottlingMixin
 17
 18
 19class AuthenticatorStaticStage(ConfigurableStage, FriendlyNamedStage, Stage):
 20    """Setup static token based authentication for the user."""
 21
 22    token_count = models.PositiveIntegerField(default=6)
 23    token_length = models.PositiveIntegerField(default=12, validators=[MaxValueValidator(100)])
 24
 25    @property
 26    def serializer(self) -> type[BaseSerializer]:
 27        from authentik.stages.authenticator_static.api import AuthenticatorStaticStageSerializer
 28
 29        return AuthenticatorStaticStageSerializer
 30
 31    @property
 32    def view(self) -> type[View]:
 33        from authentik.stages.authenticator_static.stage import AuthenticatorStaticStageView
 34
 35        return AuthenticatorStaticStageView
 36
 37    @property
 38    def component(self) -> str:
 39        return "ak-stage-authenticator-static-form"
 40
 41    def ui_user_settings(self) -> UserSettingSerializer | None:
 42        return UserSettingSerializer(
 43            data={
 44                "title": self.friendly_name or str(self._meta.verbose_name),
 45                "component": "ak-user-settings-authenticator-static",
 46            }
 47        )
 48
 49    def __str__(self) -> str:
 50        return f"Static Authenticator Setup Stage {self.name}"
 51
 52    class Meta:
 53        verbose_name = _("Static Authenticator Setup Stage")
 54        verbose_name_plural = _("Static Authenticator Setup Stages")
 55
 56
 57class StaticDevice(SerializerModel, ThrottlingMixin, Device):
 58    """
 59    A static :class:`~authentik.stages.authenticator.models.Device` simply consists of random
 60    tokens shared by the database and the user.
 61
 62    These are frequently used as emergency tokens in case a user's normal
 63    device is lost or unavailable. They can be consumed in any order; each
 64    token will be removed from the database as soon as it is used.
 65
 66    This model has no fields of its own, but serves as a container for
 67    :class:`StaticToken` objects.
 68
 69    .. attribute:: token_set
 70
 71        The RelatedManager for our tokens.
 72
 73    """
 74
 75    @property
 76    def serializer(self) -> type[BaseSerializer]:
 77        from authentik.stages.authenticator_static.api import StaticDeviceSerializer
 78
 79        return StaticDeviceSerializer
 80
 81    def get_throttle_factor(self):
 82        return getattr(settings, "OTP_STATIC_THROTTLE_FACTOR", 1)
 83
 84    def verify_token(self, token):
 85        verify_allowed, _ = self.verify_is_allowed()
 86        if verify_allowed:
 87            match = self.token_set.filter(token=token).first()
 88            if match is not None:
 89                match.delete()
 90                self.throttle_reset()
 91            else:
 92                self.throttle_increment()
 93        else:
 94            match = None
 95
 96        return match is not None
 97
 98    class Meta(Device.Meta):
 99        verbose_name = _("Static Device")
100        verbose_name_plural = _("Static Devices")
101
102
103class StaticToken(models.Model):
104    """
105    A single token belonging to a :class:`StaticDevice`.
106
107    .. attribute:: device
108
109        *ForeignKey*: A foreign key to :class:`StaticDevice`.
110
111    .. attribute:: token
112
113        *CharField*: A random string up to 100 characters.
114    """
115
116    device = models.ForeignKey(StaticDevice, related_name="token_set", on_delete=models.CASCADE)
117    token = models.CharField(max_length=100, db_index=True)
118
119    class Meta:
120        verbose_name = _("Static Token")
121        verbose_name_plural = _("Static Tokens")
122
123    def __str__(self) -> str:
124        return "Static Token"
125
126    @staticmethod
127    def random_token():
128        """
129        Returns a new random string that can be used as a static token.
130
131        :rtype: bytes
132
133        """
134        return b32encode(urandom(5)).decode("utf-8").lower()
20class AuthenticatorStaticStage(ConfigurableStage, FriendlyNamedStage, Stage):
21    """Setup static token based authentication for the user."""
22
23    token_count = models.PositiveIntegerField(default=6)
24    token_length = models.PositiveIntegerField(default=12, validators=[MaxValueValidator(100)])
25
26    @property
27    def serializer(self) -> type[BaseSerializer]:
28        from authentik.stages.authenticator_static.api import AuthenticatorStaticStageSerializer
29
30        return AuthenticatorStaticStageSerializer
31
32    @property
33    def view(self) -> type[View]:
34        from authentik.stages.authenticator_static.stage import AuthenticatorStaticStageView
35
36        return AuthenticatorStaticStageView
37
38    @property
39    def component(self) -> str:
40        return "ak-stage-authenticator-static-form"
41
42    def ui_user_settings(self) -> UserSettingSerializer | None:
43        return UserSettingSerializer(
44            data={
45                "title": self.friendly_name or str(self._meta.verbose_name),
46                "component": "ak-user-settings-authenticator-static",
47            }
48        )
49
50    def __str__(self) -> str:
51        return f"Static Authenticator Setup Stage {self.name}"
52
53    class Meta:
54        verbose_name = _("Static Authenticator Setup Stage")
55        verbose_name_plural = _("Static Authenticator Setup Stages")

Setup static token based authentication for the user.

def token_count(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def token_length(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

serializer: type[rest_framework.serializers.BaseSerializer]
26    @property
27    def serializer(self) -> type[BaseSerializer]:
28        from authentik.stages.authenticator_static.api import AuthenticatorStaticStageSerializer
29
30        return AuthenticatorStaticStageSerializer

Get serializer for this model

view: type[django.views.generic.base.View]
32    @property
33    def view(self) -> type[View]:
34        from authentik.stages.authenticator_static.stage import AuthenticatorStaticStageView
35
36        return AuthenticatorStaticStageView

Return StageView class that implements logic for this stage

component: str
38    @property
39    def component(self) -> str:
40        return "ak-stage-authenticator-static-form"

Return component used to edit this object

def ui_user_settings(self) -> authentik.core.types.UserSettingSerializer | None:
42    def ui_user_settings(self) -> UserSettingSerializer | None:
43        return UserSettingSerializer(
44            data={
45                "title": self.friendly_name or str(self._meta.verbose_name),
46                "component": "ak-user-settings-authenticator-static",
47            }
48        )

Entrypoint to integrate with User settings. Can either return None if no user settings are available, or a challenge.

configure_flow

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def friendly_name(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

configure_flow_id
stage_ptr_id
stage_ptr

Accessor to the related object on the forward side of a one-to-one relation.

In the example::

class Restaurant(Model):
    place = OneToOneField(Place, related_name='restaurant')

Restaurant.place is a ForwardOneToOneDescriptor instance.

class AuthenticatorStaticStage.DoesNotExist(authentik.flows.models.Stage.DoesNotExist):

The requested object does not exist

class AuthenticatorStaticStage.MultipleObjectsReturned(authentik.flows.models.Stage.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

 58class StaticDevice(SerializerModel, ThrottlingMixin, Device):
 59    """
 60    A static :class:`~authentik.stages.authenticator.models.Device` simply consists of random
 61    tokens shared by the database and the user.
 62
 63    These are frequently used as emergency tokens in case a user's normal
 64    device is lost or unavailable. They can be consumed in any order; each
 65    token will be removed from the database as soon as it is used.
 66
 67    This model has no fields of its own, but serves as a container for
 68    :class:`StaticToken` objects.
 69
 70    .. attribute:: token_set
 71
 72        The RelatedManager for our tokens.
 73
 74    """
 75
 76    @property
 77    def serializer(self) -> type[BaseSerializer]:
 78        from authentik.stages.authenticator_static.api import StaticDeviceSerializer
 79
 80        return StaticDeviceSerializer
 81
 82    def get_throttle_factor(self):
 83        return getattr(settings, "OTP_STATIC_THROTTLE_FACTOR", 1)
 84
 85    def verify_token(self, token):
 86        verify_allowed, _ = self.verify_is_allowed()
 87        if verify_allowed:
 88            match = self.token_set.filter(token=token).first()
 89            if match is not None:
 90                match.delete()
 91                self.throttle_reset()
 92            else:
 93                self.throttle_increment()
 94        else:
 95            match = None
 96
 97        return match is not None
 98
 99    class Meta(Device.Meta):
100        verbose_name = _("Static Device")
101        verbose_name_plural = _("Static Devices")

A static :class:~authentik.stages.authenticator.models.Device simply consists of random tokens shared by the database and the user.

These are frequently used as emergency tokens in case a user's normal device is lost or unavailable. They can be consumed in any order; each token will be removed from the database as soon as it is used.

This model has no fields of its own, but serves as a container for :class:StaticToken objects.

.. attribute:: token_set

The RelatedManager for our tokens.
serializer: type[rest_framework.serializers.BaseSerializer]
76    @property
77    def serializer(self) -> type[BaseSerializer]:
78        from authentik.stages.authenticator_static.api import StaticDeviceSerializer
79
80        return StaticDeviceSerializer

Get serializer for this model

def get_throttle_factor(self):
82    def get_throttle_factor(self):
83        return getattr(settings, "OTP_STATIC_THROTTLE_FACTOR", 1)

This must be implemented to return the throttle factor.

The number of seconds required between verification attempts will be :math:c2^{n-1} where c is this factor and n is the number of previous failures. A factor of 1 translates to delays of 1, 2, 4, 8, etc. seconds. A factor of 0 disables the throttling.

Normally this is just a wrapper for a plugin-specific setting like :setting:OTP_EMAIL_THROTTLE_FACTOR.

def verify_token(self, token):
85    def verify_token(self, token):
86        verify_allowed, _ = self.verify_is_allowed()
87        if verify_allowed:
88            match = self.token_set.filter(token=token).first()
89            if match is not None:
90                match.delete()
91                self.throttle_reset()
92            else:
93                self.throttle_increment()
94        else:
95            match = None
96
97        return match is not None

Verifies a token. As a rule, the token should no longer be valid if this returns True.

:param str token: The OTP token provided by the user. :rtype: bool

def throttling_failure_timestamp(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def throttling_failure_count(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

user

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def name(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def confirmed(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def last_used(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def created(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def last_updated(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def get_next_by_created(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_previous_by_created(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_next_by_last_updated(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

def get_previous_by_last_updated(unknown):

Method descriptor with partial application of the given arguments and keywords.

Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.

user_id
def id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

token_set

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

class StaticDevice.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class StaticDevice.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

class StaticToken(django.db.models.base.Model):
104class StaticToken(models.Model):
105    """
106    A single token belonging to a :class:`StaticDevice`.
107
108    .. attribute:: device
109
110        *ForeignKey*: A foreign key to :class:`StaticDevice`.
111
112    .. attribute:: token
113
114        *CharField*: A random string up to 100 characters.
115    """
116
117    device = models.ForeignKey(StaticDevice, related_name="token_set", on_delete=models.CASCADE)
118    token = models.CharField(max_length=100, db_index=True)
119
120    class Meta:
121        verbose_name = _("Static Token")
122        verbose_name_plural = _("Static Tokens")
123
124    def __str__(self) -> str:
125        return "Static Token"
126
127    @staticmethod
128    def random_token():
129        """
130        Returns a new random string that can be used as a static token.
131
132        :rtype: bytes
133
134        """
135        return b32encode(urandom(5)).decode("utf-8").lower()

A single token belonging to a :class:StaticDevice.

.. attribute:: device

*ForeignKey*: A foreign key to :class:`StaticDevice`.

.. attribute:: token

*CharField*: A random string up to 100 characters.
device

Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.

In the example::

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Child.parent is a ForwardManyToOneDescriptor instance.

def token(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

@staticmethod
def random_token():
127    @staticmethod
128    def random_token():
129        """
130        Returns a new random string that can be used as a static token.
131
132        :rtype: bytes
133
134        """
135        return b32encode(urandom(5)).decode("utf-8").lower()

Returns a new random string that can be used as a static token.

:rtype: bytes

device_id
def id(unknown):

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

def objects(unknown):

The type of the None singleton.

class StaticToken.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

class StaticToken.MultipleObjectsReturned(django.core.exceptions.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.