Skip to content

website/integrations: make grafana terraform section expand#19192

Merged
BeryJu merged 1 commit intomainfrom
website/integrations/grafana-tf-expand
Jan 5, 2026
Merged

website/integrations: make grafana terraform section expand#19192
BeryJu merged 1 commit intomainfrom
website/integrations/grafana-tf-expand

Conversation

@BeryJu
Copy link
Member

@BeryJu BeryJu commented Jan 5, 2026

No description provided.

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
@BeryJu BeryJu requested a review from a team as a code owner January 5, 2026 15:58
@netlify
Copy link

netlify bot commented Jan 5, 2026

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit a7afe47
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/695bdf939340cd00089c6b92
😎 Deploy Preview https://deploy-preview-19192--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Jan 5, 2026

Deploy Preview for authentik-integrations ready!

Name Link
🔨 Latest commit a7afe47
🔍 Latest deploy log https://app.netlify.com/projects/authentik-integrations/deploys/695bdf93e4c28b0008f928eb
😎 Deploy Preview https://deploy-preview-19192--authentik-integrations.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link

codecov bot commented Jan 5, 2026

❌ 4 Tests Failed:

Tests completed Failed Passed Skipped
2869 4 2865 2
View the top 3 failed test(s) by shortest run time
tests.e2e.test_provider_oauth2_grafana.TestProviderOAuth2OAuth::test_authorization_logout
Stack Traces | 20.2s run time
self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        # Raise a warning during app initialization (stored_app_configs is only
        # ever set during testing).
        if not apps.ready and not apps.stored_app_configs:
            warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
                # params default might be backend specific.
>               return self.cursor.execute(sql)

.venv/lib/python3.13.../db/backends/utils.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [IDLE] (host=localhost user=authentik database=test_authentik) at 0x7f94544d5c10>
args = ('TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderproper...lows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";',)
kwargs = {}

    def execute(self, *args, **kwargs):
        execute_total.labels(alias, vendor).inc()
        with (
            query_duration_seconds.labels(**labels).time(),
            ExceptionCounterByType(errors_total, extra_labels=labels),
        ):
>           return super().execute(*args, **kwargs)

.venv/lib/python3.13.../django_prometheus/db/common.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [IDLE] (host=localhost user=authentik database=test_authentik) at 0x7f94544d5c10>
query = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None

    def execute(
        self,
        query: Query,
        params: Params | None = None,
        *,
        prepare: bool | None = None,
        binary: bool | None = None,
    ) -> Self:
        """
        Execute a query or command to the database.
        """
        try:
            with self._conn.lock:
                self._conn.wait(
                    self._execute_gen(query, params, prepare=prepare, binary=binary)
                )
        except e._NO_TRACEBACK as ex:
>           raise ex.with_traceback(None)
E           psycopg.errors.DeadlockDetected: deadlock detected
E           DETAIL:  Process 172 waits for AccessExclusiveLock on relation 17555 of database 16389; blocked by process 191.
E           Process 191 waits for AccessShareLock on relation 17961 of database 16389; blocked by process 172.
E           HINT:  See server log for query details.

.venv/lib/python3.13....../site-packages/psycopg/cursor.py:97: DeadlockDetected

The above exception was the direct cause of the following exception:

self = <django.core.management.commands.flush.Command object at 0x7f9453821a30>
options = {'allow_cascade': False, 'database': 'default', 'force_color': False, 'inhibit_post_migrate': False, ...}
database = 'default'
connection = <DatabaseWrapper vendor='postgresql' alias='default'>
verbosity = 0, interactive = False, reset_sequences = False
allow_cascade = False, inhibit_post_migrate = False

        def handle(self, **options):
            database = options["database"]
            connection = connections[database]
            verbosity = options["verbosity"]
            interactive = options["interactive"]
            # The following are stealth options used by Django's internals.
            reset_sequences = options.get("reset_sequences", True)
            allow_cascade = options.get("allow_cascade", False)
            inhibit_post_migrate = options.get("inhibit_post_migrate", False)
    
            self.style = no_style()
    
            # Import the 'management' module within each installed app, to register
            # dispatcher events.
            for app_config in apps.get_app_configs():
                try:
                    import_module(".management", app_config.name)
                except ImportError:
                    pass
    
            sql_list = sql_flush(
                self.style,
                connection,
                reset_sequences=reset_sequences,
                allow_cascade=allow_cascade,
            )
    
            if interactive:
                confirm = input(
                    """You have requested a flush of the database.
    This will IRREVERSIBLY DESTROY all data currently in the "%s" database,
    and return each table to an empty state.
    Are you sure you want to do this?
    
        Type 'yes' to continue, or 'no' to cancel: """
                    % connection.settings_dict["NAME"]
                )
            else:
                confirm = "yes"
    
            if confirm == "yes":
                try:
>                   connection.ops.execute_sql_flush(sql_list)

.venv/lib/python3.13.../management/commands/flush.py:74: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <psqlextra.backend.operations.PostgresOperations object at 0x7f9460a842f0>
sql_list = ['TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderproper...flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";']

    def execute_sql_flush(self, sql_list):
        """Execute a list of SQL statements to flush the database."""
        with transaction.atomic(
            using=self.connection.alias,
            savepoint=self.connection.features.can_rollback_ddl,
        ):
            with self.connection.cursor() as cursor:
                for sql in sql_list:
>                   cursor.execute(sql)

.venv/lib/python3.13.../backends/base/operations.py:473: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>, 'TRUNCATE "authentik_policies_unique_password_uniq...flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";')
kwargs = {}

    def runner(*args: "P.args", **kwargs: "P.kwargs"):
        # type: (...) -> R
        if sentry_sdk.get_client().get_integration(integration) is None:
            return original_function(*args, **kwargs)
    
>       return sentry_patched_function(*args, **kwargs)

.venv/lib/python3.13.../site-packages/sentry_sdk/utils.py:1816: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None

    @ensure_integration_enabled(DjangoIntegration, real_execute)
    def execute(self, sql, params=None):
        # type: (CursorWrapper, Any, Optional[Any]) -> Any
        with record_sql_queries(
            cursor=self.cursor,
            query=sql,
            params_list=params,
            paramstyle="format",
            executemany=False,
            span_origin=DjangoIntegration.origin_db,
        ) as span:
            _set_db_data(span, self)
>           result = real_execute(self, sql, params)

.venv/lib/python3.13.../integrations/django/__init__.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None

    def execute(self, sql, params=None):
>       return self._execute_with_wrappers(
            sql, params, many=False, executor=self._execute
        )

.venv/lib/python3.13.../db/backends/utils.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None, many = False
executor = <bound method CursorWrapper._execute of <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>>

    def _execute_with_wrappers(self, sql, params, many, executor):
        context = {"connection": self.db, "cursor": self}
        for wrapper in reversed(self.db.execute_wrappers):
            executor = functools.partial(wrapper, executor)
>       return executor(sql, params, many, context)

.venv/lib/python3.13.../db/backends/utils.py:92: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        # Raise a warning during app initialization (stored_app_configs is only
        # ever set during testing).
        if not apps.ready and not apps.stored_app_configs:
            warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
        self.db.validate_no_broken_transaction()
>       with self.db.wrap_database_errors:

.venv/lib/python3.13.../db/backends/utils.py:100: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.utils.DatabaseErrorWrapper object at 0x7f94577b5be0>
exc_type = <class 'psycopg.errors.DeadlockDetected'>
exc_value = DeadlockDetected('deadlock detected\nDETAIL:  Process 172 waits for AccessExclusiveLock on relation 17555 of database ...ccessShareLock on relation 17961 of database 16389; blocked by process 172.\nHINT:  See server log for query details.')
traceback = <traceback object at 0x7f945bd66d00>

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None:
            return
        for dj_exc_type in (
            DataError,
            OperationalError,
            IntegrityError,
            InternalError,
            ProgrammingError,
            NotSupportedError,
            DatabaseError,
            InterfaceError,
            Error,
        ):
            db_exc_type = getattr(self.wrapper.Database, dj_exc_type.__name__)
            if issubclass(exc_type, db_exc_type):
                dj_exc_value = dj_exc_type(*exc_value.args)
                # Only set the 'errors_occurred' flag for errors that may make
                # the connection unusable.
                if dj_exc_type not in (DataError, IntegrityError):
                    self.wrapper.errors_occurred = True
>               raise dj_exc_value.with_traceback(traceback) from exc_value

.venv/lib/python3.13.../django/db/utils.py:91: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>
sql = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f9453e841d0>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        # Raise a warning during app initialization (stored_app_configs is only
        # ever set during testing).
        if not apps.ready and not apps.stored_app_configs:
            warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
                # params default might be backend specific.
>               return self.cursor.execute(sql)

.venv/lib/python3.13.../db/backends/utils.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [IDLE] (host=localhost user=authentik database=test_authentik) at 0x7f94544d5c10>
args = ('TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderproper...lows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";',)
kwargs = {}

    def execute(self, *args, **kwargs):
        execute_total.labels(alias, vendor).inc()
        with (
            query_duration_seconds.labels(**labels).time(),
            ExceptionCounterByType(errors_total, extra_labels=labels),
        ):
>           return super().execute(*args, **kwargs)

.venv/lib/python3.13.../django_prometheus/db/common.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [IDLE] (host=localhost user=authentik database=test_authentik) at 0x7f94544d5c10>
query = 'TRUNCATE "authentik_policies_unique_password_uniquepasswordpolicy", "authentik_providers_radius_radiusproviderpropert..._flows_flowstagebinding", "authentik_policies_reputation_reputationpolicy", "authentik_providers_oauth2_refreshtoken";'
params = None

    def execute(
        self,
        query: Query,
        params: Params | None = None,
        *,
        prepare: bool | None = None,
        binary: bool | None = None,
    ) -> Self:
        """
        Execute a query or command to the database.
        """
        try:
            with self._conn.lock:
                self._conn.wait(
                    self._execute_gen(query, params, prepare=prepare, binary=binary)
                )
        except e._NO_TRACEBACK as ex:
>           raise ex.with_traceback(None)
E           django.db.utils.OperationalError: deadlock detected
E           DETAIL:  Process 172 waits for AccessExclusiveLock on relation 17555 of database 16389; blocked by process 191.
E           Process 191 waits for AccessShareLock on relation 17961 of database 16389; blocked by process 172.
E           HINT:  See server log for query details.

.venv/lib/python3.13....../site-packages/psycopg/cursor.py:97: OperationalError

The above exception was the direct cause of the following exception:

self = <tests.e2e.test_provider_oauth2_grafana.TestProviderOAuth2OAuth testMethod=test_authorization_logout>
result = <TestCaseFunction test_authorization_logout>, debug = False

    def _setup_and_call(self, result, debug=False):
        """
        Perform the following in order: pre-setup, run test, post-teardown,
        skipping pre/post hooks if test is set to be skipped.
    
        If debug=True, reraise any errors in setup and use super().debug()
        instead of __call__() to run the test.
        """
        testMethod = getattr(self, self._testMethodName)
        skipped = getattr(self.__class__, "__unittest_skip__", False) or getattr(
            testMethod, "__unittest_skip__", False
        )
    
        # Convert async test methods.
        if iscoroutinefunction(testMethod):
            setattr(self, self._testMethodName, async_to_sync(testMethod))
    
        if not skipped:
            try:
                if self.__class__._pre_setup_ran_eagerly:
                    self.__class__._pre_setup_ran_eagerly = False
                else:
                    self._pre_setup()
            except Exception:
                if debug:
                    raise
                result.addError(self, sys.exc_info())
                return
        if debug:
            super().debug()
        else:
            super().__call__(result)
        if not skipped:
            try:
>               self._post_teardown()

.venv/lib/python3.13.../django/test/testcases.py:379: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oauth2_grafana.TestProviderOAuth2OAuth testMethod=test_authorization_logout>

    def _post_teardown(self):
        """
        Perform post-test things:
        * Flush the contents of the database to leave a clean slate. If the
          class has an 'available_apps' attribute, don't fire post_migrate.
        * Force-close the connection so the next test gets a clean cursor.
        """
        try:
>           self._fixture_teardown()

.venv/lib/python3.13.../django/test/testcases.py:1231: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oauth2_grafana.TestProviderOAuth2OAuth testMethod=test_authorization_logout>

    def _fixture_teardown(self):
        # Allow TRUNCATE ... CASCADE and don't emit the post_migrate signal
        # when flushing only a subset of the apps
        for db_name in self._databases_names(include_mirrors=False):
            # Flush the database
            inhibit_post_migrate = (
                self.available_apps is not None
                or (  # Inhibit the post_migrate signal when using serialized
                    # rollback to avoid trying to recreate the serialized data.
                    self.serialized_rollback
                    and hasattr(connections[db_name], "_test_serialized_contents")
                )
            )
>           call_command(
                "flush",
                verbosity=0,
                interactive=False,
                database=db_name,
                reset_sequences=False,
                allow_cascade=self.available_apps is not None,
                inhibit_post_migrate=inhibit_post_migrate,
            )

.venv/lib/python3.13.../django/test/testcases.py:1266: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

command_name = 'flush', args = ()
options = {'allow_cascade': False, 'database': 'default', 'inhibit_post_migrate': False, 'interactive': False, ...}
command = <django.core.management.commands.flush.Command object at 0x7f9453821a30>
app_name = 'django.core'
parser = CommandParser(prog=' flush', usage=None, description='Removes ALL DATA from the database, including data added during ....', formatter_class=<class 'django.core.management.base.DjangoHelpFormatter'>, conflict_handler='error', add_help=True)
opt_mapping = {'database': 'database', 'force_color': 'force_color', 'help': 'help', 'no_color': 'no_color', ...}
arg_options = {'allow_cascade': False, 'database': 'default', 'inhibit_post_migrate': False, 'interactive': False, ...}
parse_args = []

    def call_command(command_name, *args, **options):
        """
        Call the given command, with the given options and args/kwargs.
    
        This is the primary API you should use for calling specific commands.
    
        `command_name` may be a string or a command object. Using a string is
        preferred unless the command object is required for further processing or
        testing.
    
        Some examples:
            call_command('migrate')
            call_command('shell', plain=True)
            call_command('sqlmigrate', 'myapp')
    
            from django.core.management.commands import flush
            cmd = flush.Command()
            call_command(cmd, verbosity=0, interactive=False)
            # Do something with cmd ...
        """
        if isinstance(command_name, BaseCommand):
            # Command object passed in.
            command = command_name
            command_name = command.__class__.__module__.split(".")[-1]
        else:
            # Load the command object by name.
            try:
                app_name = get_commands()[command_name]
            except KeyError:
                raise CommandError("Unknown command: %r" % command_name)
    
            if isinstance(app_name, BaseCommand):
                # If the command is already loaded, use it directly.
                command = app_name
            else:
                command = load_command_class(app_name, command_name)
    
        # Simulate argument parsing to get the option defaults (see #10080 for details).
        parser = command.create_parser("", command_name)
        # Use the `dest` option name from the parser option
        opt_mapping = {
            min(s_opt.option_strings).lstrip("-").replace("-", "_"): s_opt.dest
            for s_opt in parser._actions
            if s_opt.option_strings
        }
        arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
        parse_args = []
        for arg in args:
            if isinstance(arg, (list, tuple)):
                parse_args += map(str, arg)
            else:
                parse_args.append(str(arg))
    
        def get_actions(parser):
            # Parser actions and actions from sub-parser choices.
            for opt in parser._actions:
                if isinstance(opt, _SubParsersAction):
                    for sub_opt in opt.choices.values():
                        yield from get_actions(sub_opt)
                else:
                    yield opt
    
        parser_actions = list(get_actions(parser))
        mutually_exclusive_required_options = {
            opt
            for group in parser._mutually_exclusive_groups
            for opt in group._group_actions
            if group.required
        }
        # Any required arguments which are passed in via **options must be passed
        # to parse_args().
        for opt in parser_actions:
            if opt.dest in options and (
                opt.required or opt in mutually_exclusive_required_options
            ):
                opt_dest_count = sum(v == opt.dest for v in opt_mapping.values())
                if opt_dest_count > 1:
                    raise TypeError(
                        f"Cannot pass the dest {opt.dest!r} that matches multiple "
                        f"arguments via **options."
                    )
                parse_args.append(min(opt.option_strings))
                if isinstance(opt, (_AppendConstAction, _CountAction, _StoreConstAction)):
                    continue
                value = arg_options[opt.dest]
                if isinstance(value, (list, tuple)):
                    parse_args += map(str, value)
                else:
                    parse_args.append(str(value))
        defaults = parser.parse_args(args=parse_args)
        defaults = dict(defaults._get_kwargs(), **arg_options)
        # Raise an error if any unknown options were passed.
        stealth_options = set(command.base_stealth_options + command.stealth_options)
        dest_parameters = {action.dest for action in parser_actions}
        valid_options = (dest_parameters | stealth_options).union(opt_mapping)
        unknown_options = set(options) - valid_options
        if unknown_options:
            raise TypeError(
                "Unknown option(s) for %s command: %s. "
                "Valid options are: %s."
                % (
                    command_name,
                    ", ".join(sorted(unknown_options)),
                    ", ".join(sorted(valid_options)),
                )
            )
        # Move positional args out of options to mimic legacy optparse
        args = defaults.pop("args", ())
        if "skip_checks" not in options:
            defaults["skip_checks"] = True
    
>       return command.execute(*args, **defaults)

.venv/lib/python3.13.../core/management/__init__.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.core.management.commands.flush.Command object at 0x7f9453821a30>
args = ()
options = {'allow_cascade': False, 'database': 'default', 'force_color': False, 'inhibit_post_migrate': False, ...}

    def execute(self, *args, **options):
        """
        Try to execute this command, performing system checks if needed (as
        controlled by the ``requires_system_checks`` attribute, except if
        force-skipped).
        """
        if options["force_color"] and options["no_color"]:
            raise CommandError(
                "The --no-color and --force-color options can't be used together."
            )
        if options["force_color"]:
            self.style = color_style(force_color=True)
        elif options["no_color"]:
            self.style = no_style()
            self.stderr.style_func = None
        if options.get("stdout"):
            self.stdout = OutputWrapper(options["stdout"])
        if options.get("stderr"):
            self.stderr = OutputWrapper(options["stderr"])
    
        if self.requires_system_checks and not options["skip_checks"]:
            check_kwargs = self.get_check_kwargs(options)
            self.check(**check_kwargs)
        if self.requires_migrations_checks:
            self.check_migrations()
>       output = self.handle(*args, **options)

.venv/lib/python3.13.../core/management/base.py:460: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.core.management.commands.flush.Command object at 0x7f9453821a30>
options = {'allow_cascade': False, 'database': 'default', 'force_color': False, 'inhibit_post_migrate': False, ...}
database = 'default'
connection = <DatabaseWrapper vendor='postgresql' alias='default'>
verbosity = 0, interactive = False, reset_sequences = False
allow_cascade = False, inhibit_post_migrate = False

        def handle(self, **options):
            database = options["database"]
            connection = connections[database]
            verbosity = options["verbosity"]
            interactive = options["interactive"]
            # The following are stealth options used by Django's internals.
            reset_sequences = options.get("reset_sequences", True)
            allow_cascade = options.get("allow_cascade", False)
            inhibit_post_migrate = options.get("inhibit_post_migrate", False)
    
            self.style = no_style()
    
            # Import the 'management' module within each installed app, to register
            # dispatcher events.
            for app_config in apps.get_app_configs():
                try:
                    import_module(".management", app_config.name)
                except ImportError:
                    pass
    
            sql_list = sql_flush(
                self.style,
                connection,
                reset_sequences=reset_sequences,
                allow_cascade=allow_cascade,
            )
    
            if interactive:
                confirm = input(
                    """You have requested a flush of the database.
    This will IRREVERSIBLY DESTROY all data currently in the "%s" database,
    and return each table to an empty state.
    Are you sure you want to do this?
    
        Type 'yes' to continue, or 'no' to cancel: """
                    % connection.settings_dict["NAME"]
                )
            else:
                confirm = "yes"
    
            if confirm == "yes":
                try:
                    connection.ops.execute_sql_flush(sql_list)
                except Exception as exc:
>                   raise CommandError(
                        "Database %s couldn't be flushed. Possible reasons:\n"
                        "  * The database isn't running or isn't configured correctly.\n"
                        "  * At least one of the expected database tables doesn't exist.\n"
                        "  * The SQL was invalid.\n"
                        "Hint: Look at the output of 'django-admin sqlflush'. "
                        "That's the SQL this command wasn't able to run."
                        % (connection.settings_dict["NAME"],)
                    ) from exc
E                   django.core.management.base.CommandError: Database test_authentik couldn't be flushed. Possible reasons:
E                     * The database isn't running or isn't configured correctly.
E                     * At least one of the expected database tables doesn't exist.
E                     * The SQL was invalid.
E                   Hint: Look at the output of 'django-admin sqlflush'. That's the SQL this command wasn't able to run.

.venv/lib/python3.13.../management/commands/flush.py:76: CommandError
tests.e2e.test_provider_saml.TestProviderSAML::test_sp_initiated_explicit
Stack Traces | 49.7s run time
self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit(self):
        """test SAML Provider flow SP-initiated flow (explicit consent)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:249: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f6a34feb3b67ff8a9d82b09421881089", element="f.BF2F796F40AD5AE225CAAB4AD2F49A0B.d.1304DEDB7DFC2F1373055A9190E5ED04.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab96a36430>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab9e323b00>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f6a34feb3b67ff8a9d82b09421881089", element="f.BF2F796F40AD5AE225CAAB4AD2F49A0B.d.1304DEDB7DFC2F1373055A9190E5ED04.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f6a34feb3b67ff8a9d82b09421881089", element="f.BF2F796F40AD5AE225CAAB4AD2F49A0B.d.1304DEDB7DFC2F1373055A9190E5ED04.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f6a34feb3b67ff8a9d82b09421881089", element="f.BF2F796F40AD5AE225CAAB4AD2F49A0B.d.1304DEDB7DFC2F1373055A9190E5ED04.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.BF2F796F40AD5AE225CAAB4AD2F49A0B.d.1304DEDB7DFC2F1373055A9190E5ED04.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="f6a34feb3b67ff8a9d82b09421881089")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab9e080450>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55daf422fcd5 \\u003Cunknown>\\n#28 0x55daf4241709 \\u003Cunknown>\\n#29 0x7f1da5c3e428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55daf4242432 <unknown>
E       #1 0x55daf3c8771b <unknown>
E       #2 0x55daf3c9b207 <unknown>
E       #3 0x55daf3c99e10 <unknown>
E       #4 0x55daf3c9b719 <unknown>
E       #5 0x55daf3c8ec64 <unknown>
E       #6 0x55daf3c8d873 <unknown>
E       #7 0x55daf3c90d6f <unknown>
E       #8 0x55daf3c90e55 <unknown>
E       #9 0x55daf3cd6da4 <unknown>
E       #10 0x55daf3cd77d5 <unknown>
E       #11 0x55daf3ccbcaa <unknown>
E       #12 0x55daf3cfbee1 <unknown>
E       #13 0x55daf3ccbb81 <unknown>
E       #14 0x55daf3cfc0a2 <unknown>
E       #15 0x55daf3d1db62 <unknown>
E       #16 0x55daf3cfbc67 <unknown>
E       #17 0x55daf3cca047 <unknown>
E       #18 0x55daf3ccae15 <unknown>
E       #19 0x55daf420d064 <unknown>
E       #20 0x55daf4210354 <unknown>
E       #21 0x55daf420fe0e <unknown>
E       #22 0x55daf42107e9 <unknown>
E       #23 0x55daf41f6a7a <unknown>
E       #24 0x55daf4210b5a <unknown>
E       #25 0x55daf41df629 <unknown>
E       #26 0x55daf422fae9 <unknown>
E       #27 0x55daf422fcd5 <unknown>
E       #28 0x55daf4241709 <unknown>
E       #29 0x7f1da5c3e428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit(self):
        """test SAML Provider flow SP-initiated flow (explicit consent)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:249: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="2ba8b1af8de3917d00afd2ebf59a50ce", element="f.E825AD1101CDC8076BBF81BEF5BDA86E.d.2C21F25EFE12624BCB70DDF16B47376D.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab96c4be00>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab9523e480>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="2ba8b1af8de3917d00afd2ebf59a50ce", element="f.E825AD1101CDC8076BBF81BEF5BDA86E.d.2C21F25EFE12624BCB70DDF16B47376D.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="2ba8b1af8de3917d00afd2ebf59a50ce", element="f.E825AD1101CDC8076BBF81BEF5BDA86E.d.2C21F25EFE12624BCB70DDF16B47376D.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="2ba8b1af8de3917d00afd2ebf59a50ce", element="f.E825AD1101CDC8076BBF81BEF5BDA86E.d.2C21F25EFE12624BCB70DDF16B47376D.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.E825AD1101CDC8076BBF81BEF5BDA86E.d.2C21F25EFE12624BCB70DDF16B47376D.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="2ba8b1af8de3917d00afd2ebf59a50ce")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab9e332120>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55cd152accd5 \\u003Cunknown>\\n#28 0x55cd152be709 \\u003Cunknown>\\n#29 0x7fc1989df428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55cd152bf432 <unknown>
E       #1 0x55cd14d0471b <unknown>
E       #2 0x55cd14d18207 <unknown>
E       #3 0x55cd14d16e10 <unknown>
E       #4 0x55cd14d18719 <unknown>
E       #5 0x55cd14d0bc64 <unknown>
E       #6 0x55cd14d0a873 <unknown>
E       #7 0x55cd14d0dd6f <unknown>
E       #8 0x55cd14d0de55 <unknown>
E       #9 0x55cd14d53da4 <unknown>
E       #10 0x55cd14d547d5 <unknown>
E       #11 0x55cd14d48caa <unknown>
E       #12 0x55cd14d78ee1 <unknown>
E       #13 0x55cd14d48b81 <unknown>
E       #14 0x55cd14d790a2 <unknown>
E       #15 0x55cd14d9ab62 <unknown>
E       #16 0x55cd14d78c67 <unknown>
E       #17 0x55cd14d47047 <unknown>
E       #18 0x55cd14d47e15 <unknown>
E       #19 0x55cd1528a064 <unknown>
E       #20 0x55cd1528d354 <unknown>
E       #21 0x55cd1528ce0e <unknown>
E       #22 0x55cd1528d7e9 <unknown>
E       #23 0x55cd15273a7a <unknown>
E       #24 0x55cd1528db5a <unknown>
E       #25 0x55cd1525c629 <unknown>
E       #26 0x55cd152acae9 <unknown>
E       #27 0x55cd152accd5 <unknown>
E       #28 0x55cd152be709 <unknown>
E       #29 0x7fc1989df428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7fab955f0450>
test_case = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
subTest = False

    @contextlib.contextmanager
    def testPartExecutor(self, test_case, subTest=False):
        old_success = self.success
        self.success = True
        try:
>           yield

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
result = <TestCaseFunction test_sp_initiated_explicit>

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None
    
        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                _addSkip(result, self, skip_why)
                return result
    
            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            start_time = time.perf_counter()
            try:
                self._outcome = outcome
    
                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self):
>                       self._callTestMethod(testMethod)

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
method = <bound method TestProviderSAML.test_sp_initiated_explicit of <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>>

    def _callTestMethod(self, method):
>       if method() is not None:

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:606: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
>               raise exc

tests/e2e/utils.py:530: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit(self):
        """test SAML Provider flow SP-initiated flow (explicit consent)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:249: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d0294a4dcd27cb68839675396f28f896", element="f.50CB1FEDE5AA0076135427CD4C5E5C9D.d.E7EA54E7B818AB11A9E617B50DBD7EC6.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab959896a0>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab9e17ad40>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d0294a4dcd27cb68839675396f28f896", element="f.50CB1FEDE5AA0076135427CD4C5E5C9D.d.E7EA54E7B818AB11A9E617B50DBD7EC6.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d0294a4dcd27cb68839675396f28f896", element="f.50CB1FEDE5AA0076135427CD4C5E5C9D.d.E7EA54E7B818AB11A9E617B50DBD7EC6.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d0294a4dcd27cb68839675396f28f896", element="f.50CB1FEDE5AA0076135427CD4C5E5C9D.d.E7EA54E7B818AB11A9E617B50DBD7EC6.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.50CB1FEDE5AA0076135427CD4C5E5C9D.d.E7EA54E7B818AB11A9E617B50DBD7EC6.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="d0294a4dcd27cb68839675396f28f896")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab9e333d40>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55bdfdb23cd5 \\u003Cunknown>\\n#28 0x55bdfdb35709 \\u003Cunknown>\\n#29 0x7f9342957428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55bdfdb36432 <unknown>
E       #1 0x55bdfd57b71b <unknown>
E       #2 0x55bdfd58f207 <unknown>
E       #3 0x55bdfd58de10 <unknown>
E       #4 0x55bdfd58f719 <unknown>
E       #5 0x55bdfd582c64 <unknown>
E       #6 0x55bdfd581873 <unknown>
E       #7 0x55bdfd584d6f <unknown>
E       #8 0x55bdfd584e55 <unknown>
E       #9 0x55bdfd5cada4 <unknown>
E       #10 0x55bdfd5cb7d5 <unknown>
E       #11 0x55bdfd5bfcaa <unknown>
E       #12 0x55bdfd5efee1 <unknown>
E       #13 0x55bdfd5bfb81 <unknown>
E       #14 0x55bdfd5f00a2 <unknown>
E       #15 0x55bdfd611b62 <unknown>
E       #16 0x55bdfd5efc67 <unknown>
E       #17 0x55bdfd5be047 <unknown>
E       #18 0x55bdfd5bee15 <unknown>
E       #19 0x55bdfdb01064 <unknown>
E       #20 0x55bdfdb04354 <unknown>
E       #21 0x55bdfdb03e0e <unknown>
E       #22 0x55bdfdb047e9 <unknown>
E       #23 0x55bdfdaeaa7a <unknown>
E       #24 0x55bdfdb04b5a <unknown>
E       #25 0x55bdfdad3629 <unknown>
E       #26 0x55bdfdb23ae9 <unknown>
E       #27 0x55bdfdb23cd5 <unknown>
E       #28 0x55bdfdb35709 <unknown>
E       #29 0x7f9342957428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException
tests.e2e.test_provider_saml.TestProviderSAML::test_sp_initiated_explicit_post
Stack Traces | 50.9s run time
self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit_post(self):
        """test SAML Provider flow SP-initiated flow (explicit consent) (POST binding)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider, True)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="19e58b42ebcb5aaeae1eea2e0bfe6c8d", element="f.6972A341A8359899D8F17C8686726403.d.99AF5F028F3607B93A747930A28DDC97.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab953c71c0>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab934ca160>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="19e58b42ebcb5aaeae1eea2e0bfe6c8d", element="f.6972A341A8359899D8F17C8686726403.d.99AF5F028F3607B93A747930A28DDC97.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="19e58b42ebcb5aaeae1eea2e0bfe6c8d", element="f.6972A341A8359899D8F17C8686726403.d.99AF5F028F3607B93A747930A28DDC97.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="19e58b42ebcb5aaeae1eea2e0bfe6c8d", element="f.6972A341A8359899D8F17C8686726403.d.99AF5F028F3607B93A747930A28DDC97.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.6972A341A8359899D8F17C8686726403.d.99AF5F028F3607B93A747930A28DDC97.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="19e58b42ebcb5aaeae1eea2e0bfe6c8d")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab9e23a430>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x560038e19cd5 \\u003Cunknown>\\n#28 0x560038e2b709 \\u003Cunknown>\\n#29 0x7f202bcdd428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x560038e2c432 <unknown>
E       #1 0x56003887171b <unknown>
E       #2 0x560038885207 <unknown>
E       #3 0x560038883e10 <unknown>
E       #4 0x560038885719 <unknown>
E       #5 0x560038878c64 <unknown>
E       #6 0x560038877873 <unknown>
E       #7 0x56003887ad6f <unknown>
E       #8 0x56003887ae55 <unknown>
E       #9 0x5600388c0da4 <unknown>
E       #10 0x5600388c17d5 <unknown>
E       #11 0x5600388b5caa <unknown>
E       #12 0x5600388e5ee1 <unknown>
E       #13 0x5600388b5b81 <unknown>
E       #14 0x5600388e60a2 <unknown>
E       #15 0x560038907b62 <unknown>
E       #16 0x5600388e5c67 <unknown>
E       #17 0x5600388b4047 <unknown>
E       #18 0x5600388b4e15 <unknown>
E       #19 0x560038df7064 <unknown>
E       #20 0x560038dfa354 <unknown>
E       #21 0x560038df9e0e <unknown>
E       #22 0x560038dfa7e9 <unknown>
E       #23 0x560038de0a7a <unknown>
E       #24 0x560038dfab5a <unknown>
E       #25 0x560038dc9629 <unknown>
E       #26 0x560038e19ae9 <unknown>
E       #27 0x560038e19cd5 <unknown>
E       #28 0x560038e2b709 <unknown>
E       #29 0x7f202bcdd428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit_post(self):
        """test SAML Provider flow SP-initiated flow (explicit consent) (POST binding)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider, True)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="44201bd72388c9c1768343c13dd98375", element="f.E354FDFC77FF78D82005CAE0404E51A1.d.3F924635F101B92EF25DC40327304FBF.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab934d99b0>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab9e060cc0>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="44201bd72388c9c1768343c13dd98375", element="f.E354FDFC77FF78D82005CAE0404E51A1.d.3F924635F101B92EF25DC40327304FBF.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="44201bd72388c9c1768343c13dd98375", element="f.E354FDFC77FF78D82005CAE0404E51A1.d.3F924635F101B92EF25DC40327304FBF.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="44201bd72388c9c1768343c13dd98375", element="f.E354FDFC77FF78D82005CAE0404E51A1.d.3F924635F101B92EF25DC40327304FBF.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.E354FDFC77FF78D82005CAE0404E51A1.d.3F924635F101B92EF25DC40327304FBF.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="44201bd72388c9c1768343c13dd98375")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab92e38ae0>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55d3004cbcd5 \\u003Cunknown>\\n#28 0x55d3004dd709 \\u003Cunknown>\\n#29 0x7efe3a595428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55d3004de432 <unknown>
E       #1 0x55d2fff2371b <unknown>
E       #2 0x55d2fff37207 <unknown>
E       #3 0x55d2fff35e10 <unknown>
E       #4 0x55d2fff37719 <unknown>
E       #5 0x55d2fff2ac64 <unknown>
E       #6 0x55d2fff29873 <unknown>
E       #7 0x55d2fff2cd6f <unknown>
E       #8 0x55d2fff2ce55 <unknown>
E       #9 0x55d2fff72da4 <unknown>
E       #10 0x55d2fff737d5 <unknown>
E       #11 0x55d2fff67caa <unknown>
E       #12 0x55d2fff97ee1 <unknown>
E       #13 0x55d2fff67b81 <unknown>
E       #14 0x55d2fff980a2 <unknown>
E       #15 0x55d2fffb9b62 <unknown>
E       #16 0x55d2fff97c67 <unknown>
E       #17 0x55d2fff66047 <unknown>
E       #18 0x55d2fff66e15 <unknown>
E       #19 0x55d3004a9064 <unknown>
E       #20 0x55d3004ac354 <unknown>
E       #21 0x55d3004abe0e <unknown>
E       #22 0x55d3004ac7e9 <unknown>
E       #23 0x55d300492a7a <unknown>
E       #24 0x55d3004acb5a <unknown>
E       #25 0x55d30047b629 <unknown>
E       #26 0x55d3004cbae9 <unknown>
E       #27 0x55d3004cbcd5 <unknown>
E       #28 0x55d3004dd709 <unknown>
E       #29 0x7efe3a595428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7fab9e330c80>
test_case = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
subTest = False

    @contextlib.contextmanager
    def testPartExecutor(self, test_case, subTest=False):
        old_success = self.success
        self.success = True
        try:
>           yield

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
result = <TestCaseFunction test_sp_initiated_explicit_post>

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None
    
        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                _addSkip(result, self, skip_why)
                return result
    
            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            start_time = time.perf_counter()
            try:
                self._outcome = outcome
    
                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self):
>                       self._callTestMethod(testMethod)

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
method = <bound method TestProviderSAML.test_sp_initiated_explicit_post of <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>>

    def _callTestMethod(self, method):
>       if method() is not None:

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:606: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
>               raise exc

tests/e2e/utils.py:530: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, file = 'system/providers-saml.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - SAML Provider - Mapp...rosoft..../identity/claims/windowsaccountname"\n      expression: |\n        return request.user.username\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint(
        "default/flow-default-provider-authorization-explicit-consent.yaml",
    )
    @apply_blueprint(
        "system/providers-saml.yaml",
    )
    @reconcile_app("authentik_crypto")
    def test_sp_initiated_explicit_post(self):
        """test SAML Provider flow SP-initiated flow (explicit consent) (POST binding)"""
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider: SAMLProvider = SAMLProvider.objects.create(
            name=generate_id(),
            acs_url="http://localhost:9009/saml/acs",
            audience="authentik-e2e",
            issuer="authentik-e2e",
            sp_binding=SAMLBindings.POST,
            authorization_flow=authorization_flow,
            signing_kp=create_test_cert(),
        )
        provider.property_mappings.set(SAMLPropertyMapping.objects.all())
        provider.save()
        app = Application.objects.create(
            name="SAML",
            slug=generate_id(),
            provider=provider,
        )
        self.setup_client(provider, True)
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_saml.py:344: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_saml.TestProviderSAML testMethod=test_sp_initiated_explicit_post>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f013506223aa30f1ee2edfdb74a00b6d", element="f.95B9C228BE707AD4761F5E3B05DC2572.d.788ECA9614B7454C5349430892725524.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fab956b0980>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fab9e08aa20>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f013506223aa30f1ee2edfdb74a00b6d", element="f.95B9C228BE707AD4761F5E3B05DC2572.d.788ECA9614B7454C5349430892725524.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f013506223aa30f1ee2edfdb74a00b6d", element="f.95B9C228BE707AD4761F5E3B05DC2572.d.788ECA9614B7454C5349430892725524.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="f013506223aa30f1ee2edfdb74a00b6d", element="f.95B9C228BE707AD4761F5E3B05DC2572.d.788ECA9614B7454C5349430892725524.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.95B9C228BE707AD4761F5E3B05DC2572.d.788ECA9614B7454C5349430892725524.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="f013506223aa30f1ee2edfdb74a00b6d")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fab96cee090>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55bd17f71cd5 \\u003Cunknown>\\n#28 0x55bd17f83709 \\u003Cunknown>\\n#29 0x7fde6320d428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55bd17f84432 <unknown>
E       #1 0x55bd179c971b <unknown>
E       #2 0x55bd179dd207 <unknown>
E       #3 0x55bd179dbe10 <unknown>
E       #4 0x55bd179dd719 <unknown>
E       #5 0x55bd179d0c64 <unknown>
E       #6 0x55bd179cf873 <unknown>
E       #7 0x55bd179d2d6f <unknown>
E       #8 0x55bd179d2e55 <unknown>
E       #9 0x55bd17a18da4 <unknown>
E       #10 0x55bd17a197d5 <unknown>
E       #11 0x55bd17a0dcaa <unknown>
E       #12 0x55bd17a3dee1 <unknown>
E       #13 0x55bd17a0db81 <unknown>
E       #14 0x55bd17a3e0a2 <unknown>
E       #15 0x55bd17a5fb62 <unknown>
E       #16 0x55bd17a3dc67 <unknown>
E       #17 0x55bd17a0c047 <unknown>
E       #18 0x55bd17a0ce15 <unknown>
E       #19 0x55bd17f4f064 <unknown>
E       #20 0x55bd17f52354 <unknown>
E       #21 0x55bd17f51e0e <unknown>
E       #22 0x55bd17f527e9 <unknown>
E       #23 0x55bd17f38a7a <unknown>
E       #24 0x55bd17f52b5a <unknown>
E       #25 0x55bd17f21629 <unknown>
E       #26 0x55bd17f71ae9 <unknown>
E       #27 0x55bd17f71cd5 <unknown>
E       #28 0x55bd17f83709 <unknown>
E       #29 0x7fde6320d428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException
tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC::test_authorization_consent_explicit
Stack Traces | 54.6s run time
self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4664649324eba4ca2573ce4dff3be46f", element="f.ED2A64644671A5BF7511ABEFB80D892E.d.50259069F8A580F3B5432D0456F9B939.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fe3f2fc5150>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fe3f3d05300>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4664649324eba4ca2573ce4dff3be46f", element="f.ED2A64644671A5BF7511ABEFB80D892E.d.50259069F8A580F3B5432D0456F9B939.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4664649324eba4ca2573ce4dff3be46f", element="f.ED2A64644671A5BF7511ABEFB80D892E.d.50259069F8A580F3B5432D0456F9B939.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="4664649324eba4ca2573ce4dff3be46f", element="f.ED2A64644671A5BF7511ABEFB80D892E.d.50259069F8A580F3B5432D0456F9B939.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.ED2A64644671A5BF7511ABEFB80D892E.d.50259069F8A580F3B5432D0456F9B939.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="4664649324eba4ca2573ce4dff3be46f")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fe3f414d590>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x55bfab284cd5 \\u003Cunknown>\\n#28 0x55bfab296709 \\u003Cunknown>\\n#29 0x7fb5debbb428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x55bfab297432 <unknown>
E       #1 0x55bfaacdc71b <unknown>
E       #2 0x55bfaacf0207 <unknown>
E       #3 0x55bfaaceee10 <unknown>
E       #4 0x55bfaacf0719 <unknown>
E       #5 0x55bfaace3c64 <unknown>
E       #6 0x55bfaace2873 <unknown>
E       #7 0x55bfaace5d6f <unknown>
E       #8 0x55bfaace5e55 <unknown>
E       #9 0x55bfaad2bda4 <unknown>
E       #10 0x55bfaad2c7d5 <unknown>
E       #11 0x55bfaad20caa <unknown>
E       #12 0x55bfaad50ee1 <unknown>
E       #13 0x55bfaad20b81 <unknown>
E       #14 0x55bfaad510a2 <unknown>
E       #15 0x55bfaad72b62 <unknown>
E       #16 0x55bfaad50c67 <unknown>
E       #17 0x55bfaad1f047 <unknown>
E       #18 0x55bfaad1fe15 <unknown>
E       #19 0x55bfab262064 <unknown>
E       #20 0x55bfab265354 <unknown>
E       #21 0x55bfab264e0e <unknown>
E       #22 0x55bfab2657e9 <unknown>
E       #23 0x55bfab24ba7a <unknown>
E       #24 0x55bfab265b5a <unknown>
E       #25 0x55bfab234629 <unknown>
E       #26 0x55bfab284ae9 <unknown>
E       #27 0x55bfab284cd5 <unknown>
E       #28 0x55bfab296709 <unknown>
E       #29 0x7fb5debbb428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d34c3fd724643e9480901af7eed88a16", element="f.89B6B89C2ED70CC6CC7B509B95184F85.d.89FD81DC1FA00E9308822D1DE8258B09.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fe3f48c2e60>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fe3f3d519e0>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d34c3fd724643e9480901af7eed88a16", element="f.89B6B89C2ED70CC6CC7B509B95184F85.d.89FD81DC1FA00E9308822D1DE8258B09.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d34c3fd724643e9480901af7eed88a16", element="f.89B6B89C2ED70CC6CC7B509B95184F85.d.89FD81DC1FA00E9308822D1DE8258B09.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="d34c3fd724643e9480901af7eed88a16", element="f.89B6B89C2ED70CC6CC7B509B95184F85.d.89FD81DC1FA00E9308822D1DE8258B09.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.89B6B89C2ED70CC6CC7B509B95184F85.d.89FD81DC1FA00E9308822D1DE8258B09.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="d34c3fd724643e9480901af7eed88a16")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fe3f4244190>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x5563b3497cd5 \\u003Cunknown>\\n#28 0x5563b34a9709 \\u003Cunknown>\\n#29 0x7f1d92d63428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x5563b34aa432 <unknown>
E       #1 0x5563b2eef71b <unknown>
E       #2 0x5563b2f03207 <unknown>
E       #3 0x5563b2f01e10 <unknown>
E       #4 0x5563b2f03719 <unknown>
E       #5 0x5563b2ef6c64 <unknown>
E       #6 0x5563b2ef5873 <unknown>
E       #7 0x5563b2ef8d6f <unknown>
E       #8 0x5563b2ef8e55 <unknown>
E       #9 0x5563b2f3eda4 <unknown>
E       #10 0x5563b2f3f7d5 <unknown>
E       #11 0x5563b2f33caa <unknown>
E       #12 0x5563b2f63ee1 <unknown>
E       #13 0x5563b2f33b81 <unknown>
E       #14 0x5563b2f640a2 <unknown>
E       #15 0x5563b2f85b62 <unknown>
E       #16 0x5563b2f63c67 <unknown>
E       #17 0x5563b2f32047 <unknown>
E       #18 0x5563b2f32e15 <unknown>
E       #19 0x5563b3475064 <unknown>
E       #20 0x5563b3478354 <unknown>
E       #21 0x5563b3477e0e <unknown>
E       #22 0x5563b34787e9 <unknown>
E       #23 0x5563b345ea7a <unknown>
E       #24 0x5563b3478b5a <unknown>
E       #25 0x5563b3447629 <unknown>
E       #26 0x5563b3497ae9 <unknown>
E       #27 0x5563b3497cd5 <unknown>
E       #28 0x5563b34a9709 <unknown>
E       #29 0x7f1d92d63428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7fe3f414d450>
test_case = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
subTest = False

    @contextlib.contextmanager
    def testPartExecutor(self, test_case, subTest=False):
        old_success = self.success
        self.success = True
        try:
>           yield

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
result = <TestCaseFunction test_authorization_consent_explicit>

    def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None
    
        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                _addSkip(result, self, skip_why)
                return result
    
            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            start_time = time.perf_counter()
            try:
                self._outcome = outcome
    
                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self):
>                       self._callTestMethod(testMethod)

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
method = <bound method TestProviderOAuth2OIDC.test_authorization_consent_explicit of <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>>

    def _callTestMethod(self, method):
>       if method() is not None:

.../hostedtoolcache/Python/3.13.11........./x64/lib/python3.13/unittest/case.py:606: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
                raise exc
            logger.debug("Retrying on error", exc=exc, test=self)
            self.tearDown()
            self._post_teardown()
            self._pre_setup()
            self.setUp()
>           return wrapper(self, *args, **kwargs)

tests/e2e/utils.py:536: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
            return func(self, *args, **kwargs)
    
        except tuple(exceptions) as exc:
            count += 1
            if count > max_retires:
                logger.debug("Exceeded retry count", exc=exc, test=self)
    
>               raise exc

tests/e2e/utils.py:530: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
args = (), kwargs = {}

    @wraps(func)
    def wrapper(self: TransactionTestCase, *args, **kwargs):
        """Run test again if we're below max_retries, including tearDown and
        setUp. Otherwise raise the error"""
        nonlocal count
        try:
>           return func(self, *args, **kwargs)

tests/e2e/utils.py:523: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'default/flow-default-invalidation-flow.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Invalidation flow\nentries:\n- attrs:\n    designation: invalidation\n    na...0\n    stage: !KeyOf default-invalidation-logout\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-explicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (explicit consent)\nentries:\n- attrs:\n    desi...e: !KeyOf default-provider-authorization-consent\n    target: !KeyOf flow\n  model: authentik_flows.flowstagebinding\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, file = 'system/providers-oauth2.yaml'
content = 'version: 1\nmetadata:\n  labels:\n    blueprints.goauthentik.io/system: "true"\n  name: System - OAuth2 Provider - Sc... application the ability to access the authentik API\n        # on behalf of the authorizing user\n        return {}\n'

    @wraps(func)
    def wrapper(*args, **kwargs):
        for file in files:
            content = BlueprintInstance(path=file).retrieve()
            Importer.from_string(content).apply()
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>,)
kwargs = {}, config = <AuthentikCryptoConfig: authentik_crypto>

    @wraps(func)
    def wrapper(*args, **kwargs):
        config = apps.get_app_config(app_name)
        if isinstance(config, ManagedAppConfig):
            config._on_startup_callback(None)
>       return func(*args, **kwargs)

.../blueprints/tests/__init__.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-explicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_explicit(self):
        """test OpenID Provider flow (default authorization flow with explicit consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-explicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            authorization_flow=authorization_flow,
            client_type=ClientTypes.CONFIDENTIAL,
            client_id=self.client_id,
            client_secret=self.client_secret,
            signing_key=create_test_cert(),
            redirect_uris=[
                RedirectURI(RedirectURIMatchingMode.STRICT, "http://localhost:9009/auth/callback")
            ],
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        app = Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009")
        self.login()
    
        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "ak-flow-executor")))
    
        flow_executor = self.get_shadow_root("ak-flow-executor")
>       consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor)

tests/e2e/test_provider_oidc.py:264: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc.TestProviderOAuth2OIDC testMethod=test_authorization_consent_explicit>
selector = 'ak-stage-consent'
container = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="3b3d3a46d42b5d06566ec5483b30a6f6", element="f.B9C3F8A35D1ADDF97820F9544F214826.d.E687A94FA32BF5B4BAFD357B76E147AD.e.6")>
timeout = 10

    def get_shadow_root(
        self, selector: str, container: WebElement | WebDriver | None = None, timeout: float = 10
    ) -> WebElement:
        """Get the shadow root of a web component specified by `selector`."""
        if not container:
            container = self.driver
        wait = WebDriverWait(container, timeout)
        host: WebElement | None = None
    
        try:
>           host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'ShadowRoot' object has no attribute 'session_id'") raised in repr()] WebDriverWait object at 0x7fe3f3c4a350>
method = <function SeleniumTestCase.get_shadow_root.<locals>.<lambda> at 0x7fe3f486b380>
message = ''

    def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = "") -> T:
        """Wait until the method returns a value that is not False.
    
        Calls the method provided with the driver as an argument until the
        return value does not evaluate to ``False``.
    
        Parameters:
        -----------
        method: callable(WebDriver)
            - A callable object that takes a WebDriver instance as an argument.
    
        message: str
            - Optional message for :exc:`TimeoutException`
    
        Return:
        -------
        object: T
            - The result of the last call to `method`
    
        Raises:
        -------
        TimeoutException
            - If 'method' does not return a truthy value within the WebDriverWait
            object's timeout
    
        Example:
        --------
        >>> from selenium.webdriver.common.by import By
        >>> from selenium.webdriver.support.ui import WebDriverWait
        >>> from selenium.webdriver.support import expected_conditions as EC
    
        # Wait until an element is visible on the page
        >>> wait = WebDriverWait(driver, 10)
        >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId")))
        >>> print(element.text)
        """
        screen = None
        stacktrace = None
    
        end_time = time.monotonic() + self._timeout
        while True:
            try:
>               value = method(self._driver)

.venv/lib/python3.13.../webdriver/support/wait.py:137: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

c = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="3b3d3a46d42b5d06566ec5483b30a6f6", element="f.B9C3F8A35D1ADDF97820F9544F214826.d.E687A94FA32BF5B4BAFD357B76E147AD.e.6")>

>   host = wait.until(lambda c: c.find_element(By.CSS_SELECTOR, selector))

tests/e2e/utils.py:378: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="3b3d3a46d42b5d06566ec5483b30a6f6", element="f.B9C3F8A35D1ADDF97820F9544F214826.d.E687A94FA32BF5B4BAFD357B76E147AD.e.6")>
by = 'css selector', value = 'ak-stage-consent'

    def find_element(self, by: str = By.ID, value: str = None):
        """Find an element inside a shadow root given a By strategy and
        locator.
    
        Parameters:
        -----------
        by : selenium.webdriver.common.by.By
            The locating strategy to use. Default is `By.ID`. Supported values include:
            - By.ID: Locate by element ID.
            - By.NAME: Locate by the `name` attribute.
            - By.XPATH: Locate by an XPath expression.
            - By.CSS_SELECTOR: Locate by a CSS selector.
            - By.CLASS_NAME: Locate by the `class` attribute.
            - By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
            - By.LINK_TEXT: Locate a link element by its exact text.
            - By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
            - RelativeBy: Locate elements relative to a specified root element.
    
        Example:
        --------
        element = driver.find_element(By.ID, 'foo')
    
        Returns:
        -------
        WebElement
            The first matching `WebElement` found on the page.
        """
        if by == By.ID:
            by = By.CSS_SELECTOR
            value = f'[id="{value}"]'
        elif by == By.CLASS_NAME:
            by = By.CSS_SELECTOR
            value = f".{value}"
        elif by == By.NAME:
            by = By.CSS_SELECTOR
            value = f'[name="{value}"]'
    
>       return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:79: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.shadowroot.ShadowRoot (session="3b3d3a46d42b5d06566ec5483b30a6f6", element="f.B9C3F8A35D1ADDF97820F9544F214826.d.E687A94FA32BF5B4BAFD357B76E147AD.e.6")>
command = 'findElementFromShadowRoot'
params = {'shadowId': 'f.B9C3F8A35D1ADDF97820F9544F214826.d.E687A94FA32BF5B4BAFD357B76E147AD.e.6', 'using': 'css selector', 'value': 'ak-stage-consent'}

    def _execute(self, command, params=None):
        """Executes a command against the underlying HTML element.
    
        Args:
          command: The name of the command to _execute as a string.
          params: A dictionary of named parameters to send with the command.
    
        Returns:
          The command's JSON response loaded into a dictionary object.
        """
        if not params:
            params = {}
        params["shadowId"] = self._id
>       return self.session.execute(command, params)

.venv/lib/python3.13.../webdriver/remote/shadowroot.py:133: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.webdriver.WebDriver (session="3b3d3a46d42b5d06566ec5483b30a6f6")>
driver_command = 'findElementFromShadowRoot'
params = {'using': 'css selector', 'value': 'ak-stage-consent'}

    def execute(self, driver_command: str, params: dict = None) -> dict:
        """Sends a command to be executed by a command.CommandExecutor.
    
        Parameters:
        -----------
        driver_command : str
            - The name of the command to execute as a string.
    
        params : dict
            - A dictionary of named Parameters to send with the command.
    
        Returns:
        --------
          dict - The command's JSON response loaded into a dictionary object.
        """
        params = self._wrap_value(params)
    
        if self.session_id:
            if not params:
                params = {"sessionId": self.session_id}
            elif "sessionId" not in params:
                params["sessionId"] = self.session_id
    
        response = self.command_executor.execute(driver_command, params)
        if response:
>           self.error_handler.check_response(response)

.venv/lib/python3.13.../webdriver/remote/webdriver.py:448: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7fe3f3d8ce90>
response = {'status': 404, 'value': '{"value":{"error":"detached shadow root","message":"detached shadow root: detached shadow ro...\\n#27 0x5579e2945cd5 \\u003Cunknown>\\n#28 0x5579e2957709 \\u003Cunknown>\\n#29 0x7f78da089428 \\u003Cunknown>\\n"}}'}

    def check_response(self, response: Dict[str, Any]) -> None:
        """Checks that a JSON response from the WebDriver does not have an
        error.
    
        :Args:
         - response - The JSON response from the WebDriver server as a dictionary
           object.
    
        :Raises: If the response contains an error message.
        """
        status = response.get("status", None)
        if not status or status == ErrorCode.SUCCESS:
            return
        value = None
        message = response.get("message", "")
        screen: str = response.get("screen", "")
        stacktrace = None
        if isinstance(status, int):
            value_json = response.get("value", None)
            if value_json and isinstance(value_json, str):
                import json
    
                try:
                    value = json.loads(value_json)
                    if len(value) == 1:
                        value = value["value"]
                    status = value.get("error", None)
                    if not status:
                        status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                        message = value.get("value") or value.get("message")
                        if not isinstance(message, str):
                            value = message
                            message = message.get("message")
                    else:
                        message = value.get("message", None)
                except ValueError:
                    pass
    
        exception_class: Type[WebDriverException]
        e = ErrorCode()
        error_codes = [item for item in dir(e) if not item.startswith("__")]
        for error_code in error_codes:
            error_info = getattr(ErrorCode, error_code)
            if isinstance(error_info, list) and status in error_info:
                exception_class = getattr(ExceptionMapping, error_code, WebDriverException)
                break
        else:
            exception_class = WebDriverException
    
        if not value:
            value = response["value"]
        if isinstance(value, str):
            raise exception_class(value)
        if message == "" and "message" in value:
            message = value["message"]
    
        screen = None  # type: ignore[assignment]
        if "screen" in value:
            screen = value["screen"]
    
        stacktrace = None
        st_value = value.get("stackTrace") or value.get("stacktrace")
        if st_value:
            if isinstance(st_value, str):
                stacktrace = st_value.split("\n")
            else:
                stacktrace = []
                try:
                    for frame in st_value:
                        line = frame.get("lineNumber", "")
                        file = frame.get("fileName", "<anonymous>")
                        if line:
                            file = f"{file}:{line}"
                        meth = frame.get("methodName", "<anonymous>")
                        if "className" in frame:
                            meth = f"{frame['className']}.{meth}"
                        msg = "    at %s (%s)"
                        msg = msg % (meth, file)
                        stacktrace.append(msg)
                except TypeError:
                    pass
        if exception_class == UnexpectedAlertPresentException:
            alert_text = None
            if "data" in value:
                alert_text = value["data"].get("text")
            elif "alert" in value:
                alert_text = value["alert"].get("text")
            raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
>       raise exception_class(message, screen, stacktrace)
E       selenium.common.exceptions.DetachedShadowRootException: Message: detached shadow root: detached shadow root not found
E         (Session info: chrome=143.0.7499.169)
E       Stacktrace:
E       #0 0x5579e2958432 <unknown>
E       #1 0x5579e239d71b <unknown>
E       #2 0x5579e23b1207 <unknown>
E       #3 0x5579e23afe10 <unknown>
E       #4 0x5579e23b1719 <unknown>
E       #5 0x5579e23a4c64 <unknown>
E       #6 0x5579e23a3873 <unknown>
E       #7 0x5579e23a6d6f <unknown>
E       #8 0x5579e23a6e55 <unknown>
E       #9 0x5579e23ecda4 <unknown>
E       #10 0x5579e23ed7d5 <unknown>
E       #11 0x5579e23e1caa <unknown>
E       #12 0x5579e2411ee1 <unknown>
E       #13 0x5579e23e1b81 <unknown>
E       #14 0x5579e24120a2 <unknown>
E       #15 0x5579e2433b62 <unknown>
E       #16 0x5579e2411c67 <unknown>
E       #17 0x5579e23e0047 <unknown>
E       #18 0x5579e23e0e15 <unknown>
E       #19 0x5579e2923064 <unknown>
E       #20 0x5579e2926354 <unknown>
E       #21 0x5579e2925e0e <unknown>
E       #22 0x5579e29267e9 <unknown>
E       #23 0x5579e290ca7a <unknown>
E       #24 0x5579e2926b5a <unknown>
E       #25 0x5579e28f5629 <unknown>
E       #26 0x5579e2945ae9 <unknown>
E       #27 0x5579e2945cd5 <unknown>
E       #28 0x5579e2957709 <unknown>
E       #29 0x7f78da089428 <unknown>

.venv/lib/python3.13.../webdriver/remote/errorhandler.py:232: DetachedShadowRootException

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@BeryJu BeryJu merged commit 6b27b37 into main Jan 5, 2026
91 of 96 checks passed
@BeryJu BeryJu deleted the website/integrations/grafana-tf-expand branch January 5, 2026 16:25
kensternberg-authentik added a commit that referenced this pull request Jan 5, 2026
* main: (43 commits)
  web/maintenance: no missing element type definitions (#18950)
  core: add prettier failure on duplicate group names (#18941)
  website/integrations: make grafana terraform section expand (#19192)
  lib: update error logging (#18628)
  core, web: update translations (#19179)
  web: bump @formatjs/intl-listformat from 8.1.0 to 8.1.1 in /web (#19182)
  ci: bump getsentry/action-release from 3.4.0 to 3.5.0 (#19183)
  web: bump knip from 5.78.0 to 5.79.0 in /web (#19181)
  lifecycle: fix migration conn_options for psycopg connection (#19134)
  website/docs: remove duplicates in slo docs (#19170)
  web/admin: adjust sync threshold, add tooltip (#19131)
  web: Fix user library colors, modal z-indexes, table progress bars (#19152)
  web: fix slug auto-updating when editing existing applications (#19169)
  core: handle deserialization errors from FileField migration (#19067)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#19137)
  website/integrations: vaultwarden: add custom email scope (#19160)
  ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 (#19138)
  core, web: update translations (#19135)
  web: bump globals from 16.5.0 to 17.0.0 in /web (#19154)
  web/user: fix consent delete form missing details (#19147)
  ...
kensternberg-authentik added a commit that referenced this pull request Jan 6, 2026
* main: (38 commits)
  web/maintenance: no missing element type definitions (#18950)
  core: add prettier failure on duplicate group names (#18941)
  website/integrations: make grafana terraform section expand (#19192)
  lib: update error logging (#18628)
  core, web: update translations (#19179)
  web: bump @formatjs/intl-listformat from 8.1.0 to 8.1.1 in /web (#19182)
  ci: bump getsentry/action-release from 3.4.0 to 3.5.0 (#19183)
  web: bump knip from 5.78.0 to 5.79.0 in /web (#19181)
  lifecycle: fix migration conn_options for psycopg connection (#19134)
  website/docs: remove duplicates in slo docs (#19170)
  web/admin: adjust sync threshold, add tooltip (#19131)
  web: Fix user library colors, modal z-indexes, table progress bars (#19152)
  web: fix slug auto-updating when editing existing applications (#19169)
  core: handle deserialization errors from FileField migration (#19067)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#19137)
  website/integrations: vaultwarden: add custom email scope (#19160)
  ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 (#19138)
  core, web: update translations (#19135)
  web: bump globals from 16.5.0 to 17.0.0 in /web (#19154)
  web/user: fix consent delete form missing details (#19147)
  ...
kensternberg-authentik added a commit that referenced this pull request Jan 12, 2026
* main: (24 commits)
  web/maintenance: no missing element type definitions (#18950)
  core: add prettier failure on duplicate group names (#18941)
  website/integrations: make grafana terraform section expand (#19192)
  lib: update error logging (#18628)
  core, web: update translations (#19179)
  web: bump @formatjs/intl-listformat from 8.1.0 to 8.1.1 in /web (#19182)
  ci: bump getsentry/action-release from 3.4.0 to 3.5.0 (#19183)
  web: bump knip from 5.78.0 to 5.79.0 in /web (#19181)
  lifecycle: fix migration conn_options for psycopg connection (#19134)
  website/docs: remove duplicates in slo docs (#19170)
  web/admin: adjust sync threshold, add tooltip (#19131)
  web: Fix user library colors, modal z-indexes, table progress bars (#19152)
  web: fix slug auto-updating when editing existing applications (#19169)
  core: handle deserialization errors from FileField migration (#19067)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#19137)
  website/integrations: vaultwarden: add custom email scope (#19160)
  ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 (#19138)
  core, web: update translations (#19135)
  web: bump globals from 16.5.0 to 17.0.0 in /web (#19154)
  web/user: fix consent delete form missing details (#19147)
  ...
enchantednatures pushed a commit to enchantednatures/HomeCluster that referenced this pull request Feb 26, 2026
> ℹ️ **Note**
> 
> This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [authentik](https://goauthentik.io)
([source](https://redirect.github.com/goauthentik/helm)) | major |
`2025.12.4` → `2026.2.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/1) for more information.

---

### Release Notes

<details>
<summary>goauthentik/helm (authentik)</summary>

###
[`v2026.2.0`](https://redirect.github.com/goauthentik/helm/releases/tag/authentik-2026.2.0)

[Compare
Source](https://redirect.github.com/goauthentik/helm/compare/authentik-2025.12.4...authentik-2026.2.0)

authentik is an open-source Identity Provider focused on flexibility and
versatility

#### What's Changed

- charts/authentik: add fields auto-added by Kubernetes to httproute by
[@&#8203;cfi2017](https://redirect.github.com/cfi2017) in
[#&#8203;453](https://redirect.github.com/goauthentik/helm/pull/453)
- chore(deps): update helm/kind-action action to v1.14.0 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;454](https://redirect.github.com/goauthentik/helm/pull/454)
- charts/authentik: add note how to set version by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[#&#8203;456](https://redirect.github.com/goauthentik/helm/pull/456)
- charts/authentik: bump to 2026.2.0 by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in [#&#8203;457](https://redirect.github.com/goauthentik/helm/pull/457)

#### New Contributors

- [@&#8203;cfi2017](https://redirect.github.com/cfi2017) made their
first contribution in
[#&#8203;453](https://redirect.github.com/goauthentik/helm/pull/453)

**Full Changelog**:
<https://github.com/goauthentik/helm/compare/authentik-2025.12.4...authentik-2026.2.0>

***

### Full release notes for authentik

See <https://docs.goauthentik.io/docs/releases/2026.2>

#### What's Changed

- root: bump version to 2026.2.0-rc1 by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18794](https://redirect.github.com/goauthentik/authentik/pull/18794)
- tests/e2e: retry detached shadow roots by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18796](https://redirect.github.com/goauthentik/authentik/pull/18796)
- website/release notes: Update v2025.12 release notes by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18797](https://redirect.github.com/goauthentik/authentik/pull/18797)
- web/admin: fix read-only provider selection for application form by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18768](https://redirect.github.com/goauthentik/authentik/pull/18768)
- web: bump the react group across 1 directory with 2 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18775](https://redirect.github.com/goauthentik/authentik/pull/18775)
- web: bump chromedriver from 143.0.0 to 143.0.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18776](https://redirect.github.com/goauthentik/authentik/pull/18776)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18774](https://redirect.github.com/goauthentik/authentik/pull/18774)
- internal: don't warn on empty outpost for embedded by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18786](https://redirect.github.com/goauthentik/authentik/pull/18786)
- lifecycle/aws: bump aws-cdk from 2.1033.0 to 2.1034.0 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18771](https://redirect.github.com/goauthentik/authentik/pull/18771)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18804](https://redirect.github.com/goauthentik/authentik/pull/18804)
- root: Add macOS support for sed in Makefile by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18795](https://redirect.github.com/goauthentik/authentik/pull/18795)
- ci: bump astral-sh/setup-uv from 7.1.5 to 7.1.6 in
/.github/actions/setup by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18826](https://redirect.github.com/goauthentik/authentik/pull/18826)
- ci: bump actions/upload-artifact from 5.0.0 to 6.0.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18824](https://redirect.github.com/goauthentik/authentik/pull/18824)
- core: bump goauthentik/fips-debian from `07f41ce` to `c10cd2c` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18822](https://redirect.github.com/goauthentik/authentik/pull/18822)
- ci: bump actions/download-artifact from 6.0.0 to 7.0.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18825](https://redirect.github.com/goauthentik/authentik/pull/18825)
- ci: bump actions/cache from 5.0.0 to 5.0.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18823](https://redirect.github.com/goauthentik/authentik/pull/18823)
- core: list applications fix by
[@&#8203;ryanpesek](https://redirect.github.com/ryanpesek) in
[goauthentik/authentik#18798](https://redirect.github.com/goauthentik/authentik/pull/18798)
- website/docs: add icon info to style guide by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18832](https://redirect.github.com/goauthentik/authentik/pull/18832)
- core: remove superuser check from `Token` list by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18684](https://redirect.github.com/goauthentik/authentik/pull/18684)
- packages/django-dramatiq-postgres: broker: close django connections on
consumer close by [@&#8203;rissson](https://redirect.github.com/rissson)
in
[goauthentik/authentik#18833](https://redirect.github.com/goauthentik/authentik/pull/18833)
- core: bump goauthentik.io/api/v3 from 3.2025120.26 to 3.2026020.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18815](https://redirect.github.com/goauthentik/authentik/pull/18815)
- admin/files: revert add check for /media existence
([#&#8203;18636](https://redirect.github.com/goauthentik/helm/issues/18636))
by [@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18829](https://redirect.github.com/goauthentik/authentik/pull/18829)
- website/docs: add jellyseer integration doc by
[@&#8203;gabay](https://redirect.github.com/gabay) in
[goauthentik/authentik#18812](https://redirect.github.com/goauthentik/authentik/pull/18812)
- crypto: Store details parsed from includeDetails in database instead
by [@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#18013](https://redirect.github.com/goauthentik/authentik/pull/18013)
- core: skip s3 tests if endpoint isn't available by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18841](https://redirect.github.com/goauthentik/authentik/pull/18841)
- admin/files: fix get\_objects\_for\_user queryset argument in
FileUsedByView by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18845](https://redirect.github.com/goauthentik/authentik/pull/18845)
- core: bump goauthentik/fips-debian from `c10cd2c` to `2f19fc1` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18856](https://redirect.github.com/goauthentik/authentik/pull/18856)
- ci: replace codecov test-results action by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18862](https://redirect.github.com/goauthentik/authentik/pull/18862)
- core: add skip s3\_test\_server\_available to
TestResolveFileUrlS3Backend by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18858](https://redirect.github.com/goauthentik/authentik/pull/18858)
- rbac: alter migrated direct permission roles by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18860](https://redirect.github.com/goauthentik/authentik/pull/18860)
- core: bump library/golang from `5d35fb8` to `8e8f9c8` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18855](https://redirect.github.com/goauthentik/authentik/pull/18855)
- web/admin/rbac: misc object permission fixes by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18859](https://redirect.github.com/goauthentik/authentik/pull/18859)
- outposts: fix permission errors for related certificates by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18861](https://redirect.github.com/goauthentik/authentik/pull/18861)
- website/docs: adjust RBAC-related details in 2025.12 release notes by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18863](https://redirect.github.com/goauthentik/authentik/pull/18863)
- website/docs: Add docs for passkey autofill (WebauthN Conditional UI)
by [@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18805](https://redirect.github.com/goauthentik/authentik/pull/18805)
- website/docs: 2025.10.3 release notes by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18868](https://redirect.github.com/goauthentik/authentik/pull/18868)
- web: add custom message with links for empty data export list by
[@&#8203;atereshkin](https://redirect.github.com/atereshkin) in
[goauthentik/authentik#18830](https://redirect.github.com/goauthentik/authentik/pull/18830)
- web: fix notification counter by
[@&#8203;atereshkin](https://redirect.github.com/atereshkin) in
[goauthentik/authentik#18781](https://redirect.github.com/goauthentik/authentik/pull/18781)
- web: bump vite from 7.2.7 to 7.3.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18854](https://redirect.github.com/goauthentik/authentik/pull/18854)
- stages/authenticator\_\*: fix code input field not string by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18875](https://redirect.github.com/goauthentik/authentik/pull/18875)
- web: fix file upload form by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18808](https://redirect.github.com/goauthentik/authentik/pull/18808)
- web/admin: endpoint: change wording and add helper text by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18871](https://redirect.github.com/goauthentik/authentik/pull/18871)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18807](https://redirect.github.com/goauthentik/authentik/pull/18807)
- website/integrations: bookstack: fix redir url by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18891](https://redirect.github.com/goauthentik/authentik/pull/18891)
- core: bump astral-sh/uv from 0.9.17 to 0.9.18 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18898](https://redirect.github.com/goauthentik/authentik/pull/18898)
- core: bump goauthentik/fips-debian from `2f19fc1` to `189345a` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18897](https://redirect.github.com/goauthentik/authentik/pull/18897)
- web: bump knip from 5.73.3 to 5.74.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18896](https://redirect.github.com/goauthentik/authentik/pull/18896)
- web: bump [@&#8203;types/node](https://redirect.github.com/types/node)
from 25.0.0 to 25.0.3 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18895](https://redirect.github.com/goauthentik/authentik/pull/18895)
- web: bump the rollup group across 1 directory with 4 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18852](https://redirect.github.com/goauthentik/authentik/pull/18852)
- web: bump the bundler group across 1 directory with 7 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18894](https://redirect.github.com/goauthentik/authentik/pull/18894)
- web: bump
[@&#8203;sentry/browser](https://redirect.github.com/sentry/browser)
from 10.30.0 to 10.31.0 in /web in the sentry group across 1 directory
by [@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18893](https://redirect.github.com/goauthentik/authentik/pull/18893)
- lifecycle/aws: bump aws-cdk from 2.1034.0 to 2.1100.0 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18850](https://redirect.github.com/goauthentik/authentik/pull/18850)
- web: bump the goauthentik group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18819](https://redirect.github.com/goauthentik/authentik/pull/18819)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18818](https://redirect.github.com/goauthentik/authentik/pull/18818)
- web: bump the eslint group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18851](https://redirect.github.com/goauthentik/authentik/pull/18851)
- core: bump goauthentik.io/api/v3 from 3.2026020.1 to 3.2026020.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18892](https://redirect.github.com/goauthentik/authentik/pull/18892)
- tasks/middleware: close connections on worker status update database
error by [@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18881](https://redirect.github.com/goauthentik/authentik/pull/18881)
- website/docs: added list of Int Guide contributors (also edited
frontmatter) by [@&#8203;tanberry](https://redirect.github.com/tanberry)
in
[goauthentik/authentik#18888](https://redirect.github.com/goauthentik/authentik/pull/18888)
- api: fix page\_size with invalid query param by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18879](https://redirect.github.com/goauthentik/authentik/pull/18879)
- ci/release-tag: checkout correct branch for make test-docker by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18880](https://redirect.github.com/goauthentik/authentik/pull/18880)
- api: fix latest version for public schema by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18902](https://redirect.github.com/goauthentik/authentik/pull/18902)
- website/docs: 2025.12: remove superfluous changes by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18910](https://redirect.github.com/goauthentik/authentik/pull/18910)
- web/admin: reword some things on the device view page by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18785](https://redirect.github.com/goauthentik/authentik/pull/18785)
- core/groups: optimize prefetch queries to fetch only required fields
by [@&#8203;joaocfernandes](https://redirect.github.com/joaocfernandes)
in
[goauthentik/authentik#18448](https://redirect.github.com/goauthentik/authentik/pull/18448)
- root: fix docker-compose data mount by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18903](https://redirect.github.com/goauthentik/authentik/pull/18903)
- web/admin: add UI copy to RBAC modal by
[@&#8203;tanberry](https://redirect.github.com/tanberry) in
[goauthentik/authentik#18917](https://redirect.github.com/goauthentik/authentik/pull/18917)
- tests/e2e: handle StaleElementReferenceException in
parse\_json\_content by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18842](https://redirect.github.com/goauthentik/authentik/pull/18842)
- core: bump goauthentik/fips-debian from `189345a` to `10dadf1` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18927](https://redirect.github.com/goauthentik/authentik/pull/18927)
- web: bump chromedriver from 143.0.1 to 143.0.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18926](https://redirect.github.com/goauthentik/authentik/pull/18926)
- web: bump knip from 5.74.0 to 5.75.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18924](https://redirect.github.com/goauthentik/authentik/pull/18924)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18920](https://redirect.github.com/goauthentik/authentik/pull/18920)
- lifecycle/aws: bump aws-cdk from 2.1100.0 to 2.1100.1 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18922](https://redirect.github.com/goauthentik/authentik/pull/18922)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18923](https://redirect.github.com/goauthentik/authentik/pull/18923)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18817](https://redirect.github.com/goauthentik/authentik/pull/18817)
- stages: remove more global state by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18641](https://redirect.github.com/goauthentik/authentik/pull/18641)
- packages/ak-guardian: cast safely by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18929](https://redirect.github.com/goauthentik/authentik/pull/18929)
- web/flow: Fix spurious double submit on ak-stage-autosubmit by
[@&#8203;dminuoso](https://redirect.github.com/dminuoso) in
[goauthentik/authentik#18727](https://redirect.github.com/goauthentik/authentik/pull/18727)
- website/integrations: Add launch URL for Immich by
[@&#8203;optix2000](https://redirect.github.com/optix2000) in
[goauthentik/authentik#18921](https://redirect.github.com/goauthentik/authentik/pull/18921)
- crypto: fix extra cert data in db migration by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18937](https://redirect.github.com/goauthentik/authentik/pull/18937)
- web/elements: progress-bar and table loading header by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18934](https://redirect.github.com/goauthentik/authentik/pull/18934)
- stages/identification: replace sleep with make\_password by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18883](https://redirect.github.com/goauthentik/authentik/pull/18883)
- website/docs: endpoint devices by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18634](https://redirect.github.com/goauthentik/authentik/pull/18634)
- website/docs: Fix labels, Pre-Release detection by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18945](https://redirect.github.com/goauthentik/authentik/pull/18945)
- website/docs: release notes: add endpoint device links to 2025.12
notes by [@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18940](https://redirect.github.com/goauthentik/authentik/pull/18940)
- website/docs: Fix version parsing. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18948](https://redirect.github.com/goauthentik/authentik/pull/18948)
- web/admin: fix endpoints user binding by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18935](https://redirect.github.com/goauthentik/authentik/pull/18935)
- flows/executor: fix KeyError when session has no existing plan by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18951](https://redirect.github.com/goauthentik/authentik/pull/18951)
- root: move docker files to lifecycle/containers and change
docker-compose to compose by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#16624](https://redirect.github.com/goauthentik/authentik/pull/16624)
- core: bump goauthentik.io/api/v3 from 3.2026020.3 to 3.2026020.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18956](https://redirect.github.com/goauthentik/authentik/pull/18956)
- web: bump chromedriver from 143.0.2 to 143.0.3 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18959](https://redirect.github.com/goauthentik/authentik/pull/18959)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18958](https://redirect.github.com/goauthentik/authentik/pull/18958)
- web: bump
[@&#8203;sentry/browser](https://redirect.github.com/sentry/browser)
from 10.31.0 to 10.32.0 in /web in the sentry group across 1 directory
by [@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18957](https://redirect.github.com/goauthentik/authentik/pull/18957)
- ci: bump actions/attest-build-provenance from 3.0.0 to 3.1.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18960](https://redirect.github.com/goauthentik/authentik/pull/18960)
- website/docs: add note to active directory source doc by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18787](https://redirect.github.com/goauthentik/authentik/pull/18787)
- web/maintenance: fix missing custom web component imports by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18942](https://redirect.github.com/goauthentik/authentik/pull/18942)
- web/maintenance: no unknown tag names by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18944](https://redirect.github.com/goauthentik/authentik/pull/18944)
- website/integrations: Fix path for Cloudflare Access by
[@&#8203;stijn220](https://redirect.github.com/stijn220) in
[goauthentik/authentik#18979](https://redirect.github.com/goauthentik/authentik/pull/18979)
- blueprints: add InternallyManagedMixin instead of large list by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18983](https://redirect.github.com/goauthentik/authentik/pull/18983)
- web/admin: fix dark theme on map by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18985](https://redirect.github.com/goauthentik/authentik/pull/18985)
- web/admin: Fix haveibeenpwned link in PasswordPolicyForm by
[@&#8203;hskrtich](https://redirect.github.com/hskrtich) in
[goauthentik/authentik#18984](https://redirect.github.com/goauthentik/authentik/pull/18984)
- events: notifications live update by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18980](https://redirect.github.com/goauthentik/authentik/pull/18980)
- web: fix Open button selecting row instead of navigating by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18992](https://redirect.github.com/goauthentik/authentik/pull/18992)
- blueprints: fix flaky tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19002](https://redirect.github.com/goauthentik/authentik/pull/19002)
- ci: bump docker/setup-buildx-action from 3.11.1 to 3.12.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18999](https://redirect.github.com/goauthentik/authentik/pull/18999)
- providers/oauth2: Automated OpenID Conformance tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#14785](https://redirect.github.com/goauthentik/authentik/pull/14785)
- enterprise/reports: improve export list, confirmation by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18981](https://redirect.github.com/goauthentik/authentik/pull/18981)
- enterprise/search: add static autocomplete structure by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19008](https://redirect.github.com/goauthentik/authentik/pull/19008)
- website/docs: improve endpoint devices docs by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19007](https://redirect.github.com/goauthentik/authentik/pull/19007)
- web: bump the rollup group across 1 directory with 4 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18994](https://redirect.github.com/goauthentik/authentik/pull/18994)
- core: bump openapitools/openapi-generator-cli from v7.16.0 to v7.18.0
in /scripts/api by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19018](https://redirect.github.com/goauthentik/authentik/pull/19018)
- web: bump globby from 16.0.0 to 16.1.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18995](https://redirect.github.com/goauthentik/authentik/pull/18995)
- api: rework schema generation by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18977](https://redirect.github.com/goauthentik/authentik/pull/18977)
- web/admin: prevent file upload attempt when backend not managed by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18646](https://redirect.github.com/goauthentik/authentik/pull/18646)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19019](https://redirect.github.com/goauthentik/authentik/pull/19019)
- core: bump goauthentik.io/api/v3 from 3.2026020.4 to 3.2026020.5 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19017](https://redirect.github.com/goauthentik/authentik/pull/19017)
- website/docs: Prioritize "Release Candidate" over "Current Release" by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18975](https://redirect.github.com/goauthentik/authentik/pull/18975)
- web: Locale selector UI fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18972](https://redirect.github.com/goauthentik/authentik/pull/18972)
- web: Fix Storybook package resolution with `npm link` by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19016](https://redirect.github.com/goauthentik/authentik/pull/19016)
- core: use chunked\_queryset for expired message deletion by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19028](https://redirect.github.com/goauthentik/authentik/pull/19028)
- web/admin: use consistent icon for inactive user status by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19032](https://redirect.github.com/goauthentik/authentik/pull/19032)
- web: bump lit from 3.3.1 to 3.3.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19038](https://redirect.github.com/goauthentik/authentik/pull/19038)
- web: bump
[@&#8203;lit/reactive-element](https://redirect.github.com/lit/reactive-element)
from 2.1.1 to 2.1.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19037](https://redirect.github.com/goauthentik/authentik/pull/19037)
- web: bump knip from 5.75.1 to 5.77.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19041](https://redirect.github.com/goauthentik/authentik/pull/19041)
- core: bump goauthentik.io/api/v3 from 3.2026020.5 to 3.2026020.6 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19036](https://redirect.github.com/goauthentik/authentik/pull/19036)
- docs/release notes: update 2025.12 release notes by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#19043](https://redirect.github.com/goauthentik/authentik/pull/19043)
- lib/sync: fix sync\_dispatch by
[@&#8203;krejcar25](https://redirect.github.com/krejcar25) in
[goauthentik/authentik#19053](https://redirect.github.com/goauthentik/authentik/pull/19053)
- endpoints/devices: cleanup by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19047](https://redirect.github.com/goauthentik/authentik/pull/19047)
- blueprints: set enrollment token key by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19061](https://redirect.github.com/goauthentik/authentik/pull/19061)
- website/integrations: Add Wallos by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19013](https://redirect.github.com/goauthentik/authentik/pull/19013)
- website/docs: FreeIPA documentation updates by
[@&#8203;borutmrak](https://redirect.github.com/borutmrak) in
[goauthentik/authentik#15183](https://redirect.github.com/goauthentik/authentik/pull/15183)
- \*: Auto compress images by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19065](https://redirect.github.com/goauthentik/authentik/pull/19065)
- blueprints: fix deadlock and task context error in MetaApplyBlueprint
by [@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19033](https://redirect.github.com/goauthentik/authentik/pull/19033)
- web: fix file search input not resetting results properly by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19034](https://redirect.github.com/goauthentik/authentik/pull/19034)
- website/integrations: owncloud: fix php by
[@&#8203;ocmateusz](https://redirect.github.com/ocmateusz) in
[goauthentik/authentik#19073](https://redirect.github.com/goauthentik/authentik/pull/19073)
- tests/e2e: add endpoint tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19072](https://redirect.github.com/goauthentik/authentik/pull/19072)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18991](https://redirect.github.com/goauthentik/authentik/pull/18991)
- web: bump knip from 5.77.0 to 5.77.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19049](https://redirect.github.com/goauthentik/authentik/pull/19049)
- web: bump
[@&#8203;lit/localize-tools](https://redirect.github.com/lit/localize-tools)
from 0.8.0 to 0.8.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19040](https://redirect.github.com/goauthentik/authentik/pull/19040)
- internal: update TLS Suite by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19076](https://redirect.github.com/goauthentik/authentik/pull/19076)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 7.7.13 to 8.1.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19054](https://redirect.github.com/goauthentik/authentik/pull/19054)
- web/admin: fix button alignment on user view page by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19079](https://redirect.github.com/goauthentik/authentik/pull/19079)
- website/docs: endpoints: mention connector key required for stage to
work by [@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19084](https://redirect.github.com/goauthentik/authentik/pull/19084)
- website/docs: rel notes .12: add wallos by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19063](https://redirect.github.com/goauthentik/authentik/pull/19063)
- website/docs: endpoint devices: update features table by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19094](https://redirect.github.com/goauthentik/authentik/pull/19094)
- website/docs: endpoint devices: add path to macos setup by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19093](https://redirect.github.com/goauthentik/authentik/pull/19093)
- web: bump knip from 5.77.1 to 5.78.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19090](https://redirect.github.com/goauthentik/authentik/pull/19090)
- web: fix promoted source button hover losing blue color by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19048](https://redirect.github.com/goauthentik/authentik/pull/19048)
- web: Fix stale flow background by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19015](https://redirect.github.com/goauthentik/authentik/pull/19015)
- Update Vaultwarden documentation by removing warning by
[@&#8203;austin-dudzik](https://redirect.github.com/austin-dudzik) in
[goauthentik/authentik#19102](https://redirect.github.com/goauthentik/authentik/pull/19102)
- web/maintenance/no unknown attributes (part 1) by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18970](https://redirect.github.com/goauthentik/authentik/pull/18970)
- website/integrations: Add Pulse by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19105](https://redirect.github.com/goauthentik/authentik/pull/19105)
- website/integrations: Add Audiobookshelf by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19104](https://redirect.github.com/goauthentik/authentik/pull/19104)
- website/docs: release notes: Add more integrations by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19109](https://redirect.github.com/goauthentik/authentik/pull/19109)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19111](https://redirect.github.com/goauthentik/authentik/pull/19111)
- core: bump library/nginx from `fb01117` to `ad85427` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19112](https://redirect.github.com/goauthentik/authentik/pull/19112)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19110](https://redirect.github.com/goauthentik/authentik/pull/19110)
- web: Fix Impersonation, Lit Reactive Controller Contexts by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19114](https://redirect.github.com/goauthentik/authentik/pull/19114)
- web: Capitalize language display names, code owner fix by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19119](https://redirect.github.com/goauthentik/authentik/pull/19119)
- core: bump library/nginx from `ad85427` to `ca871a8` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19126](https://redirect.github.com/goauthentik/authentik/pull/19126)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19124](https://redirect.github.com/goauthentik/authentik/pull/19124)
- core: bump github.com/jackc/pgx/v5 from 5.7.6 to 5.8.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19088](https://redirect.github.com/goauthentik/authentik/pull/19088)
- core: bump library/node from `ccfd9da` to `03729a7` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19125](https://redirect.github.com/goauthentik/authentik/pull/19125)
- core, web: bump qs from 6.14.0 to 6.14.1 in
/packages/docusaurus-config by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19130](https://redirect.github.com/goauthentik/authentik/pull/19130)
- website/integrations: karakeep: don't hardcode wellknown's slug by
[@&#8203;pippo73](https://redirect.github.com/pippo73) in
[goauthentik/authentik#19127](https://redirect.github.com/goauthentik/authentik/pull/19127)
- web: disable user settings fields when changes are not allowed by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19132](https://redirect.github.com/goauthentik/authentik/pull/19132)
- website/docs: endpoint agent release notes by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19042](https://redirect.github.com/goauthentik/authentik/pull/19042)
- website/docs: fix build by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19148](https://redirect.github.com/goauthentik/authentik/pull/19148)
- web: Token Form Fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19121](https://redirect.github.com/goauthentik/authentik/pull/19121)
- web/user: fix consent delete form missing details by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19147](https://redirect.github.com/goauthentik/authentik/pull/19147)
- web: bump globals from 16.5.0 to 17.0.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19154](https://redirect.github.com/goauthentik/authentik/pull/19154)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19135](https://redirect.github.com/goauthentik/authentik/pull/19135)
- ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19138](https://redirect.github.com/goauthentik/authentik/pull/19138)
- website/integrations: vaultwarden: add custom email scope by
[@&#8203;williamkray](https://redirect.github.com/williamkray) in
[goauthentik/authentik#19160](https://redirect.github.com/goauthentik/authentik/pull/19160)
- stages/authenticator\_webauthn: Update FIDO MDS3 & Passkey aaguid
blobs by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19137](https://redirect.github.com/goauthentik/authentik/pull/19137)
- core: handle deserialization errors from FileField migration by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19067](https://redirect.github.com/goauthentik/authentik/pull/19067)
- web: fix slug auto-updating when editing existing applications by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19169](https://redirect.github.com/goauthentik/authentik/pull/19169)
- web: Fix user library colors, modal z-indexes, table progress bars by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19152](https://redirect.github.com/goauthentik/authentik/pull/19152)
- web/admin: adjust sync threshold, add tooltip by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19131](https://redirect.github.com/goauthentik/authentik/pull/19131)
- website/docs: remove duplicates in slo docs by
[@&#8203;nmasnadithya](https://redirect.github.com/nmasnadithya) in
[goauthentik/authentik#19170](https://redirect.github.com/goauthentik/authentik/pull/19170)
- lifecycle: fix migration conn\_options for psycopg connection by
[@&#8203;D-Tasker207](https://redirect.github.com/D-Tasker207) in
[goauthentik/authentik#19134](https://redirect.github.com/goauthentik/authentik/pull/19134)
- web: bump knip from 5.78.0 to 5.79.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19181](https://redirect.github.com/goauthentik/authentik/pull/19181)
- ci: bump getsentry/action-release from 3.4.0 to 3.5.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19183](https://redirect.github.com/goauthentik/authentik/pull/19183)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 8.1.0 to 8.1.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19182](https://redirect.github.com/goauthentik/authentik/pull/19182)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19179](https://redirect.github.com/goauthentik/authentik/pull/19179)
- lib: update error logging by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#18628](https://redirect.github.com/goauthentik/authentik/pull/18628)
- website/integrations: make grafana terraform section expand by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19192](https://redirect.github.com/goauthentik/authentik/pull/19192)
- core: add prettier failure on duplicate group names by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18941](https://redirect.github.com/goauthentik/authentik/pull/18941)
- web/maintenance: no missing element type definitions by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18950](https://redirect.github.com/goauthentik/authentik/pull/18950)
- root: codespell: ignore Python virtual env, group patterns. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19180](https://redirect.github.com/goauthentik/authentik/pull/19180)
- web: Merge branch -- Stale notifications, synchronized context
objects, rendering fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19141](https://redirect.github.com/goauthentik/authentik/pull/19141)
- website/docs: Add docs for roles by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#19196](https://redirect.github.com/goauthentik/authentik/pull/19196)
- web: Defer table refresh, visibility checks. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19194](https://redirect.github.com/goauthentik/authentik/pull/19194)
- rbac: Add show all to roles tab, add role tab to groups by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#19097](https://redirect.github.com/goauthentik/authentik/pull/19097)
- website/docs: rewrite section about users and perms by
[@&#8203;tanberry](https://redirect.github.com/tanberry) in
[goauthentik/authentik#19195](https://redirect.github.com/goauthentik/authentik/pull/19195)
- core: add last\_login filter to users API by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18993](https://redirect.github.com/goauthentik/authentik/pull/18993)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19205](https://redirect.github.com/goauthentik/authentik/pull/19205)
- admin/files: support %(theme)s variable in media file paths by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19108](https://redirect.github.com/goauthentik/authentik/pull/19108)
- website/integrations: glpi: add step by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19208](https://redirect.github.com/goauthentik/authentik/pull/19208)
- web: bump knip from 5.79.0 to 5.80.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19207](https://redirect.github.com/goauthentik/authentik/pull/19207)
- core: bump axllent/mailpit from v1.28.0 to v1.28.1 in /tests/e2e by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19204](https://redirect.github.com/goauthentik/authentik/pull/19204)
- lifecycle/aws: bump aws-cdk from 2.1100.1 to 2.1100.2 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19203](https://redirect.github.com/goauthentik/authentik/pull/19203)
- core: bump goauthentik.io/api/v3 from 3.2026020.6 to 3.2026020.7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19202](https://redirect.github.com/goauthentik/authentik/pull/19202)
- translate: Updates for project authentik and language pt\_BR by
[@&#8203;transifex-integration](https://redirect.github.com/transifex-integration)\[bot]
in
[goauthentik/authentik#19082](https://redirect.github.com/goauthentik/authentik/pull/19082)
- web/maintenance: lint pass to add missing HTMLElementEventMap entries
by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18953](https://redirect.github.com/goauthentik/authentik/pull/18953)
- outpost/proxyv2: reduce max number of postgres connections by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#19211](https://redirect.github.com/goauthentik/authentik/pull/19211)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19200](https://redirect.github.com/goauthentik/authentik/pull/19200)
- website/docs: revisit endpoint docs the nth by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19116](https://redirect.github.com/goauthentik/authentik/pull/19116)
- ci: bump astral-sh/setup-uv from 7.1.6 to 7.2.0 in
/.github/actions/setup by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19221](https://redirect.github.com/goauthentik/authentik/pull/19221)
- endpoints: include license status in agent config by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19227](https://redirect.github.com/goauthentik/authentik/pull/19227)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 8.1.1 to 8.1.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19220](https://redirect.github.com/goauthentik/authentik/pull/19220)
- lifecycle/aws: bump aws-cdk from 2.1100.2 to 2.1100.3 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19219](https://redirect.github.com/goauthentik/authentik/pull/19219)
- core: bump goauthentik.io/api/v3 from 3.2026020.7 to 3.2026020.8 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19218](https://redirect.github.com/goauthentik/authentik/pull/19218)
- stages/authenticator\_static: set max token length to 100 chars by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19162](https://redirect.github.com/goauthentik/authentik/pull/19162)
- website/glossary: improve by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18969](https://redirect.github.com/goauthentik/authentik/pull/18969)
- core: fix read replica routing during transactions by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19086](https://redirect.github.com/goauthentik/authentik/pull/19086)
- website: Fix typos. by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19243](https://redirect.github.com/goauthentik/authentik/pull/19243)
- core: bump goauthentik.io/api/v3 from 3.2026020.8 to 3.2026020.10 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19242](https://redirect.github.com/goauthentik/authentik/pull/19242)
- website/integrations: update AWS by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#17861](https://redirect.github.com/goauthentik/authentik/pull/17861)
- website/docs: update github social login script example by
[@&#8203;busybox11](https://redirect.github.com/busybox11) in
[goauthentik/authentik#19246](https://redirect.github.com/goauthentik/authentik/pull/19246)
- web: bump vite from 7.3.0 to 7.3.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19245](https://redirect.github.com/goauthentik/authentik/pull/19245)
- web: bump the rollup group across 1 directory with 4 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19206](https://redirect.github.com/goauthentik/authentik/pull/19206)
- stages/prompt: optimize API endpoints by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#19251](https://redirect.github.com/goauthentik/authentik/pull/19251)
- website/docs: update entra id provider docs by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18366](https://redirect.github.com/goauthentik/authentik/pull/18366)
- website/integations: fix aws spelling by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19253](https://redirect.github.com/goauthentik/authentik/pull/19253)
- stages/password: replace session-based retries with reputation by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18643](https://redirect.github.com/goauthentik/authentik/pull/18643)
- web: bump chromedriver from 143.0.3 to 143.0.4 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19244](https://redirect.github.com/goauthentik/authentik/pull/19244)
- web/admin: add banner to flow import form by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19288](https://redirect.github.com/goauthentik/authentik/pull/19288)
- core: bump django from v5.2.9 to 5.2.10 by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#19290](https://redirect.github.com/goauthentik/authentik/pull/19290)
- endpoints: show agent version by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19239](https://redirect.github.com/goauthentik/authentik/pull/19239)
- core: bump urllib3 from 2.5.0 to v2.6.3 by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#19287](https://redirect.github.com/goauthentik/authentik/pull/19287)
- web: bump knip from 5.80.0 to 5.80.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19301](https://redirect.github.com/goauthentik/authentik/pull/19301)
- web: Fix flow inspector advancement event. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19309](https://redirect.github.com/goauthentik/authentik/pull/19309)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19237](https://redirect.github.com/goauthentik/authentik/pull/19237)
- website/docs: deprecate GCDT auth stage by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19306](https://redirect.github.com/goauthentik/authentik/pull/19306)
- website/docs: Fix typo in GitHub OAuth Source instructions by
[@&#8203;tcrasset](https://redirect.github.com/tcrasset) in
[goauthentik/authentik#18936](https://redirect.github.com/goauthentik/authentik/pull/18936)
- website/docs: update m2m doc by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18963](https://redirect.github.com/goauthentik/authentik/pull/18963)
- website/docs: Fix documentation example for
`app_entitlements_attributes`. by
[@&#8203;sebastianw](https://redirect.github.com/sebastianw) in
[goauthentik/authentik#19316](https://redirect.github.com/goauthentik/authentik/pull/19316)
- website/docs: add flow import warnings by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19307](https://redirect.github.com/goauthentik/authentik/pull/19307)
- web: bump pino from 10.1.0 to 10.1.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19333](https://redirect.github.com/goauthentik/authentik/pull/19333)
- web: bump knip from 5.80.1 to 5.80.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19332](https://redirect.github.com/goauthentik/authentik/pull/19332)
- core: bump axllent/mailpit from v1.28.1 to v1.28.2 in /tests/e2e by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19329](https://redirect.github.com/goauthentik/authentik/pull/19329)
- web: bump [@&#8203;types/node](https://redirect.github.com/types/node)
from 25.0.3 to 25.0.6 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19331](https://redirect.github.com/goauthentik/authentik/pull/19331)
- core: bump library/nginx from `ca871a8` to `7272239` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19334](https://redirect.github.com/goauthentik/authentik/pull/19334)
- website/docs: update unique email policy by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19305](https://redirect.github.com/goauthentik/authentik/pull/19305)
- web: bump
[@&#8203;types/react](https://redirect.github.com/types/react) from
19.2.7 to 19.2.8 in /web in the react group across 1 directory by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19330](https://redirect.github.com/goauthentik/authentik/pull/19330)
- web: Images styles, theming by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19233](https://redirect.github.com/goauthentik/authentik/pull/19233)
- website/docs: update LDAP provider docs by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18272](https://redirect.github.com/goauthentik/authentik/pull/18272)
- web: Flow info, localization, back button. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19234](https://redirect.github.com/goauthentik/authentik/pull/19234)
- web: bump type-fest from 5.3.1 to 5.4.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19354](https://redirect.github.com/goauthentik/authentik/pull/19354)
- core: bump goauthentik.io/api/v3 from 3.2026020.10 to 3.2026020.11 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19300](https://redirect.github.com/goauthentik/authentik/pull/19300)
- ci: bump actions/setup-go from 6.1.0 to 6.2.0 in
/.github/actions/setup by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19352](https://redirect.github.com/goauthentik/authentik/pull/19352)
- web: bump the bundler group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19349](https://redirect.github.com/goauthentik/authentik/pull/19349)
- ci: bump actions/setup-go from 6.1.0 to 6.2.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19351](https://redirect.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "every weekend" in timezone
America/New_York, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/enchantednatures/HomeCluster).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4zNi4yIiwidXBkYXRlZEluVmVyIjoiNDMuMzYuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUvaGVsbSIsInR5cGUvbWFqb3IiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
fgeck pushed a commit to fgeck/homelab-k3s that referenced this pull request Mar 10, 2026
> ℹ️ **Note**
> 
> This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [authentik](https://goauthentik.io)
([source](https://redirect.github.com/goauthentik/helm)) | major |
`^2025.0.0` → `^2026.0.0` |

---

### Release Notes

<details>
<summary>goauthentik/helm (authentik)</summary>

###
[`v2026.2.1`](https://redirect.github.com/goauthentik/helm/releases/tag/authentik-2026.2.1)

[Compare
Source](https://redirect.github.com/goauthentik/helm/compare/authentik-2026.2.0...authentik-2026.2.1)

authentik is an open-source Identity Provider focused on flexibility and
versatility

#### What's Changed

- charts/authentik: Add weight to http route service by
[@&#8203;flipper](https://redirect.github.com/flipper) in
[#&#8203;458](https://redirect.github.com/goauthentik/helm/pull/458)
- chore(deps): update docker.io/library/postgres docker tag to v17.9 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;452](https://redirect.github.com/goauthentik/helm/pull/452)
- chore(deps): update prometheuscommunity/postgres-exporter docker tag
to v0.19.1 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;445](https://redirect.github.com/goauthentik/helm/pull/445)
- charts/authentik: bump to 2026.2.1 by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in [#&#8203;459](https://redirect.github.com/goauthentik/helm/pull/459)

#### New Contributors

- [@&#8203;flipper](https://redirect.github.com/flipper) made their
first contribution in
[#&#8203;458](https://redirect.github.com/goauthentik/helm/pull/458)

**Full Changelog**:
<https://github.com/goauthentik/helm/compare/authentik-2026.2.0...authentik-2026.2.1>

***

### Full release notes for authentik

See <https://docs.goauthentik.io/docs/releases/2026.2#fixed-in-202621>

#### What's Changed

- website/docs: update supported versions (cherry-pick
[#&#8203;20534](https://redirect.github.com/goauthentik/helm/issues/20534)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20535](https://redirect.github.com/goauthentik/authentik/pull/20535)
- website/docs: fix upgrade link in `2026.2` release notes (cherry-pick
[#&#8203;20539](https://redirect.github.com/goauthentik/helm/issues/20539)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20542](https://redirect.github.com/goauthentik/authentik/pull/20542)
- website/docs: remove bad logs redirect (cherry-pick
[#&#8203;20522](https://redirect.github.com/goauthentik/helm/issues/20522)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20548](https://redirect.github.com/goauthentik/authentik/pull/20548)
- website/docs: revamp enterprise section (cherry-pick
[#&#8203;20379](https://redirect.github.com/goauthentik/helm/issues/20379)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20546](https://redirect.github.com/goauthentik/authentik/pull/20546)
- docs: fix typos and wording in docs and integrations (cherry-pick
[#&#8203;20550](https://redirect.github.com/goauthentik/helm/issues/20550)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20563](https://redirect.github.com/goauthentik/authentik/pull/20563)
- ci: add `reason` change to versions repo bump (cherry-pick
[#&#8203;20562](https://redirect.github.com/goauthentik/helm/issues/20562)
to version-2026.2) by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#20569](https://redirect.github.com/goauthentik/authentik/pull/20569)
- internal: make http timeouts configurable (cherry-pick
[#&#8203;20472](https://redirect.github.com/goauthentik/helm/issues/20472)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20567](https://redirect.github.com/goauthentik/authentik/pull/20567)
- packages/django-dramatiq-postgres: use fork (cherry-pick
[#&#8203;20606](https://redirect.github.com/goauthentik/helm/issues/20606)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20608](https://redirect.github.com/goauthentik/authentik/pull/20608)
- web/flows: fix source icons being always inverted (cherry-pick
[#&#8203;20419](https://redirect.github.com/goauthentik/helm/issues/20419)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20607](https://redirect.github.com/goauthentik/authentik/pull/20607)
- crypto: fix kid legacy signal (cherry-pick
[#&#8203;20627](https://redirect.github.com/goauthentik/helm/issues/20627)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20628](https://redirect.github.com/goauthentik/authentik/pull/20628)
- sources/ldap: add connection logging & downgrade message (cherry-pick
[#&#8203;20519](https://redirect.github.com/goauthentik/helm/issues/20519)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20636](https://redirect.github.com/goauthentik/authentik/pull/20636)
- packages/django-dramatiq-postgres: fix worker startup on macos
(cherry-pick
[#&#8203;20637](https://redirect.github.com/goauthentik/helm/issues/20637)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20641](https://redirect.github.com/goauthentik/authentik/pull/20641)
- enterprise/wsfed: Fix metadata export and signing logic (cherry-pick
[#&#8203;20643](https://redirect.github.com/goauthentik/helm/issues/20643)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20649](https://redirect.github.com/goauthentik/authentik/pull/20649)
- website/docs: entra id provider: add custom email domain info
(cherry-pick
[#&#8203;20444](https://redirect.github.com/goauthentik/helm/issues/20444)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20660](https://redirect.github.com/goauthentik/authentik/pull/20660)
- website/docs: kerberos: add note about caching (cherry-pick
[#&#8203;20663](https://redirect.github.com/goauthentik/helm/issues/20663)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20664](https://redirect.github.com/goauthentik/authentik/pull/20664)
- core: fix get\_provider returning base Provider instead of subclass
(cherry-pick
[#&#8203;19064](https://redirect.github.com/goauthentik/helm/issues/19064)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20670](https://redirect.github.com/goauthentik/authentik/pull/20670)
- packages/django-channels-postgres: eagerly delete messages
(cherry-pick
[#&#8203;20687](https://redirect.github.com/goauthentik/helm/issues/20687)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20688](https://redirect.github.com/goauthentik/authentik/pull/20688)
- outpost/proxyv2: prevent panic in handleSignOut (cherry-pick
[#&#8203;20097](https://redirect.github.com/goauthentik/helm/issues/20097)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20689](https://redirect.github.com/goauthentik/authentik/pull/20689)
- website/docs: add 2025 pentest (cherry-pick
[#&#8203;20626](https://redirect.github.com/goauthentik/helm/issues/20626)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20691](https://redirect.github.com/goauthentik/authentik/pull/20691)
- web: fix identification stage styling in compatibility mode
(cherry-pick
[#&#8203;20684](https://redirect.github.com/goauthentik/helm/issues/20684)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20694](https://redirect.github.com/goauthentik/authentik/pull/20694)
- providers/proxy: move search path to query instead of runtime
parameter (cherry-pick
[#&#8203;20662](https://redirect.github.com/goauthentik/helm/issues/20662)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20693](https://redirect.github.com/goauthentik/authentik/pull/20693)
- website/docs: add release notes for `2026.2.1` (cherry-pick
[#&#8203;20659](https://redirect.github.com/goauthentik/helm/issues/20659)
to version-2026.2) by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#20695](https://redirect.github.com/goauthentik/authentik/pull/20695)

**Full Changelog**:
<https://github.com/goauthentik/authentik/compare/version/2026.2.0...version/2026.2.1>

###
[`v2026.2.0`](https://redirect.github.com/goauthentik/helm/releases/tag/authentik-2026.2.0)

[Compare
Source](https://redirect.github.com/goauthentik/helm/compare/authentik-2025.12.4...authentik-2026.2.0)

authentik is an open-source Identity Provider focused on flexibility and
versatility

#### What's Changed

- charts/authentik: add fields auto-added by Kubernetes to httproute by
[@&#8203;cfi2017](https://redirect.github.com/cfi2017) in
[#&#8203;453](https://redirect.github.com/goauthentik/helm/pull/453)
- chore(deps): update helm/kind-action action to v1.14.0 by
[@&#8203;renovate](https://redirect.github.com/renovate)\[bot] in
[#&#8203;454](https://redirect.github.com/goauthentik/helm/pull/454)
- charts/authentik: add note how to set version by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[#&#8203;456](https://redirect.github.com/goauthentik/helm/pull/456)
- charts/authentik: bump to 2026.2.0 by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in [#&#8203;457](https://redirect.github.com/goauthentik/helm/pull/457)

#### New Contributors

- [@&#8203;cfi2017](https://redirect.github.com/cfi2017) made their
first contribution in
[#&#8203;453](https://redirect.github.com/goauthentik/helm/pull/453)

**Full Changelog**:
<https://github.com/goauthentik/helm/compare/authentik-2025.12.4...authentik-2026.2.0>

***

### Full release notes for authentik

See <https://docs.goauthentik.io/docs/releases/2026.2>

#### What's Changed

- root: bump version to 2026.2.0-rc1 by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18794](https://redirect.github.com/goauthentik/authentik/pull/18794)
- tests/e2e: retry detached shadow roots by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18796](https://redirect.github.com/goauthentik/authentik/pull/18796)
- website/release notes: Update v2025.12 release notes by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18797](https://redirect.github.com/goauthentik/authentik/pull/18797)
- web/admin: fix read-only provider selection for application form by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18768](https://redirect.github.com/goauthentik/authentik/pull/18768)
- web: bump the react group across 1 directory with 2 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18775](https://redirect.github.com/goauthentik/authentik/pull/18775)
- web: bump chromedriver from 143.0.0 to 143.0.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18776](https://redirect.github.com/goauthentik/authentik/pull/18776)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18774](https://redirect.github.com/goauthentik/authentik/pull/18774)
- internal: don't warn on empty outpost for embedded by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18786](https://redirect.github.com/goauthentik/authentik/pull/18786)
- lifecycle/aws: bump aws-cdk from 2.1033.0 to 2.1034.0 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18771](https://redirect.github.com/goauthentik/authentik/pull/18771)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18804](https://redirect.github.com/goauthentik/authentik/pull/18804)
- root: Add macOS support for sed in Makefile by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18795](https://redirect.github.com/goauthentik/authentik/pull/18795)
- ci: bump astral-sh/setup-uv from 7.1.5 to 7.1.6 in
/.github/actions/setup by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18826](https://redirect.github.com/goauthentik/authentik/pull/18826)
- ci: bump actions/upload-artifact from 5.0.0 to 6.0.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18824](https://redirect.github.com/goauthentik/authentik/pull/18824)
- core: bump goauthentik/fips-debian from `07f41ce` to `c10cd2c` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18822](https://redirect.github.com/goauthentik/authentik/pull/18822)
- ci: bump actions/download-artifact from 6.0.0 to 7.0.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18825](https://redirect.github.com/goauthentik/authentik/pull/18825)
- ci: bump actions/cache from 5.0.0 to 5.0.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18823](https://redirect.github.com/goauthentik/authentik/pull/18823)
- core: list applications fix by
[@&#8203;ryanpesek](https://redirect.github.com/ryanpesek) in
[goauthentik/authentik#18798](https://redirect.github.com/goauthentik/authentik/pull/18798)
- website/docs: add icon info to style guide by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18832](https://redirect.github.com/goauthentik/authentik/pull/18832)
- core: remove superuser check from `Token` list by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18684](https://redirect.github.com/goauthentik/authentik/pull/18684)
- packages/django-dramatiq-postgres: broker: close django connections on
consumer close by [@&#8203;rissson](https://redirect.github.com/rissson)
in
[goauthentik/authentik#18833](https://redirect.github.com/goauthentik/authentik/pull/18833)
- core: bump goauthentik.io/api/v3 from 3.2025120.26 to 3.2026020.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18815](https://redirect.github.com/goauthentik/authentik/pull/18815)
- admin/files: revert add check for /media existence
([#&#8203;18636](https://redirect.github.com/goauthentik/helm/issues/18636))
by [@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18829](https://redirect.github.com/goauthentik/authentik/pull/18829)
- website/docs: add jellyseer integration doc by
[@&#8203;gabay](https://redirect.github.com/gabay) in
[goauthentik/authentik#18812](https://redirect.github.com/goauthentik/authentik/pull/18812)
- crypto: Store details parsed from includeDetails in database instead
by [@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#18013](https://redirect.github.com/goauthentik/authentik/pull/18013)
- core: skip s3 tests if endpoint isn't available by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18841](https://redirect.github.com/goauthentik/authentik/pull/18841)
- admin/files: fix get\_objects\_for\_user queryset argument in
FileUsedByView by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18845](https://redirect.github.com/goauthentik/authentik/pull/18845)
- core: bump goauthentik/fips-debian from `c10cd2c` to `2f19fc1` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18856](https://redirect.github.com/goauthentik/authentik/pull/18856)
- ci: replace codecov test-results action by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18862](https://redirect.github.com/goauthentik/authentik/pull/18862)
- core: add skip s3\_test\_server\_available to
TestResolveFileUrlS3Backend by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18858](https://redirect.github.com/goauthentik/authentik/pull/18858)
- rbac: alter migrated direct permission roles by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18860](https://redirect.github.com/goauthentik/authentik/pull/18860)
- core: bump library/golang from `5d35fb8` to `8e8f9c8` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18855](https://redirect.github.com/goauthentik/authentik/pull/18855)
- web/admin/rbac: misc object permission fixes by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18859](https://redirect.github.com/goauthentik/authentik/pull/18859)
- outposts: fix permission errors for related certificates by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18861](https://redirect.github.com/goauthentik/authentik/pull/18861)
- website/docs: adjust RBAC-related details in 2025.12 release notes by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18863](https://redirect.github.com/goauthentik/authentik/pull/18863)
- website/docs: Add docs for passkey autofill (WebauthN Conditional UI)
by [@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18805](https://redirect.github.com/goauthentik/authentik/pull/18805)
- website/docs: 2025.10.3 release notes by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18868](https://redirect.github.com/goauthentik/authentik/pull/18868)
- web: add custom message with links for empty data export list by
[@&#8203;atereshkin](https://redirect.github.com/atereshkin) in
[goauthentik/authentik#18830](https://redirect.github.com/goauthentik/authentik/pull/18830)
- web: fix notification counter by
[@&#8203;atereshkin](https://redirect.github.com/atereshkin) in
[goauthentik/authentik#18781](https://redirect.github.com/goauthentik/authentik/pull/18781)
- web: bump vite from 7.2.7 to 7.3.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18854](https://redirect.github.com/goauthentik/authentik/pull/18854)
- stages/authenticator\_\*: fix code input field not string by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18875](https://redirect.github.com/goauthentik/authentik/pull/18875)
- web: fix file upload form by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18808](https://redirect.github.com/goauthentik/authentik/pull/18808)
- web/admin: endpoint: change wording and add helper text by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18871](https://redirect.github.com/goauthentik/authentik/pull/18871)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18807](https://redirect.github.com/goauthentik/authentik/pull/18807)
- website/integrations: bookstack: fix redir url by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18891](https://redirect.github.com/goauthentik/authentik/pull/18891)
- core: bump astral-sh/uv from 0.9.17 to 0.9.18 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18898](https://redirect.github.com/goauthentik/authentik/pull/18898)
- core: bump goauthentik/fips-debian from `2f19fc1` to `189345a` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18897](https://redirect.github.com/goauthentik/authentik/pull/18897)
- web: bump knip from 5.73.3 to 5.74.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18896](https://redirect.github.com/goauthentik/authentik/pull/18896)
- web: bump [@&#8203;types/node](https://redirect.github.com/types/node)
from 25.0.0 to 25.0.3 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18895](https://redirect.github.com/goauthentik/authentik/pull/18895)
- web: bump the rollup group across 1 directory with 4 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18852](https://redirect.github.com/goauthentik/authentik/pull/18852)
- web: bump the bundler group across 1 directory with 7 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18894](https://redirect.github.com/goauthentik/authentik/pull/18894)
- web: bump
[@&#8203;sentry/browser](https://redirect.github.com/sentry/browser)
from 10.30.0 to 10.31.0 in /web in the sentry group across 1 directory
by [@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18893](https://redirect.github.com/goauthentik/authentik/pull/18893)
- lifecycle/aws: bump aws-cdk from 2.1034.0 to 2.1100.0 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18850](https://redirect.github.com/goauthentik/authentik/pull/18850)
- web: bump the goauthentik group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18819](https://redirect.github.com/goauthentik/authentik/pull/18819)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18818](https://redirect.github.com/goauthentik/authentik/pull/18818)
- web: bump the eslint group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18851](https://redirect.github.com/goauthentik/authentik/pull/18851)
- core: bump goauthentik.io/api/v3 from 3.2026020.1 to 3.2026020.3 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18892](https://redirect.github.com/goauthentik/authentik/pull/18892)
- tasks/middleware: close connections on worker status update database
error by [@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18881](https://redirect.github.com/goauthentik/authentik/pull/18881)
- website/docs: added list of Int Guide contributors (also edited
frontmatter) by [@&#8203;tanberry](https://redirect.github.com/tanberry)
in
[goauthentik/authentik#18888](https://redirect.github.com/goauthentik/authentik/pull/18888)
- api: fix page\_size with invalid query param by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18879](https://redirect.github.com/goauthentik/authentik/pull/18879)
- ci/release-tag: checkout correct branch for make test-docker by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18880](https://redirect.github.com/goauthentik/authentik/pull/18880)
- api: fix latest version for public schema by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18902](https://redirect.github.com/goauthentik/authentik/pull/18902)
- website/docs: 2025.12: remove superfluous changes by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18910](https://redirect.github.com/goauthentik/authentik/pull/18910)
- web/admin: reword some things on the device view page by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18785](https://redirect.github.com/goauthentik/authentik/pull/18785)
- core/groups: optimize prefetch queries to fetch only required fields
by [@&#8203;joaocfernandes](https://redirect.github.com/joaocfernandes)
in
[goauthentik/authentik#18448](https://redirect.github.com/goauthentik/authentik/pull/18448)
- root: fix docker-compose data mount by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18903](https://redirect.github.com/goauthentik/authentik/pull/18903)
- web/admin: add UI copy to RBAC modal by
[@&#8203;tanberry](https://redirect.github.com/tanberry) in
[goauthentik/authentik#18917](https://redirect.github.com/goauthentik/authentik/pull/18917)
- tests/e2e: handle StaleElementReferenceException in
parse\_json\_content by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#18842](https://redirect.github.com/goauthentik/authentik/pull/18842)
- core: bump goauthentik/fips-debian from `189345a` to `10dadf1` by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18927](https://redirect.github.com/goauthentik/authentik/pull/18927)
- web: bump chromedriver from 143.0.1 to 143.0.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18926](https://redirect.github.com/goauthentik/authentik/pull/18926)
- web: bump knip from 5.74.0 to 5.75.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18924](https://redirect.github.com/goauthentik/authentik/pull/18924)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18920](https://redirect.github.com/goauthentik/authentik/pull/18920)
- lifecycle/aws: bump aws-cdk from 2.1100.0 to 2.1100.1 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18922](https://redirect.github.com/goauthentik/authentik/pull/18922)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18923](https://redirect.github.com/goauthentik/authentik/pull/18923)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18817](https://redirect.github.com/goauthentik/authentik/pull/18817)
- stages: remove more global state by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18641](https://redirect.github.com/goauthentik/authentik/pull/18641)
- packages/ak-guardian: cast safely by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18929](https://redirect.github.com/goauthentik/authentik/pull/18929)
- web/flow: Fix spurious double submit on ak-stage-autosubmit by
[@&#8203;dminuoso](https://redirect.github.com/dminuoso) in
[goauthentik/authentik#18727](https://redirect.github.com/goauthentik/authentik/pull/18727)
- website/integrations: Add launch URL for Immich by
[@&#8203;optix2000](https://redirect.github.com/optix2000) in
[goauthentik/authentik#18921](https://redirect.github.com/goauthentik/authentik/pull/18921)
- crypto: fix extra cert data in db migration by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#18937](https://redirect.github.com/goauthentik/authentik/pull/18937)
- web/elements: progress-bar and table loading header by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18934](https://redirect.github.com/goauthentik/authentik/pull/18934)
- stages/identification: replace sleep with make\_password by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18883](https://redirect.github.com/goauthentik/authentik/pull/18883)
- website/docs: endpoint devices by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18634](https://redirect.github.com/goauthentik/authentik/pull/18634)
- website/docs: Fix labels, Pre-Release detection by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18945](https://redirect.github.com/goauthentik/authentik/pull/18945)
- website/docs: release notes: add endpoint device links to 2025.12
notes by [@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18940](https://redirect.github.com/goauthentik/authentik/pull/18940)
- website/docs: Fix version parsing. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18948](https://redirect.github.com/goauthentik/authentik/pull/18948)
- web/admin: fix endpoints user binding by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18935](https://redirect.github.com/goauthentik/authentik/pull/18935)
- flows/executor: fix KeyError when session has no existing plan by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18951](https://redirect.github.com/goauthentik/authentik/pull/18951)
- root: move docker files to lifecycle/containers and change
docker-compose to compose by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#16624](https://redirect.github.com/goauthentik/authentik/pull/16624)
- core: bump goauthentik.io/api/v3 from 3.2026020.3 to 3.2026020.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18956](https://redirect.github.com/goauthentik/authentik/pull/18956)
- web: bump chromedriver from 143.0.2 to 143.0.3 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18959](https://redirect.github.com/goauthentik/authentik/pull/18959)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18958](https://redirect.github.com/goauthentik/authentik/pull/18958)
- web: bump
[@&#8203;sentry/browser](https://redirect.github.com/sentry/browser)
from 10.31.0 to 10.32.0 in /web in the sentry group across 1 directory
by [@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18957](https://redirect.github.com/goauthentik/authentik/pull/18957)
- ci: bump actions/attest-build-provenance from 3.0.0 to 3.1.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18960](https://redirect.github.com/goauthentik/authentik/pull/18960)
- website/docs: add note to active directory source doc by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#18787](https://redirect.github.com/goauthentik/authentik/pull/18787)
- web/maintenance: fix missing custom web component imports by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18942](https://redirect.github.com/goauthentik/authentik/pull/18942)
- web/maintenance: no unknown tag names by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18944](https://redirect.github.com/goauthentik/authentik/pull/18944)
- website/integrations: Fix path for Cloudflare Access by
[@&#8203;stijn220](https://redirect.github.com/stijn220) in
[goauthentik/authentik#18979](https://redirect.github.com/goauthentik/authentik/pull/18979)
- blueprints: add InternallyManagedMixin instead of large list by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18983](https://redirect.github.com/goauthentik/authentik/pull/18983)
- web/admin: fix dark theme on map by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18985](https://redirect.github.com/goauthentik/authentik/pull/18985)
- web/admin: Fix haveibeenpwned link in PasswordPolicyForm by
[@&#8203;hskrtich](https://redirect.github.com/hskrtich) in
[goauthentik/authentik#18984](https://redirect.github.com/goauthentik/authentik/pull/18984)
- events: notifications live update by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18980](https://redirect.github.com/goauthentik/authentik/pull/18980)
- web: fix Open button selecting row instead of navigating by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18992](https://redirect.github.com/goauthentik/authentik/pull/18992)
- blueprints: fix flaky tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19002](https://redirect.github.com/goauthentik/authentik/pull/19002)
- ci: bump docker/setup-buildx-action from 3.11.1 to 3.12.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18999](https://redirect.github.com/goauthentik/authentik/pull/18999)
- providers/oauth2: Automated OpenID Conformance tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#14785](https://redirect.github.com/goauthentik/authentik/pull/14785)
- enterprise/reports: improve export list, confirmation by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18981](https://redirect.github.com/goauthentik/authentik/pull/18981)
- enterprise/search: add static autocomplete structure by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19008](https://redirect.github.com/goauthentik/authentik/pull/19008)
- website/docs: improve endpoint devices docs by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19007](https://redirect.github.com/goauthentik/authentik/pull/19007)
- web: bump the rollup group across 1 directory with 4 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18994](https://redirect.github.com/goauthentik/authentik/pull/18994)
- core: bump openapitools/openapi-generator-cli from v7.16.0 to v7.18.0
in /scripts/api by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19018](https://redirect.github.com/goauthentik/authentik/pull/19018)
- web: bump globby from 16.0.0 to 16.1.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#18995](https://redirect.github.com/goauthentik/authentik/pull/18995)
- api: rework schema generation by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18977](https://redirect.github.com/goauthentik/authentik/pull/18977)
- web/admin: prevent file upload attempt when backend not managed by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#18646](https://redirect.github.com/goauthentik/authentik/pull/18646)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19019](https://redirect.github.com/goauthentik/authentik/pull/19019)
- core: bump goauthentik.io/api/v3 from 3.2026020.4 to 3.2026020.5 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19017](https://redirect.github.com/goauthentik/authentik/pull/19017)
- website/docs: Prioritize "Release Candidate" over "Current Release" by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18975](https://redirect.github.com/goauthentik/authentik/pull/18975)
- web: Locale selector UI fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#18972](https://redirect.github.com/goauthentik/authentik/pull/18972)
- web: Fix Storybook package resolution with `npm link` by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19016](https://redirect.github.com/goauthentik/authentik/pull/19016)
- core: use chunked\_queryset for expired message deletion by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19028](https://redirect.github.com/goauthentik/authentik/pull/19028)
- web/admin: use consistent icon for inactive user status by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19032](https://redirect.github.com/goauthentik/authentik/pull/19032)
- web: bump lit from 3.3.1 to 3.3.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19038](https://redirect.github.com/goauthentik/authentik/pull/19038)
- web: bump
[@&#8203;lit/reactive-element](https://redirect.github.com/lit/reactive-element)
from 2.1.1 to 2.1.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19037](https://redirect.github.com/goauthentik/authentik/pull/19037)
- web: bump knip from 5.75.1 to 5.77.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19041](https://redirect.github.com/goauthentik/authentik/pull/19041)
- core: bump goauthentik.io/api/v3 from 3.2026020.5 to 3.2026020.6 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19036](https://redirect.github.com/goauthentik/authentik/pull/19036)
- docs/release notes: update 2025.12 release notes by
[@&#8203;melizeche](https://redirect.github.com/melizeche) in
[goauthentik/authentik#19043](https://redirect.github.com/goauthentik/authentik/pull/19043)
- lib/sync: fix sync\_dispatch by
[@&#8203;krejcar25](https://redirect.github.com/krejcar25) in
[goauthentik/authentik#19053](https://redirect.github.com/goauthentik/authentik/pull/19053)
- endpoints/devices: cleanup by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19047](https://redirect.github.com/goauthentik/authentik/pull/19047)
- blueprints: set enrollment token key by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19061](https://redirect.github.com/goauthentik/authentik/pull/19061)
- website/integrations: Add Wallos by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19013](https://redirect.github.com/goauthentik/authentik/pull/19013)
- website/docs: FreeIPA documentation updates by
[@&#8203;borutmrak](https://redirect.github.com/borutmrak) in
[goauthentik/authentik#15183](https://redirect.github.com/goauthentik/authentik/pull/15183)
- \*: Auto compress images by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19065](https://redirect.github.com/goauthentik/authentik/pull/19065)
- blueprints: fix deadlock and task context error in MetaApplyBlueprint
by [@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19033](https://redirect.github.com/goauthentik/authentik/pull/19033)
- web: fix file search input not resetting results properly by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19034](https://redirect.github.com/goauthentik/authentik/pull/19034)
- website/integrations: owncloud: fix php by
[@&#8203;ocmateusz](https://redirect.github.com/ocmateusz) in
[goauthentik/authentik#19073](https://redirect.github.com/goauthentik/authentik/pull/19073)
- tests/e2e: add endpoint tests by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19072](https://redirect.github.com/goauthentik/authentik/pull/19072)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#18991](https://redirect.github.com/goauthentik/authentik/pull/18991)
- web: bump knip from 5.77.0 to 5.77.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19049](https://redirect.github.com/goauthentik/authentik/pull/19049)
- web: bump
[@&#8203;lit/localize-tools](https://redirect.github.com/lit/localize-tools)
from 0.8.0 to 0.8.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19040](https://redirect.github.com/goauthentik/authentik/pull/19040)
- internal: update TLS Suite by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19076](https://redirect.github.com/goauthentik/authentik/pull/19076)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 7.7.13 to 8.1.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19054](https://redirect.github.com/goauthentik/authentik/pull/19054)
- web/admin: fix button alignment on user view page by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19079](https://redirect.github.com/goauthentik/authentik/pull/19079)
- website/docs: endpoints: mention connector key required for stage to
work by [@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19084](https://redirect.github.com/goauthentik/authentik/pull/19084)
- website/docs: rel notes .12: add wallos by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19063](https://redirect.github.com/goauthentik/authentik/pull/19063)
- website/docs: endpoint devices: update features table by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19094](https://redirect.github.com/goauthentik/authentik/pull/19094)
- website/docs: endpoint devices: add path to macos setup by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19093](https://redirect.github.com/goauthentik/authentik/pull/19093)
- web: bump knip from 5.77.1 to 5.78.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19090](https://redirect.github.com/goauthentik/authentik/pull/19090)
- web: fix promoted source button hover losing blue color by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19048](https://redirect.github.com/goauthentik/authentik/pull/19048)
- web: Fix stale flow background by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19015](https://redirect.github.com/goauthentik/authentik/pull/19015)
- Update Vaultwarden documentation by removing warning by
[@&#8203;austin-dudzik](https://redirect.github.com/austin-dudzik) in
[goauthentik/authentik#19102](https://redirect.github.com/goauthentik/authentik/pull/19102)
- web/maintenance/no unknown attributes (part 1) by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18970](https://redirect.github.com/goauthentik/authentik/pull/18970)
- website/integrations: Add Pulse by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19105](https://redirect.github.com/goauthentik/authentik/pull/19105)
- website/integrations: Add Audiobookshelf by
[@&#8203;0skater0](https://redirect.github.com/0skater0) in
[goauthentik/authentik#19104](https://redirect.github.com/goauthentik/authentik/pull/19104)
- website/docs: release notes: Add more integrations by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19109](https://redirect.github.com/goauthentik/authentik/pull/19109)
- web: bump the storybook group across 1 directory with 5 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19111](https://redirect.github.com/goauthentik/authentik/pull/19111)
- core: bump library/nginx from `fb01117` to `ad85427` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19112](https://redirect.github.com/goauthentik/authentik/pull/19112)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19110](https://redirect.github.com/goauthentik/authentik/pull/19110)
- web: Fix Impersonation, Lit Reactive Controller Contexts by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19114](https://redirect.github.com/goauthentik/authentik/pull/19114)
- web: Capitalize language display names, code owner fix by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19119](https://redirect.github.com/goauthentik/authentik/pull/19119)
- core: bump library/nginx from `ad85427` to `ca871a8` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19126](https://redirect.github.com/goauthentik/authentik/pull/19126)
- web: bump the swc group across 1 directory with 11 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19124](https://redirect.github.com/goauthentik/authentik/pull/19124)
- core: bump github.com/jackc/pgx/v5 from 5.7.6 to 5.8.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19088](https://redirect.github.com/goauthentik/authentik/pull/19088)
- core: bump library/node from `ccfd9da` to `03729a7` in /website by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19125](https://redirect.github.com/goauthentik/authentik/pull/19125)
- core, web: bump qs from 6.14.0 to 6.14.1 in
/packages/docusaurus-config by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19130](https://redirect.github.com/goauthentik/authentik/pull/19130)
- website/integrations: karakeep: don't hardcode wellknown's slug by
[@&#8203;pippo73](https://redirect.github.com/pippo73) in
[goauthentik/authentik#19127](https://redirect.github.com/goauthentik/authentik/pull/19127)
- web: disable user settings fields when changes are not allowed by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19132](https://redirect.github.com/goauthentik/authentik/pull/19132)
- website/docs: endpoint agent release notes by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19042](https://redirect.github.com/goauthentik/authentik/pull/19042)
- website/docs: fix build by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19148](https://redirect.github.com/goauthentik/authentik/pull/19148)
- web: Token Form Fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19121](https://redirect.github.com/goauthentik/authentik/pull/19121)
- web/user: fix consent delete form missing details by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19147](https://redirect.github.com/goauthentik/authentik/pull/19147)
- web: bump globals from 16.5.0 to 17.0.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19154](https://redirect.github.com/goauthentik/authentik/pull/19154)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19135](https://redirect.github.com/goauthentik/authentik/pull/19135)
- ci: bump int128/docker-manifest-create-action from 2.10.0 to 2.12.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19138](https://redirect.github.com/goauthentik/authentik/pull/19138)
- website/integrations: vaultwarden: add custom email scope by
[@&#8203;williamkray](https://redirect.github.com/williamkray) in
[goauthentik/authentik#19160](https://redirect.github.com/goauthentik/authentik/pull/19160)
- stages/authenticator\_webauthn: Update FIDO MDS3 & Passkey aaguid
blobs by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19137](https://redirect.github.com/goauthentik/authentik/pull/19137)
- core: handle deserialization errors from FileField migration by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19067](https://redirect.github.com/goauthentik/authentik/pull/19067)
- web: fix slug auto-updating when editing existing applications by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19169](https://redirect.github.com/goauthentik/authentik/pull/19169)
- web: Fix user library colors, modal z-indexes, table progress bars by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19152](https://redirect.github.com/goauthentik/authentik/pull/19152)
- web/admin: adjust sync threshold, add tooltip by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19131](https://redirect.github.com/goauthentik/authentik/pull/19131)
- website/docs: remove duplicates in slo docs by
[@&#8203;nmasnadithya](https://redirect.github.com/nmasnadithya) in
[goauthentik/authentik#19170](https://redirect.github.com/goauthentik/authentik/pull/19170)
- lifecycle: fix migration conn\_options for psycopg connection by
[@&#8203;D-Tasker207](https://redirect.github.com/D-Tasker207) in
[goauthentik/authentik#19134](https://redirect.github.com/goauthentik/authentik/pull/19134)
- web: bump knip from 5.78.0 to 5.79.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19181](https://redirect.github.com/goauthentik/authentik/pull/19181)
- ci: bump getsentry/action-release from 3.4.0 to 3.5.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19183](https://redirect.github.com/goauthentik/authentik/pull/19183)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 8.1.0 to 8.1.1 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19182](https://redirect.github.com/goauthentik/authentik/pull/19182)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19179](https://redirect.github.com/goauthentik/authentik/pull/19179)
- lib: update error logging by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#18628](https://redirect.github.com/goauthentik/authentik/pull/18628)
- website/integrations: make grafana terraform section expand by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19192](https://redirect.github.com/goauthentik/authentik/pull/19192)
- core: add prettier failure on duplicate group names by
[@&#8203;gergosimonyi](https://redirect.github.com/gergosimonyi) in
[goauthentik/authentik#18941](https://redirect.github.com/goauthentik/authentik/pull/18941)
- web/maintenance: no missing element type definitions by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18950](https://redirect.github.com/goauthentik/authentik/pull/18950)
- root: codespell: ignore Python virtual env, group patterns. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19180](https://redirect.github.com/goauthentik/authentik/pull/19180)
- web: Merge branch -- Stale notifications, synchronized context
objects, rendering fixes by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19141](https://redirect.github.com/goauthentik/authentik/pull/19141)
- website/docs: Add docs for roles by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#19196](https://redirect.github.com/goauthentik/authentik/pull/19196)
- web: Defer table refresh, visibility checks. by
[@&#8203;GirlBossRush](https://redirect.github.com/GirlBossRush) in
[goauthentik/authentik#19194](https://redirect.github.com/goauthentik/authentik/pull/19194)
- rbac: Add show all to roles tab, add role tab to groups by
[@&#8203;PeshekDotDev](https://redirect.github.com/PeshekDotDev) in
[goauthentik/authentik#19097](https://redirect.github.com/goauthentik/authentik/pull/19097)
- website/docs: rewrite section about users and perms by
[@&#8203;tanberry](https://redirect.github.com/tanberry) in
[goauthentik/authentik#19195](https://redirect.github.com/goauthentik/authentik/pull/19195)
- core: add last\_login filter to users API by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#18993](https://redirect.github.com/goauthentik/authentik/pull/18993)
- web: bump the eslint group across 1 directory with 3 updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19205](https://redirect.github.com/goauthentik/authentik/pull/19205)
- admin/files: support %(theme)s variable in media file paths by
[@&#8203;dominic-r](https://redirect.github.com/dominic-r) in
[goauthentik/authentik#19108](https://redirect.github.com/goauthentik/authentik/pull/19108)
- website/integrations: glpi: add step by
[@&#8203;dewi-tik](https://redirect.github.com/dewi-tik) in
[goauthentik/authentik#19208](https://redirect.github.com/goauthentik/authentik/pull/19208)
- web: bump knip from 5.79.0 to 5.80.0 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19207](https://redirect.github.com/goauthentik/authentik/pull/19207)
- core: bump axllent/mailpit from v1.28.0 to v1.28.1 in /tests/e2e by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19204](https://redirect.github.com/goauthentik/authentik/pull/19204)
- lifecycle/aws: bump aws-cdk from 2.1100.1 to 2.1100.2 in
/lifecycle/aws by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19203](https://redirect.github.com/goauthentik/authentik/pull/19203)
- core: bump goauthentik.io/api/v3 from 3.2026020.6 to 3.2026020.7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19202](https://redirect.github.com/goauthentik/authentik/pull/19202)
- translate: Updates for project authentik and language pt\_BR by
[@&#8203;transifex-integration](https://redirect.github.com/transifex-integration)\[bot]
in
[goauthentik/authentik#19082](https://redirect.github.com/goauthentik/authentik/pull/19082)
- web/maintenance: lint pass to add missing HTMLElementEventMap entries
by
[@&#8203;kensternberg-authentik](https://redirect.github.com/kensternberg-authentik)
in
[goauthentik/authentik#18953](https://redirect.github.com/goauthentik/authentik/pull/18953)
- outpost/proxyv2: reduce max number of postgres connections by
[@&#8203;rissson](https://redirect.github.com/rissson) in
[goauthentik/authentik#19211](https://redirect.github.com/goauthentik/authentik/pull/19211)
- core, web: update translations by
[@&#8203;authentik-automation](https://redirect.github.com/authentik-automation)\[bot]
in
[goauthentik/authentik#19200](https://redirect.github.com/goauthentik/authentik/pull/19200)
- website/docs: revisit endpoint docs the nth by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19116](https://redirect.github.com/goauthentik/authentik/pull/19116)
- ci: bump astral-sh/setup-uv from 7.1.6 to 7.2.0 in
/.github/actions/setup by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19221](https://redirect.github.com/goauthentik/authentik/pull/19221)
- endpoints: include license status in agent config by
[@&#8203;BeryJu](https://redirect.github.com/BeryJu) in
[goauthentik/authentik#19227](https://redirect.github.com/goauthentik/authentik/pull/19227)
- web: bump
[@&#8203;formatjs/intl-listformat](https://redirect.github.com/formatjs/intl-listformat)
from 8.1.1 to 8.1.2 in /web by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[goauthentik/authentik#19220](https://redirect.github.com/goauthentik/authentik/pull/19220)
- lifecycle/aws: bump aws-cdk from 2.1100.2 to 2.1100.3 in
/lifecycle/aws by [@

</details>

---

### Configuration

📅 **Schedule**: Branch creation - Only on Sunday and Saturday ( * * * *
0,6 ) (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/fgeck/homelab-k3s).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My40My4yIiwidXBkYXRlZEluVmVyIjoiNDMuNDguMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUvaGVsbSIsInR5cGUvbWFqb3IiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant