authentik.providers.saml.tasks

SAML Provider tasks

  1"""SAML Provider tasks"""
  2
  3import requests
  4from django.contrib.auth import get_user_model
  5from dramatiq.actor import actor
  6from structlog.stdlib import get_logger
  7
  8from authentik.events.models import Event, EventAction
  9from authentik.providers.saml.models import SAMLProvider
 10from authentik.providers.saml.processors.logout_request import LogoutRequestProcessor
 11from authentik.providers.saml.processors.logout_request_parser import LogoutRequest
 12from authentik.providers.saml.processors.logout_response_processor import LogoutResponseProcessor
 13
 14LOGGER = get_logger()
 15User = get_user_model()
 16
 17
 18@actor(description="Send SAML LogoutRequest to a Service Provider")
 19def send_saml_logout_request(
 20    provider_pk: int,
 21    sls_url: str,
 22    name_id: str,
 23    name_id_format: str,
 24    session_index: str,
 25    issuer: str,
 26):
 27    """Send SAML LogoutRequest to a Service Provider using session data"""
 28    provider = SAMLProvider.objects.filter(pk=provider_pk).first()
 29    if not provider:
 30        LOGGER.error(
 31            "Provider not found for SAML logout request",
 32            provider_pk=provider_pk,
 33            session_index=session_index,
 34        )
 35        return False
 36
 37    LOGGER.debug(
 38        "Sending SAML logout request",
 39        provider=provider.name,
 40        session_index=session_index,
 41    )
 42
 43    # Note: We don't need the user object for the logout request itself
 44    processor = LogoutRequestProcessor(
 45        provider=provider,
 46        user=None,
 47        destination=sls_url,
 48        name_id=name_id,
 49        name_id_format=name_id_format,
 50        session_index=session_index,
 51        issuer=issuer,
 52    )
 53
 54    return send_post_logout_request(provider, processor)
 55
 56
 57def send_post_logout_request(provider: SAMLProvider, processor: LogoutRequestProcessor) -> bool:
 58    """Send LogoutRequest using POST binding"""
 59    encoded_request = processor.encode_post()
 60
 61    form_data = {
 62        "SAMLRequest": encoded_request,
 63    }
 64
 65    if processor.relay_state:
 66        form_data["RelayState"] = processor.relay_state
 67
 68    response = requests.post(
 69        provider.sls_url,
 70        data=form_data,
 71        timeout=10,
 72        headers={
 73            "Content-Type": "application/x-www-form-urlencoded",
 74        },
 75        allow_redirects=True,
 76    )
 77    response.raise_for_status()
 78
 79    LOGGER.debug(
 80        "Sent POST logout request",
 81        provider=provider,
 82        status_code=response.status_code,
 83    )
 84
 85    return True
 86
 87
 88@actor(description="Send SAML LogoutResponse to a Service Provider (backchannel)")
 89def send_saml_logout_response(
 90    provider_pk: int,
 91    sls_url: str,
 92    logout_request_id: str | None = None,
 93    relay_state: str | None = None,
 94    issuer: str | None = None,
 95):
 96    """Send SAML LogoutResponse to a Service Provider using backchannel (server-to-server)"""
 97    provider = SAMLProvider.objects.filter(pk=provider_pk).first()
 98    if not provider:
 99        LOGGER.error(
100            "Provider not found for SAML logout response",
101            provider_pk=provider_pk,
102        )
103        return False
104
105    LOGGER.debug(
106        "Sending backchannel SAML logout response",
107        provider=provider.name,
108        sls_url=sls_url,
109    )
110
111    # Create a minimal LogoutRequest object for the response processor
112    # We only need the ID and relay_state for building the response
113    logout_request = None
114    if logout_request_id:
115        logout_request = LogoutRequest()
116        logout_request.id = logout_request_id
117        logout_request.relay_state = relay_state
118
119    # Build the logout response
120    processor = LogoutResponseProcessor(
121        provider=provider,
122        logout_request=logout_request,
123        destination=sls_url,
124        relay_state=relay_state,
125        issuer=issuer,
126    )
127
128    encoded_response = processor.encode_post()
129
130    form_data = {
131        "SAMLResponse": encoded_response,
132    }
133
134    if relay_state:
135        form_data["RelayState"] = relay_state
136
137    # Send the logout response to the SP
138    try:
139        response = requests.post(
140            sls_url,
141            data=form_data,
142            timeout=10,
143            headers={
144                "Content-Type": "application/x-www-form-urlencoded",
145            },
146            allow_redirects=True,
147        )
148        response.raise_for_status()
149
150        LOGGER.info(
151            "Successfully sent backchannel logout response to SP",
152            provider=provider.name,
153            sls_url=sls_url,
154            status_code=response.status_code,
155        )
156        return True
157
158    except requests.exceptions.RequestException as exc:
159        LOGGER.warning(
160            "Failed to send backchannel logout response to SP",
161            provider=provider.name,
162            sls_url=sls_url,
163            error=str(exc),
164        )
165        Event.new(
166            EventAction.CONFIGURATION_ERROR,
167            provider=provider,
168            message=f"Backchannel logout response failed: {str(exc)}",
169        ).save()
170        return False
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
class User(authentik.lib.models.SerializerModel, authentik.core.models.AttributesMixin, django.contrib.auth.models.AbstractUser):
360class User(SerializerModel, AttributesMixin, AbstractUser):
361    """authentik User model, based on django's contrib auth user model."""
362
363    # Overwriting PermissionsMixin: permissions are handled by roles.
364    # (This knowingly violates the Liskov substitution principle. It is better to fail loudly.)
365    user_permissions = None
366
367    uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
368    name = models.TextField(help_text=_("User's display name."))
369    path = models.TextField(default="users")
370    type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL)
371
372    sources = models.ManyToManyField("Source", through="UserSourceConnection")
373    groups = models.ManyToManyField("Group", related_name="users")
374    roles = models.ManyToManyField("authentik_rbac.Role", related_name="users", blank=True)
375    password_change_date = models.DateTimeField(auto_now_add=True)
376
377    last_updated = models.DateTimeField(auto_now=True)
378
379    objects = UserManager()
380
381    class Meta:
382        verbose_name = _("User")
383        verbose_name_plural = _("Users")
384        permissions = [
385            ("reset_user_password", _("Reset Password")),
386            ("impersonate", _("Can impersonate other users")),
387            ("preview_user", _("Can preview user data sent to providers")),
388            ("view_user_applications", _("View applications the user has access to")),
389        ]
390        indexes = [
391            models.Index(fields=["last_login"]),
392            models.Index(fields=["password_change_date"]),
393            models.Index(fields=["uuid"]),
394            models.Index(fields=["path"]),
395            models.Index(fields=["type"]),
396            models.Index(fields=["date_joined"]),
397            models.Index(fields=["last_updated"]),
398        ]
399
400    def __str__(self):
401        return self.username
402
403    @staticmethod
404    def default_path() -> str:
405        """Get the default user path"""
406        return User._meta.get_field("path").default
407
408    def all_groups(self) -> QuerySet[Group]:
409        """Recursively get all groups this user is a member of."""
410        return self.groups.all().with_ancestors()
411
412    def all_roles(self) -> QuerySet[Role]:
413        """Get all roles of this user and all of its groups (recursively)."""
414        return Role.objects.filter(Q(users=self) | Q(groups__in=self.all_groups())).distinct()
415
416    def get_managed_role(self, create=False):
417        if create:
418            name = managed_role_name(self)
419            role, created = Role.objects.get_or_create(name=name, managed=name)
420            if created:
421                role.users.add(self)
422            return role
423        else:
424            return Role.objects.filter(name=managed_role_name(self)).first()
425
426    def get_all_model_perms_on_managed_role(self) -> QuerySet[RoleModelPermission]:
427        role = self.get_managed_role()
428        if not role:
429            return RoleModelPermission.objects.none()
430        return RoleModelPermission.objects.filter(role=role)
431
432    def get_all_obj_perms_on_managed_role(self) -> QuerySet[RoleObjectPermission]:
433        role = self.get_managed_role()
434        if not role:
435            return RoleObjectPermission.objects.none()
436        return RoleObjectPermission.objects.filter(role=role)
437
438    def assign_perms_to_managed_role(
439        self,
440        perms: str | list[str] | Permission | list[Permission],
441        obj: models.Model | None = None,
442    ):
443        if not perms:
444            return
445        role = self.get_managed_role(create=True)
446        role.assign_perms(perms, obj)
447
448    def remove_perms_from_managed_role(
449        self,
450        perms: str | list[str] | Permission | list[Permission],
451        obj: models.Model | None = None,
452    ):
453        role = self.get_managed_role()
454        if not role:
455            return None
456        role.remove_perms(perms, obj)
457
458    def remove_all_perms_from_managed_role(self):
459        role = self.get_managed_role()
460        if not role:
461            return None
462        RoleModelPermission.objects.filter(role=role).delete()
463        RoleObjectPermission.objects.filter(role=role).delete()
464
465    def group_attributes(self, request: HttpRequest | None = None) -> dict[str, Any]:
466        """Get a dictionary containing the attributes from all groups the user belongs to,
467        including the users attributes"""
468        final_attributes = {}
469        if request and hasattr(request, "brand"):
470            always_merger.merge(final_attributes, request.brand.attributes)
471        for group in self.all_groups().order_by("name"):
472            always_merger.merge(final_attributes, group.attributes)
473        always_merger.merge(final_attributes, self.attributes)
474        return final_attributes
475
476    def app_entitlements(self, app: Application | None) -> QuerySet[ApplicationEntitlement]:
477        """Get all entitlements this user has for `app`."""
478        if not app:
479            return []
480        all_groups = self.all_groups()
481        qs = app.applicationentitlement_set.filter(
482            Q(
483                Q(bindings__user=self) | Q(bindings__group__in=all_groups),
484                bindings__negate=False,
485            )
486            | Q(
487                Q(~Q(bindings__user=self), bindings__user__isnull=False)
488                | Q(~Q(bindings__group__in=all_groups), bindings__group__isnull=False),
489                bindings__negate=True,
490            ),
491            bindings__enabled=True,
492        ).order_by("name")
493        return qs
494
495    def app_entitlements_attributes(self, app: Application | None) -> dict:
496        """Get a dictionary containing all merged attributes from app entitlements for `app`."""
497        final_attributes = {}
498        for attrs in self.app_entitlements(app).values_list("attributes", flat=True):
499            always_merger.merge(final_attributes, attrs)
500        return final_attributes
501
502    @property
503    def serializer(self) -> Serializer:
504        from authentik.core.api.users import UserSerializer
505
506        return UserSerializer
507
508    @cached_property
509    def is_superuser(self) -> bool:
510        """Get supseruser status based on membership in a group with superuser status"""
511        return self.all_groups().filter(is_superuser=True).exists()
512
513    @property
514    def is_staff(self) -> bool:
515        """superuser == staff user"""
516        return self.is_superuser  # type: ignore
517
518    # TODO: remove this after 2026.
519    @property
520    def ak_groups(self):
521        """This is a proxy for a renamed, deprecated field."""
522        from authentik.events.models import Event
523
524        deprecation = "authentik.core.models.User.ak_groups"
525        replacement = "authentik.core.models.User.groups"
526        message_logger = (
527            f"{deprecation} is deprecated and will be removed in a future version of "
528            f"authentik. Please use {replacement} instead."
529        )
530        message_event = (
531            f"{message_logger} This event will not be repeated until it expires (by "
532            "default: in 30 days). See authentik logs for every will invocation of this "
533            "deprecation."
534        )
535        stacktrace = traceback.format_stack()
536        # The last line is this function, the next-to-last line is its caller
537        cause = stacktrace[-2] if len(stacktrace) > 1 else "Unknown, see stacktrace in logs"
538        if search := re.search(r'"(.*?)"', cause):
539            cause = f"Property mapping or Expression policy named {search.group(1)}"
540
541        LOGGER.warning(
542            "deprecation used",
543            message=message_logger,
544            deprecation=deprecation,
545            replacement=replacement,
546            cause=cause,
547            stacktrace=stacktrace,
548        )
549        Event.log_deprecation(
550            deprecation, message=message_event, cause=cause, replacement=replacement
551        )
552        return self.groups
553
554    def set_password(self, raw_password, signal=True, sender=None, request=None):
555        if self.pk and signal:
556            from authentik.core.signals import password_changed
557
558            if not sender:
559                sender = self
560            password_changed.send(sender=sender, user=self, password=raw_password, request=request)
561        self.password_change_date = now()
562        return super().set_password(raw_password)
563
564    @staticmethod
565    def validate_password_hash(password_hash: str):
566        """Validate that the value is a recognized Django password hash."""
567        identify_hasher(password_hash)  # Raises ValueError if invalid
568
569    def set_password_from_hash(self, password_hash: str, signal=True, sender=None, request=None):
570        """Set password directly from a pre-hashed value.
571
572        Unlike set_password(), this does not hash the input again. The provided value
573        must already be a valid Django password hash, and it is stored directly on the
574        user after validation.
575
576        Because no raw password is available, downstream password sync integrations
577        such as LDAP and Kerberos cannot be updated from this code path.
578
579        Raises ValueError if the hash format is not recognized.
580        """
581        self.validate_password_hash(password_hash)
582        if self.pk and signal:
583            from authentik.core.signals import password_hash_changed
584
585            if not sender:
586                sender = self
587            password_hash_changed.send(sender=sender, user=self, request=request)
588        self.password = password_hash
589        self.password_change_date = now()
590
591    def check_password(self, raw_password: str) -> bool:
592        """
593        Return a boolean of whether the raw_password was correct. Handles
594        hashing formats behind the scenes.
595
596        Slightly changed version which doesn't send a signal for such internal hash upgrades
597        """
598
599        def setter(raw_password):
600            self.set_password(raw_password, signal=False)
601            # Password hash upgrades shouldn't be considered password changes.
602            self._password = None
603            self.save(update_fields=["password"])
604
605        return check_password(raw_password, self.password, setter)
606
607    @property
608    def uid(self) -> str:
609        """Generate a globally unique UID, based on the user ID and the hashed secret key"""
610        return sha256(f"{self.id}-{get_unique_identifier()}".encode("ascii")).hexdigest()
611
612    def locale(self, request: HttpRequest | None = None) -> str:
613        """Get the locale the user has configured"""
614        if request and hasattr(request, "LANGUAGE_CODE"):
615            return request.LANGUAGE_CODE
616        try:
617            return self.attributes.get("settings", {}).get("locale", "")
618
619        except Exception as exc:  # noqa
620            LOGGER.warning("Failed to get default locale", exc=exc)
621        if request:
622            return request.brand.locale
623        return ""
624
625    @property
626    def avatar(self) -> str:
627        """Get avatar, depending on authentik.avatar setting"""
628        return get_avatar(self)

authentik User model, based on django's contrib auth user model.

user_permissions = None
def 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 path(unknown):

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

def type(unknown):

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

sources

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.

groups

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.

roles

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.

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

The type of the None singleton.

@staticmethod
def default_path() -> str:
403    @staticmethod
404    def default_path() -> str:
405        """Get the default user path"""
406        return User._meta.get_field("path").default

Get the default user path

def all_groups(self) -> django.db.models.query.QuerySet:
408    def all_groups(self) -> QuerySet[Group]:
409        """Recursively get all groups this user is a member of."""
410        return self.groups.all().with_ancestors()

Recursively get all groups this user is a member of.

def all_roles(self) -> django.db.models.query.QuerySet:
412    def all_roles(self) -> QuerySet[Role]:
413        """Get all roles of this user and all of its groups (recursively)."""
414        return Role.objects.filter(Q(users=self) | Q(groups__in=self.all_groups())).distinct()

Get all roles of this user and all of its groups (recursively).

def get_managed_role(self, create=False):
416    def get_managed_role(self, create=False):
417        if create:
418            name = managed_role_name(self)
419            role, created = Role.objects.get_or_create(name=name, managed=name)
420            if created:
421                role.users.add(self)
422            return role
423        else:
424            return Role.objects.filter(name=managed_role_name(self)).first()
def get_all_model_perms_on_managed_role(self) -> django.db.models.query.QuerySet:
426    def get_all_model_perms_on_managed_role(self) -> QuerySet[RoleModelPermission]:
427        role = self.get_managed_role()
428        if not role:
429            return RoleModelPermission.objects.none()
430        return RoleModelPermission.objects.filter(role=role)
def get_all_obj_perms_on_managed_role(self) -> django.db.models.query.QuerySet:
432    def get_all_obj_perms_on_managed_role(self) -> QuerySet[RoleObjectPermission]:
433        role = self.get_managed_role()
434        if not role:
435            return RoleObjectPermission.objects.none()
436        return RoleObjectPermission.objects.filter(role=role)
def assign_perms_to_managed_role( self, perms: str | list[str] | django.contrib.auth.models.Permission | list[django.contrib.auth.models.Permission], obj: django.db.models.base.Model | None = None):
438    def assign_perms_to_managed_role(
439        self,
440        perms: str | list[str] | Permission | list[Permission],
441        obj: models.Model | None = None,
442    ):
443        if not perms:
444            return
445        role = self.get_managed_role(create=True)
446        role.assign_perms(perms, obj)
def remove_perms_from_managed_role( self, perms: str | list[str] | django.contrib.auth.models.Permission | list[django.contrib.auth.models.Permission], obj: django.db.models.base.Model | None = None):
448    def remove_perms_from_managed_role(
449        self,
450        perms: str | list[str] | Permission | list[Permission],
451        obj: models.Model | None = None,
452    ):
453        role = self.get_managed_role()
454        if not role:
455            return None
456        role.remove_perms(perms, obj)
def remove_all_perms_from_managed_role(self):
458    def remove_all_perms_from_managed_role(self):
459        role = self.get_managed_role()
460        if not role:
461            return None
462        RoleModelPermission.objects.filter(role=role).delete()
463        RoleObjectPermission.objects.filter(role=role).delete()
def group_attributes( self, request: django.http.request.HttpRequest | None = None) -> dict[str, typing.Any]:
465    def group_attributes(self, request: HttpRequest | None = None) -> dict[str, Any]:
466        """Get a dictionary containing the attributes from all groups the user belongs to,
467        including the users attributes"""
468        final_attributes = {}
469        if request and hasattr(request, "brand"):
470            always_merger.merge(final_attributes, request.brand.attributes)
471        for group in self.all_groups().order_by("name"):
472            always_merger.merge(final_attributes, group.attributes)
473        always_merger.merge(final_attributes, self.attributes)
474        return final_attributes

Get a dictionary containing the attributes from all groups the user belongs to, including the users attributes

def app_entitlements( self, app: authentik.core.models.Application | None) -> django.db.models.query.QuerySet:
476    def app_entitlements(self, app: Application | None) -> QuerySet[ApplicationEntitlement]:
477        """Get all entitlements this user has for `app`."""
478        if not app:
479            return []
480        all_groups = self.all_groups()
481        qs = app.applicationentitlement_set.filter(
482            Q(
483                Q(bindings__user=self) | Q(bindings__group__in=all_groups),
484                bindings__negate=False,
485            )
486            | Q(
487                Q(~Q(bindings__user=self), bindings__user__isnull=False)
488                | Q(~Q(bindings__group__in=all_groups), bindings__group__isnull=False),
489                bindings__negate=True,
490            ),
491            bindings__enabled=True,
492        ).order_by("name")
493        return qs

Get all entitlements this user has for app.

def app_entitlements_attributes(self, app: authentik.core.models.Application | None) -> dict:
495    def app_entitlements_attributes(self, app: Application | None) -> dict:
496        """Get a dictionary containing all merged attributes from app entitlements for `app`."""
497        final_attributes = {}
498        for attrs in self.app_entitlements(app).values_list("attributes", flat=True):
499            always_merger.merge(final_attributes, attrs)
500        return final_attributes

Get a dictionary containing all merged attributes from app entitlements for app.

serializer: rest_framework.serializers.Serializer
502    @property
503    def serializer(self) -> Serializer:
504        from authentik.core.api.users import UserSerializer
505
506        return UserSerializer

Get serializer for this model

def is_superuser(unknown):

Get supseruser status based on membership in a group with superuser status

is_staff: bool
513    @property
514    def is_staff(self) -> bool:
515        """superuser == staff user"""
516        return self.is_superuser  # type: ignore

superuser == staff user

ak_groups
519    @property
520    def ak_groups(self):
521        """This is a proxy for a renamed, deprecated field."""
522        from authentik.events.models import Event
523
524        deprecation = "authentik.core.models.User.ak_groups"
525        replacement = "authentik.core.models.User.groups"
526        message_logger = (
527            f"{deprecation} is deprecated and will be removed in a future version of "
528            f"authentik. Please use {replacement} instead."
529        )
530        message_event = (
531            f"{message_logger} This event will not be repeated until it expires (by "
532            "default: in 30 days). See authentik logs for every will invocation of this "
533            "deprecation."
534        )
535        stacktrace = traceback.format_stack()
536        # The last line is this function, the next-to-last line is its caller
537        cause = stacktrace[-2] if len(stacktrace) > 1 else "Unknown, see stacktrace in logs"
538        if search := re.search(r'"(.*?)"', cause):
539            cause = f"Property mapping or Expression policy named {search.group(1)}"
540
541        LOGGER.warning(
542            "deprecation used",
543            message=message_logger,
544            deprecation=deprecation,
545            replacement=replacement,
546            cause=cause,
547            stacktrace=stacktrace,
548        )
549        Event.log_deprecation(
550            deprecation, message=message_event, cause=cause, replacement=replacement
551        )
552        return self.groups

This is a proxy for a renamed, deprecated field.

def set_password(self, raw_password, signal=True, sender=None, request=None):
554    def set_password(self, raw_password, signal=True, sender=None, request=None):
555        if self.pk and signal:
556            from authentik.core.signals import password_changed
557
558            if not sender:
559                sender = self
560            password_changed.send(sender=sender, user=self, password=raw_password, request=request)
561        self.password_change_date = now()
562        return super().set_password(raw_password)
@staticmethod
def validate_password_hash(password_hash: str):
564    @staticmethod
565    def validate_password_hash(password_hash: str):
566        """Validate that the value is a recognized Django password hash."""
567        identify_hasher(password_hash)  # Raises ValueError if invalid

Validate that the value is a recognized Django password hash.

def set_password_from_hash(self, password_hash: str, signal=True, sender=None, request=None):
569    def set_password_from_hash(self, password_hash: str, signal=True, sender=None, request=None):
570        """Set password directly from a pre-hashed value.
571
572        Unlike set_password(), this does not hash the input again. The provided value
573        must already be a valid Django password hash, and it is stored directly on the
574        user after validation.
575
576        Because no raw password is available, downstream password sync integrations
577        such as LDAP and Kerberos cannot be updated from this code path.
578
579        Raises ValueError if the hash format is not recognized.
580        """
581        self.validate_password_hash(password_hash)
582        if self.pk and signal:
583            from authentik.core.signals import password_hash_changed
584
585            if not sender:
586                sender = self
587            password_hash_changed.send(sender=sender, user=self, request=request)
588        self.password = password_hash
589        self.password_change_date = now()

Set password directly from a pre-hashed value.

Unlike set_password(), this does not hash the input again. The provided value must already be a valid Django password hash, and it is stored directly on the user after validation.

Because no raw password is available, downstream password sync integrations such as LDAP and Kerberos cannot be updated from this code path.

Raises ValueError if the hash format is not recognized.

def check_password(self, raw_password: str) -> bool:
591    def check_password(self, raw_password: str) -> bool:
592        """
593        Return a boolean of whether the raw_password was correct. Handles
594        hashing formats behind the scenes.
595
596        Slightly changed version which doesn't send a signal for such internal hash upgrades
597        """
598
599        def setter(raw_password):
600            self.set_password(raw_password, signal=False)
601            # Password hash upgrades shouldn't be considered password changes.
602            self._password = None
603            self.save(update_fields=["password"])
604
605        return check_password(raw_password, self.password, setter)

Return a boolean of whether the raw_password was correct. Handles hashing formats behind the scenes.

Slightly changed version which doesn't send a signal for such internal hash upgrades

uid: str
607    @property
608    def uid(self) -> str:
609        """Generate a globally unique UID, based on the user ID and the hashed secret key"""
610        return sha256(f"{self.id}-{get_unique_identifier()}".encode("ascii")).hexdigest()

Generate a globally unique UID, based on the user ID and the hashed secret key

def locale(self, request: django.http.request.HttpRequest | None = None) -> str:
612    def locale(self, request: HttpRequest | None = None) -> str:
613        """Get the locale the user has configured"""
614        if request and hasattr(request, "LANGUAGE_CODE"):
615            return request.LANGUAGE_CODE
616        try:
617            return self.attributes.get("settings", {}).get("locale", "")
618
619        except Exception as exc:  # noqa
620            LOGGER.warning("Failed to get default locale", exc=exc)
621        if request:
622            return request.brand.locale
623        return ""

Get the locale the user has configured

avatar: str
625    @property
626    def avatar(self) -> str:
627        """Get avatar, depending on authentik.avatar setting"""
628        return get_avatar(self)

Get avatar, depending on authentik.avatar setting

def attributes(unknown):

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

def username(unknown):

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

def first_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 last_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 email(unknown):

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

def is_active(unknown):

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

def date_joined(unknown):

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

def password(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_login(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_type_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_next_by_password_change_date(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_password_change_date(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.

def get_next_by_date_joined(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_date_joined(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 id(unknown):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

old_passwords

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.

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

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

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

endpointdevice_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 User.DoesNotExist(django.core.exceptions.ObjectDoesNotExist):

The requested object does not exist

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

The query returned multiple objects when only one was expected.

send_saml_logout_request = Actor(<function send_saml_logout_request>, queue_name='default', actor_name='send_saml_logout_request')

Send SAML LogoutRequest to a Service Provider using session data

58def send_post_logout_request(provider: SAMLProvider, processor: LogoutRequestProcessor) -> bool:
59    """Send LogoutRequest using POST binding"""
60    encoded_request = processor.encode_post()
61
62    form_data = {
63        "SAMLRequest": encoded_request,
64    }
65
66    if processor.relay_state:
67        form_data["RelayState"] = processor.relay_state
68
69    response = requests.post(
70        provider.sls_url,
71        data=form_data,
72        timeout=10,
73        headers={
74            "Content-Type": "application/x-www-form-urlencoded",
75        },
76        allow_redirects=True,
77    )
78    response.raise_for_status()
79
80    LOGGER.debug(
81        "Sent POST logout request",
82        provider=provider,
83        status_code=response.status_code,
84    )
85
86    return True

Send LogoutRequest using POST binding

send_saml_logout_response = Actor(<function send_saml_logout_response>, queue_name='default', actor_name='send_saml_logout_response')

Send SAML LogoutResponse to a Service Provider using backchannel (server-to-server)