authentik.flows.models

Flow models

  1"""Flow models"""
  2
  3from base64 import b64decode, b64encode
  4from pickle import dumps, loads  # nosec
  5from typing import TYPE_CHECKING
  6from uuid import uuid4
  7
  8from django.core.validators import validate_slug
  9from django.db import models
 10from django.http import HttpRequest
 11from django.utils.translation import gettext_lazy as _
 12from model_utils.managers import InheritanceManager
 13from rest_framework.serializers import BaseSerializer
 14from structlog.stdlib import get_logger
 15
 16from authentik.admin.files.fields import FileField
 17from authentik.admin.files.manager import get_file_manager
 18from authentik.admin.files.usage import FileUsage
 19from authentik.core.models import Token
 20from authentik.core.types import UserSettingSerializer
 21from authentik.flows.challenge import FlowLayout
 22from authentik.lib.config import CONFIG
 23from authentik.lib.models import InheritanceForeignKey, InternallyManagedMixin, SerializerModel
 24from authentik.lib.utils.reflection import class_to_path
 25from authentik.policies.models import PolicyBindingModel
 26
 27if TYPE_CHECKING:
 28    from authentik.flows.planner import FlowPlan
 29    from authentik.flows.stage import StageView
 30
 31LOGGER = get_logger()
 32
 33
 34class FlowAuthenticationRequirement(models.TextChoices):
 35    """Required level of authentication and authorization to access a flow"""
 36
 37    NONE = "none"
 38    REQUIRE_AUTHENTICATED = "require_authenticated"
 39    REQUIRE_UNAUTHENTICATED = "require_unauthenticated"
 40    REQUIRE_SUPERUSER = "require_superuser"
 41    REQUIRE_REDIRECT = "require_redirect"
 42    REQUIRE_OUTPOST = "require_outpost"
 43
 44
 45class NotConfiguredAction(models.TextChoices):
 46    """Decides how the FlowExecutor should proceed when a stage isn't configured"""
 47
 48    SKIP = "skip"
 49    DENY = "deny"
 50    CONFIGURE = "configure"
 51
 52
 53class InvalidResponseAction(models.TextChoices):
 54    """Configure how the flow executor should handle invalid responses to challenges"""
 55
 56    RETRY = "retry"
 57    RESTART = "restart"
 58    RESTART_WITH_CONTEXT = "restart_with_context"
 59
 60
 61class FlowDeniedAction(models.TextChoices):
 62    """Configure what response is given to denied flow executions"""
 63
 64    MESSAGE_CONTINUE = "message_continue"
 65    MESSAGE = "message"
 66    CONTINUE = "continue"
 67
 68
 69class FlowDesignation(models.TextChoices):
 70    """Designation of what a Flow should be used for. At a later point, this
 71    should be replaced by a database entry."""
 72
 73    AUTHENTICATION = "authentication"
 74    AUTHORIZATION = "authorization"
 75    INVALIDATION = "invalidation"
 76    ENROLLMENT = "enrollment"
 77    UNRENOLLMENT = "unenrollment"
 78    RECOVERY = "recovery"
 79    STAGE_CONFIGURATION = "stage_configuration"
 80
 81
 82class Stage(SerializerModel):
 83    """Stage is an instance of a component used in a flow. This can verify the user,
 84    enroll the user or offer a way of recovery"""
 85
 86    stage_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 87
 88    name = models.TextField(unique=True)
 89
 90    objects = InheritanceManager()
 91
 92    @property
 93    def view(self) -> type[StageView]:
 94        """Return StageView class that implements logic for this stage"""
 95        # This is a bit of a workaround, since we can't set class methods with setattr
 96        if hasattr(self, "__in_memory_type"):
 97            return getattr(self, "__in_memory_type")
 98        raise NotImplementedError
 99
100    @property
101    def component(self) -> str:
102        """Return component used to edit this object"""
103        raise NotImplementedError
104
105    def ui_user_settings(self) -> UserSettingSerializer | None:
106        """Entrypoint to integrate with User settings. Can either return None if no
107        user settings are available, or a challenge."""
108        return None
109
110    @property
111    def is_in_memory(self):
112        return hasattr(self, "__in_memory_type")
113
114    def __str__(self):
115        if self.is_in_memory:
116            return f"In-memory Stage {getattr(self, '__in_memory_type')}"
117        return f"Stage {self.name}"
118
119
120def in_memory_stage(view: type[StageView], **kwargs) -> Stage:
121    """Creates an in-memory stage instance, based on a `view` as view.
122    Any key-word arguments are set as attributes on the stage object,
123    accessible via `self.executor.current_stage`."""
124    stage = Stage()
125    # Because we can't pickle a locally generated function,
126    # we set the view as a separate property and reference a generic function
127    # that returns that member
128    setattr(stage, "__in_memory_type", view)
129    stage.name = _("Dynamic In-memory stage: {doc}".format_map({"doc": view.__doc__}))
130    stage._meta.verbose_name = class_to_path(view)
131    for key, value in kwargs.items():
132        setattr(stage, key, value)
133    return stage
134
135
136class Flow(SerializerModel, PolicyBindingModel):
137    """Flow describes how a series of Stages should be executed to authenticate/enroll/recover
138    a user. Additionally, policies can be applied, to specify which users
139    have access to this flow."""
140
141    flow_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
142
143    name = models.TextField()
144    slug = models.TextField(
145        validators=[validate_slug],
146        unique=True,
147        help_text=_("Visible in the URL."),
148    )
149
150    title = models.TextField(help_text=_("Shown as the Title in Flow pages."))
151    layout = models.TextField(default=FlowLayout.STACKED, choices=FlowLayout.choices)
152
153    designation = models.CharField(
154        max_length=100,
155        choices=FlowDesignation.choices,
156        help_text=_(
157            "Decides what this Flow is used for. For example, the Authentication flow "
158            "is redirect to when an un-authenticated user visits authentik."
159        ),
160    )
161
162    background = FileField(
163        blank=True,
164        default="",
165        help_text=_("Background shown during execution"),
166    )
167
168    compatibility_mode = models.BooleanField(
169        default=False,
170        help_text=_(
171            "Enable compatibility mode, increases compatibility with "
172            "password managers on mobile devices."
173        ),
174    )
175
176    denied_action = models.TextField(
177        choices=FlowDeniedAction.choices,
178        default=FlowDeniedAction.MESSAGE_CONTINUE,
179        help_text=_("Configure what should happen when a flow denies access to a user."),
180    )
181
182    authentication = models.TextField(
183        choices=FlowAuthenticationRequirement.choices,
184        default=FlowAuthenticationRequirement.NONE,
185        help_text=_("Required level of authentication and authorization to access a flow."),
186    )
187
188    def background_url(self, request: HttpRequest | None = None) -> str:
189        """Get the URL to the background image"""
190        if not self.background:
191            if request:
192                return request.brand.branding_default_flow_background_url()
193            return (
194                CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
195            )
196
197        return get_file_manager(FileUsage.MEDIA).file_url(self.background, request)
198
199    def background_themed_urls(self, request: HttpRequest | None = None) -> dict[str, str] | None:
200        """Get themed URLs for background if it contains %(theme)s"""
201        if not self.background:
202            if request:
203                return request.brand.branding_default_flow_background_themed_urls()
204            return None
205
206        return get_file_manager(FileUsage.MEDIA).themed_urls(self.background, request)
207
208    stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True)
209
210    @property
211    def serializer(self) -> type[BaseSerializer]:
212        from authentik.flows.api.flows import FlowSerializer
213
214        return FlowSerializer
215
216    def __str__(self) -> str:
217        return f"Flow {self.name} ({self.slug})"
218
219    class Meta:
220        verbose_name = _("Flow")
221        verbose_name_plural = _("Flows")
222
223        permissions = [
224            ("export_flow", _("Can export a Flow")),
225            ("inspect_flow", _("Can inspect a Flow's execution")),
226            ("view_flow_cache", _("View Flow's cache metrics")),
227            ("clear_flow_cache", _("Clear Flow's cache metrics")),
228        ]
229
230
231class FlowStageBinding(SerializerModel, PolicyBindingModel):
232    """Relationship between Flow and Stage. Order is required and unique for
233    each flow-stage Binding. Additionally, policies can be specified, which determine if
234    this Binding applies to the current user"""
235
236    fsb_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
237
238    target = models.ForeignKey("Flow", on_delete=models.CASCADE)
239    stage = InheritanceForeignKey(Stage, on_delete=models.CASCADE)
240
241    evaluate_on_plan = models.BooleanField(
242        default=False,
243        help_text=_("Evaluate policies during the Flow planning process."),
244    )
245    re_evaluate_policies = models.BooleanField(
246        default=True,
247        help_text=_("Evaluate policies when the Stage is presented to the user."),
248    )
249
250    invalid_response_action = models.TextField(
251        choices=InvalidResponseAction.choices,
252        default=InvalidResponseAction.RETRY,
253        help_text=_(
254            "Configure how the flow executor should handle an invalid response to a "
255            "challenge. RETRY returns the error message and a similar challenge to the "
256            "executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT "
257            "restarts the flow while keeping the current context."
258        ),
259    )
260
261    order = models.IntegerField()
262
263    objects = InheritanceManager()
264
265    @property
266    def serializer(self) -> type[BaseSerializer]:
267        from authentik.flows.api.bindings import FlowStageBindingSerializer
268
269        return FlowStageBindingSerializer
270
271    def __str__(self) -> str:
272        if not self.target_id and self.stage_id:
273            return f"In-memory Flow-stage binding {self.stage}"
274        return f"Flow-stage binding #{self.order} to {self.target_id}"
275
276    class Meta:
277        ordering = ["target", "order"]
278
279        verbose_name = _("Flow Stage Binding")
280        verbose_name_plural = _("Flow Stage Bindings")
281        unique_together = (("target", "stage", "order"),)
282
283
284class ConfigurableStage(models.Model):
285    """Abstract base class for a Stage that can be configured by the enduser.
286    The stage should create a default flow with the configure_stage designation during
287    migration."""
288
289    configure_flow = models.ForeignKey(
290        Flow,
291        on_delete=models.SET_NULL,
292        null=True,
293        blank=True,
294        help_text=_(
295            "Flow used by an authenticated user to configure this Stage. "
296            "If empty, user will not be able to configure this stage."
297        ),
298    )
299
300    class Meta:
301        abstract = True
302
303
304class FriendlyNamedStage(models.Model):
305    """Abstract base class for a Stage that can have a user friendly name configured."""
306
307    friendly_name = models.TextField(blank=True)
308
309    class Meta:
310        abstract = True
311
312
313class FlowToken(InternallyManagedMixin, Token):
314    """Subclass of a standard Token, stores the currently active flow plan upon creation.
315    Can be used to later resume a flow."""
316
317    flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
318    _plan = models.TextField()
319    revoke_on_execution = models.BooleanField(default=True)
320
321    @staticmethod
322    def pickle(plan: FlowPlan) -> str:
323        """Pickle into string"""
324        data = dumps(plan)
325        return b64encode(data).decode()
326
327    @property
328    def plan(self) -> FlowPlan:
329        """Load Flow plan from pickled version"""
330        return loads(b64decode(self._plan.encode()))  # nosec
331
332    def __str__(self) -> str:
333        return f"Flow Token {super().__str__()}"
334
335    class Meta:
336        verbose_name = _("Flow Token")
337        verbose_name_plural = _("Flow Tokens")
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
class FlowAuthenticationRequirement(django.db.models.enums.TextChoices):
35class FlowAuthenticationRequirement(models.TextChoices):
36    """Required level of authentication and authorization to access a flow"""
37
38    NONE = "none"
39    REQUIRE_AUTHENTICATED = "require_authenticated"
40    REQUIRE_UNAUTHENTICATED = "require_unauthenticated"
41    REQUIRE_SUPERUSER = "require_superuser"
42    REQUIRE_REDIRECT = "require_redirect"
43    REQUIRE_OUTPOST = "require_outpost"

Required level of authentication and authorization to access a flow

class NotConfiguredAction(django.db.models.enums.TextChoices):
46class NotConfiguredAction(models.TextChoices):
47    """Decides how the FlowExecutor should proceed when a stage isn't configured"""
48
49    SKIP = "skip"
50    DENY = "deny"
51    CONFIGURE = "configure"

Decides how the FlowExecutor should proceed when a stage isn't configured

class InvalidResponseAction(django.db.models.enums.TextChoices):
54class InvalidResponseAction(models.TextChoices):
55    """Configure how the flow executor should handle invalid responses to challenges"""
56
57    RETRY = "retry"
58    RESTART = "restart"
59    RESTART_WITH_CONTEXT = "restart_with_context"

Configure how the flow executor should handle invalid responses to challenges

class FlowDeniedAction(django.db.models.enums.TextChoices):
62class FlowDeniedAction(models.TextChoices):
63    """Configure what response is given to denied flow executions"""
64
65    MESSAGE_CONTINUE = "message_continue"
66    MESSAGE = "message"
67    CONTINUE = "continue"

Configure what response is given to denied flow executions

class FlowDesignation(django.db.models.enums.TextChoices):
70class FlowDesignation(models.TextChoices):
71    """Designation of what a Flow should be used for. At a later point, this
72    should be replaced by a database entry."""
73
74    AUTHENTICATION = "authentication"
75    AUTHORIZATION = "authorization"
76    INVALIDATION = "invalidation"
77    ENROLLMENT = "enrollment"
78    UNRENOLLMENT = "unenrollment"
79    RECOVERY = "recovery"
80    STAGE_CONFIGURATION = "stage_configuration"

Designation of what a Flow should be used for. At a later point, this should be replaced by a database entry.

STAGE_CONFIGURATION = FlowDesignation.STAGE_CONFIGURATION
class Stage(authentik.lib.models.SerializerModel):
 83class Stage(SerializerModel):
 84    """Stage is an instance of a component used in a flow. This can verify the user,
 85    enroll the user or offer a way of recovery"""
 86
 87    stage_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 88
 89    name = models.TextField(unique=True)
 90
 91    objects = InheritanceManager()
 92
 93    @property
 94    def view(self) -> type[StageView]:
 95        """Return StageView class that implements logic for this stage"""
 96        # This is a bit of a workaround, since we can't set class methods with setattr
 97        if hasattr(self, "__in_memory_type"):
 98            return getattr(self, "__in_memory_type")
 99        raise NotImplementedError
100
101    @property
102    def component(self) -> str:
103        """Return component used to edit this object"""
104        raise NotImplementedError
105
106    def ui_user_settings(self) -> UserSettingSerializer | None:
107        """Entrypoint to integrate with User settings. Can either return None if no
108        user settings are available, or a challenge."""
109        return None
110
111    @property
112    def is_in_memory(self):
113        return hasattr(self, "__in_memory_type")
114
115    def __str__(self):
116        if self.is_in_memory:
117            return f"In-memory Stage {getattr(self, '__in_memory_type')}"
118        return f"Stage {self.name}"

Stage is an instance of a component used in a flow. This can verify the user, enroll the user or offer a way of recovery

def stage_uuid(unknown):

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

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 objects(unknown):

The type of the None singleton.

view
93    @property
94    def view(self) -> type[StageView]:
95        """Return StageView class that implements logic for this stage"""
96        # This is a bit of a workaround, since we can't set class methods with setattr
97        if hasattr(self, "__in_memory_type"):
98            return getattr(self, "__in_memory_type")
99        raise NotImplementedError

Return StageView class that implements logic for this stage

component: str
101    @property
102    def component(self) -> str:
103        """Return component used to edit this object"""
104        raise NotImplementedError

Return component used to edit this object

def ui_user_settings(self) -> authentik.core.types.UserSettingSerializer | None:
106    def ui_user_settings(self) -> UserSettingSerializer | None:
107        """Entrypoint to integrate with User settings. Can either return None if no
108        user settings are available, or a challenge."""
109        return None

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

is_in_memory
111    @property
112    def is_in_memory(self):
113        return hasattr(self, "__in_memory_type")
flow_set

Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.

In the example::

class Pizza(Model):
    toppings = ManyToManyField(Topping, related_name='pizzas')

Pizza.toppings and Topping.pizzas are ManyToManyDescriptor instances.

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

flowstagebinding_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.

emailstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

endpointstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

invitationstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

passwordstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

promptstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorstaticstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorduostage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatoremailstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorsmsstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorwebauthnstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorvalidatestage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

captchastage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

identificationstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatortotpstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

consentstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

denystage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

dummystage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

redirectstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

userdeletestage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

userloginstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

userlogoutstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

userwritestage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

authenticatorendpointgdtcstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

mutualtlsstage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

sourcestage

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

In the example::

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

Place.restaurant is a ReverseOneToOneDescriptor instance.

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

The requested object does not exist

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

The query returned multiple objects when only one was expected.

def in_memory_stage(unknown):
121def in_memory_stage(view: type[StageView], **kwargs) -> Stage:
122    """Creates an in-memory stage instance, based on a `view` as view.
123    Any key-word arguments are set as attributes on the stage object,
124    accessible via `self.executor.current_stage`."""
125    stage = Stage()
126    # Because we can't pickle a locally generated function,
127    # we set the view as a separate property and reference a generic function
128    # that returns that member
129    setattr(stage, "__in_memory_type", view)
130    stage.name = _("Dynamic In-memory stage: {doc}".format_map({"doc": view.__doc__}))
131    stage._meta.verbose_name = class_to_path(view)
132    for key, value in kwargs.items():
133        setattr(stage, key, value)
134    return stage

Creates an in-memory stage instance, based on a view as view. Any key-word arguments are set as attributes on the stage object, accessible via self.executor.current_stage.

137class Flow(SerializerModel, PolicyBindingModel):
138    """Flow describes how a series of Stages should be executed to authenticate/enroll/recover
139    a user. Additionally, policies can be applied, to specify which users
140    have access to this flow."""
141
142    flow_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
143
144    name = models.TextField()
145    slug = models.TextField(
146        validators=[validate_slug],
147        unique=True,
148        help_text=_("Visible in the URL."),
149    )
150
151    title = models.TextField(help_text=_("Shown as the Title in Flow pages."))
152    layout = models.TextField(default=FlowLayout.STACKED, choices=FlowLayout.choices)
153
154    designation = models.CharField(
155        max_length=100,
156        choices=FlowDesignation.choices,
157        help_text=_(
158            "Decides what this Flow is used for. For example, the Authentication flow "
159            "is redirect to when an un-authenticated user visits authentik."
160        ),
161    )
162
163    background = FileField(
164        blank=True,
165        default="",
166        help_text=_("Background shown during execution"),
167    )
168
169    compatibility_mode = models.BooleanField(
170        default=False,
171        help_text=_(
172            "Enable compatibility mode, increases compatibility with "
173            "password managers on mobile devices."
174        ),
175    )
176
177    denied_action = models.TextField(
178        choices=FlowDeniedAction.choices,
179        default=FlowDeniedAction.MESSAGE_CONTINUE,
180        help_text=_("Configure what should happen when a flow denies access to a user."),
181    )
182
183    authentication = models.TextField(
184        choices=FlowAuthenticationRequirement.choices,
185        default=FlowAuthenticationRequirement.NONE,
186        help_text=_("Required level of authentication and authorization to access a flow."),
187    )
188
189    def background_url(self, request: HttpRequest | None = None) -> str:
190        """Get the URL to the background image"""
191        if not self.background:
192            if request:
193                return request.brand.branding_default_flow_background_url()
194            return (
195                CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
196            )
197
198        return get_file_manager(FileUsage.MEDIA).file_url(self.background, request)
199
200    def background_themed_urls(self, request: HttpRequest | None = None) -> dict[str, str] | None:
201        """Get themed URLs for background if it contains %(theme)s"""
202        if not self.background:
203            if request:
204                return request.brand.branding_default_flow_background_themed_urls()
205            return None
206
207        return get_file_manager(FileUsage.MEDIA).themed_urls(self.background, request)
208
209    stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True)
210
211    @property
212    def serializer(self) -> type[BaseSerializer]:
213        from authentik.flows.api.flows import FlowSerializer
214
215        return FlowSerializer
216
217    def __str__(self) -> str:
218        return f"Flow {self.name} ({self.slug})"
219
220    class Meta:
221        verbose_name = _("Flow")
222        verbose_name_plural = _("Flows")
223
224        permissions = [
225            ("export_flow", _("Can export a Flow")),
226            ("inspect_flow", _("Can inspect a Flow's execution")),
227            ("view_flow_cache", _("View Flow's cache metrics")),
228            ("clear_flow_cache", _("Clear Flow's cache metrics")),
229        ]

Flow describes how a series of Stages should be executed to authenticate/enroll/recover a user. Additionally, policies can be applied, to specify which users have access to this flow.

def flow_uuid(unknown):

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

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 slug(unknown):

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

def title(unknown):

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

def layout(unknown):

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

def designation(unknown):

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

def background(unknown):

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

def compatibility_mode(unknown):

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

def denied_action(unknown):

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

def authentication(unknown):

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

def background_url(self, request: django.http.request.HttpRequest | None = None) -> str:
189    def background_url(self, request: HttpRequest | None = None) -> str:
190        """Get the URL to the background image"""
191        if not self.background:
192            if request:
193                return request.brand.branding_default_flow_background_url()
194            return (
195                CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
196            )
197
198        return get_file_manager(FileUsage.MEDIA).file_url(self.background, request)

Get the URL to the background image

def background_themed_urls( self, request: django.http.request.HttpRequest | None = None) -> dict[str, str] | None:
200    def background_themed_urls(self, request: HttpRequest | None = None) -> dict[str, str] | None:
201        """Get themed URLs for background if it contains %(theme)s"""
202        if not self.background:
203            if request:
204                return request.brand.branding_default_flow_background_themed_urls()
205            return None
206
207        return get_file_manager(FileUsage.MEDIA).themed_urls(self.background, request)

Get themed URLs for background if it contains %(theme)s

stages

Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.

In the example::

class Pizza(Model):
    toppings = ManyToManyField(Topping, related_name='pizzas')

Pizza.toppings and Topping.pizzas are ManyToManyDescriptor instances.

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

serializer: type[rest_framework.serializers.BaseSerializer]
211    @property
212    def serializer(self) -> type[BaseSerializer]:
213        from authentik.flows.api.flows import FlowSerializer
214
215        return FlowSerializer

Get serializer for this model

def get_layout_display(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_designation_display(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_denied_action_display(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_authentication_display(unknown):

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

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

policybindingmodel_ptr_id
policybindingmodel_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.

provider_authentication

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.

provider_authorization

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.

provider_invalidation

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.

source_authentication

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.

source_enrollment

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.

flowstagebinding_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.

flowtoken_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.

brand_authentication

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.

brand_invalidation

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.

brand_recovery

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.

brand_unenrollment

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.

brand_user_settings

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.

brand_device_code

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.

agentconnector_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.

invitation_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.

passwordstage_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.

source_pre_authentication

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.

authenticatorstaticstage_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.

authenticatorduostage_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.

authenticatoremailstage_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.

authenticatorsmsstage_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.

authenticatorwebauthnstage_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.

telegram_source_pre_authentication

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.

authenticatortotpstage_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.

redirectstage_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.

authenticatorendpointgdtcstage_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.

The requested object does not exist

The query returned multiple objects when only one was expected.

232class FlowStageBinding(SerializerModel, PolicyBindingModel):
233    """Relationship between Flow and Stage. Order is required and unique for
234    each flow-stage Binding. Additionally, policies can be specified, which determine if
235    this Binding applies to the current user"""
236
237    fsb_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
238
239    target = models.ForeignKey("Flow", on_delete=models.CASCADE)
240    stage = InheritanceForeignKey(Stage, on_delete=models.CASCADE)
241
242    evaluate_on_plan = models.BooleanField(
243        default=False,
244        help_text=_("Evaluate policies during the Flow planning process."),
245    )
246    re_evaluate_policies = models.BooleanField(
247        default=True,
248        help_text=_("Evaluate policies when the Stage is presented to the user."),
249    )
250
251    invalid_response_action = models.TextField(
252        choices=InvalidResponseAction.choices,
253        default=InvalidResponseAction.RETRY,
254        help_text=_(
255            "Configure how the flow executor should handle an invalid response to a "
256            "challenge. RETRY returns the error message and a similar challenge to the "
257            "executor. RESTART restarts the flow from the beginning, and RESTART_WITH_CONTEXT "
258            "restarts the flow while keeping the current context."
259        ),
260    )
261
262    order = models.IntegerField()
263
264    objects = InheritanceManager()
265
266    @property
267    def serializer(self) -> type[BaseSerializer]:
268        from authentik.flows.api.bindings import FlowStageBindingSerializer
269
270        return FlowStageBindingSerializer
271
272    def __str__(self) -> str:
273        if not self.target_id and self.stage_id:
274            return f"In-memory Flow-stage binding {self.stage}"
275        return f"Flow-stage binding #{self.order} to {self.target_id}"
276
277    class Meta:
278        ordering = ["target", "order"]
279
280        verbose_name = _("Flow Stage Binding")
281        verbose_name_plural = _("Flow Stage Bindings")
282        unique_together = (("target", "stage", "order"),)

Relationship between Flow and Stage. Order is required and unique for each flow-stage Binding. Additionally, policies can be specified, which determine if this Binding applies to the current user

def fsb_uuid(unknown):

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

target

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.

stage

Forward ManyToOne Descriptor that selects subclass. Requires InheritanceAutoManager.

def evaluate_on_plan(unknown):

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

def re_evaluate_policies(unknown):

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

def invalid_response_action(unknown):

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

def order(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.

serializer: type[rest_framework.serializers.BaseSerializer]
266    @property
267    def serializer(self) -> type[BaseSerializer]:
268        from authentik.flows.api.bindings import FlowStageBindingSerializer
269
270        return FlowStageBindingSerializer

Get serializer for this model

target_id
stage_id
def get_invalid_response_action_display(unknown):

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

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

policybindingmodel_ptr_id
policybindingmodel_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 FlowStageBinding.DoesNotExist(authentik.policies.models.PolicyBindingModel.DoesNotExist):

The requested object does not exist

class FlowStageBinding.MultipleObjectsReturned(authentik.policies.models.PolicyBindingModel.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.

class ConfigurableStage(django.db.models.base.Model):
285class ConfigurableStage(models.Model):
286    """Abstract base class for a Stage that can be configured by the enduser.
287    The stage should create a default flow with the configure_stage designation during
288    migration."""
289
290    configure_flow = models.ForeignKey(
291        Flow,
292        on_delete=models.SET_NULL,
293        null=True,
294        blank=True,
295        help_text=_(
296            "Flow used by an authenticated user to configure this Stage. "
297            "If empty, user will not be able to configure this stage."
298        ),
299    )
300
301    class Meta:
302        abstract = True

Abstract base class for a Stage that can be configured by the enduser. The stage should create a default flow with the configure_stage designation during migration.

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.

configure_flow_id
class ConfigurableStage.Meta:
301    class Meta:
302        abstract = True
abstract = False
class FriendlyNamedStage(django.db.models.base.Model):
305class FriendlyNamedStage(models.Model):
306    """Abstract base class for a Stage that can have a user friendly name configured."""
307
308    friendly_name = models.TextField(blank=True)
309
310    class Meta:
311        abstract = True

Abstract base class for a Stage that can have a user friendly name configured.

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.

class FriendlyNamedStage.Meta:
310    class Meta:
311        abstract = True
abstract = False
314class FlowToken(InternallyManagedMixin, Token):
315    """Subclass of a standard Token, stores the currently active flow plan upon creation.
316    Can be used to later resume a flow."""
317
318    flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
319    _plan = models.TextField()
320    revoke_on_execution = models.BooleanField(default=True)
321
322    @staticmethod
323    def pickle(plan: FlowPlan) -> str:
324        """Pickle into string"""
325        data = dumps(plan)
326        return b64encode(data).decode()
327
328    @property
329    def plan(self) -> FlowPlan:
330        """Load Flow plan from pickled version"""
331        return loads(b64decode(self._plan.encode()))  # nosec
332
333    def __str__(self) -> str:
334        return f"Flow Token {super().__str__()}"
335
336    class Meta:
337        verbose_name = _("Flow Token")
338        verbose_name_plural = _("Flow Tokens")

Subclass of a standard Token, stores the currently active flow plan upon creation. Can be used to later resume a flow.

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 revoke_on_execution(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 pickle(unknown):
322    @staticmethod
323    def pickle(plan: FlowPlan) -> str:
324        """Pickle into string"""
325        data = dumps(plan)
326        return b64encode(data).decode()

Pickle into string

plan
328    @property
329    def plan(self) -> FlowPlan:
330        """Load Flow plan from pickled version"""
331        return loads(b64decode(self._plan.encode()))  # nosec

Load Flow plan from pickled version

flow_id
token_ptr_id
token_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 FlowToken.DoesNotExist(authentik.core.models.Token.DoesNotExist):

The requested object does not exist

class FlowToken.MultipleObjectsReturned(authentik.core.models.Token.MultipleObjectsReturned):

The query returned multiple objects when only one was expected.