Skip to content

website/docs: Add docs for passkey autofill (WebauthN Conditional UI) (cherry-pick #18805 to version-2025.12)#18870

Merged
rissson merged 1 commit intoversion-2025.12from
cherry-pick/18805-to-version-2025.12
Dec 16, 2025
Merged

website/docs: Add docs for passkey autofill (WebauthN Conditional UI) (cherry-pick #18805 to version-2025.12)#18870
rissson merged 1 commit intoversion-2025.12from
cherry-pick/18805-to-version-2025.12

Conversation

@authentik-automation
Copy link
Contributor

Cherry-pick of #18805 to version-2025.12 branch.

Original PR: #18805
Original Author: @melizeche
Cherry-picked commit: 0365147

…#18805)

* Add docs for passkey autofill feature

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* improve configuration section

* remove blank lines

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dominic R <dominic@sdko.org>
@netlify
Copy link

netlify bot commented Dec 16, 2025

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit 21d21a3
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/694177e627b21a0007cb2ba9
😎 Deploy Preview https://deploy-preview-18870--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.

@dewi-tik dewi-tik enabled auto-merge (squash) December 16, 2025 15:18
@netlify
Copy link

netlify bot commented Dec 16, 2025

Deploy Preview for authentik-integrations ready!

Name Link
🔨 Latest commit 21d21a3
🔍 Latest deploy log https://app.netlify.com/projects/authentik-integrations/deploys/694177e671a9ed0008898423
😎 Deploy Preview https://deploy-preview-18870--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 Dec 16, 2025

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
2843 1 2842 2
View the top 1 failed test(s) by shortest run time
tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit::test_authorization_consent_implied
Stack Traces | 18.7s run time
self = <unittest.case._Outcome object at 0x7f3bf446a7a0>
test_case = <tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
result = <TestCaseFunction test_authorization_consent_implied>

    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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
method = <bound method TestProviderOAuth2OIDCImplicit.test_authorization_consent_implied of <tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>>

    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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
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:468: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>,)
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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>,)
kwargs = {}
file = 'default/flow-default-provider-authorization-implicit-consent.yaml'
content = 'version: 1\nmetadata:\n  name: Default - Provider authorization flow (implicit consent)\nentries:\n- attrs:\n    desi...henticated\n  identifiers:\n    slug: default-provider-authorization-implicit-consent\n  model: authentik_flows.flow\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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>,)
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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>,)
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_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>

    @retry()
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    @apply_blueprint("default/flow-default-provider-authorization-implicit-consent.yaml")
    @apply_blueprint("system/providers-oauth2.yaml")
    @reconcile_app("authentik_crypto")
    def test_authorization_consent_implied(self):
        """test OpenID Provider flow (default authorization flow with implied consent)"""
        sleep(1)
        # Bootstrap all needed objects
        authorization_flow = Flow.objects.get(
            slug="default-provider-authorization-implicit-consent"
        )
        provider = OAuth2Provider.objects.create(
            name=self.application_slug,
            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/implicit/")
            ],
            authorization_flow=authorization_flow,
        )
        provider.property_mappings.set(
            ScopeMapping.objects.filter(
                scope_name__in=[
                    SCOPE_OPENID,
                    SCOPE_OPENID_EMAIL,
                    SCOPE_OPENID_PROFILE,
                    SCOPE_OFFLINE_ACCESS,
                ]
            )
        )
        provider.save()
        Application.objects.create(
            name=self.application_slug,
            slug=self.application_slug,
            provider=provider,
        )
        self.setup_client()
    
        self.driver.get("http://localhost:9009/implicit/")
        self.wait.until(ec.title_contains("authentik"))
        self.login()
    
>       body = self.parse_json_content()

tests/e2e/test_provider_oidc_implicit.py:153: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
context = <selenium.webdriver.remote.webelement.WebElement (session="a5f49a7926e7bcc1416bee08ee07bfd0", element="f.C1B9E8162D056D3F82286CBE5304E691.d.374FDEAACC8CBCB27CEEC744F6FD6156.e.29")>
timeout = 10

    def parse_json_content(
        self, context: WebElement | None = None, timeout: float | None = 10
    ) -> JSONType:
        """
        Parse JSON from a Selenium element's text content.
    
        If `context` is not provided, defaults to the <body> element.
        Raises a clear test failure if the element isn't found, the text doesn't appear
        within `timeout` seconds, or the text is not valid JSON.
        """
    
        try:
            if context is None:
                context = self.driver.find_element(By.TAG_NAME, "body")
        except NoSuchElementException:
            self.fail(
                f"No element found (defaulted to <body>). Current URL: {self.driver.current_url}"
            )
    
        wait_timeout = timeout or self.wait_timeout
        wait = WebDriverWait(context, wait_timeout)
    
        try:
            wait.until(lambda d: len(d.text.strip()) != 0)
        except TimeoutException:
            snippet = context.text.strip()[:500].replace("\n", " ")
            self.fail(
                f"Timed out waiting for element text to appear at {self.driver.current_url}. "
                f"Current content: {snippet or '<empty>'}"
            )
    
        body_text = context.text.strip()
        inner_html = context.get_attribute("innerHTML") or ""
    
        if "redirecting" in inner_html.lower():
            try:
                wait.until(lambda d: "redirecting" not in d.get_attribute("innerHTML").lower())
            except TimeoutException:
                snippet = context.text.strip()[:500].replace("\n", " ")
                inner_html = context.get_attribute("innerHTML") or ""
    
                self.fail(
                    f"Timed out waiting for redirect to finish at {self.driver.current_url}. "
                    f"Current content: {snippet or '<empty>'}"
                    f"{inner_html or '<empty>'}"
                )
    
            inner_html = context.get_attribute("innerHTML") or ""
            body_text = context.text.strip()
    
        snippet = body_text[:500].replace("\n", " ")
    
        if not body_text.startswith("{") and not body_text.startswith("["):
>           self.fail(
                f"Expected JSON content but got non-JSON text at {self.driver.current_url}: "
                f"{snippet or '<empty>'}"
                f"{inner_html or '<empty>'}"
            )

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

self = <tests.e2e.test_provider_oidc_implicit.TestProviderOAuth2OIDCImplicit testMethod=test_authorization_consent_implied>
msg = 'Expected JSON content but got non-JSON text at http://10.1.1.111:.../if/flow/default-authentication-flow/?client_id...ntication-flow"></ak-flow-inspector>\n            </div>\n        </div>\n    </div>\n\n\n        \n        \n    \n\n'

    def fail(self, msg=None):
        """Fail immediately, with the given message."""
>       raise self.failureException(msg)
E       AssertionError: Expected JSON content but got non-JSON text at http://10.1.1.111:.../if/flow/default-authentication-flow/?client_id=sgYBa4j2uksEBTpGgSddMqE3pgefXjHFHbXuRr9h&redirect_uri=http%3A%2F%2Flocalhost%3A9009%2Fimplicit%2F&response_type=id_token+token&scope=openid++profile+email&state=25ba3166758744c6af3408bbe5d06c53&nonce=d2f1802441a74ea0a1bb067db40daf77&next=%2Fapplication%2Fo%2Fauthorize%2F%3Fclient_id%3DsgYBa4j2uksEBTpGgSddMqE3pgefXjHFHbXuRr9h%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A9009%252Fimplicit%252F%26response_type%3Did_token%2520token%26scope%3Dopenid%2520%2520profile%2520email%26state%3D25ba3166758744c6af3408bbe5d06c53%26nonce%3Dd2f1802441a74ea0a1bb067db40daf77: Chinese Simplified (简体中文) Chinese Traditional (繁體中文) Czech (Čeština) Dutch (Nederlands) English Finnish (Suomi) French (Français) German (Deutsch) Italian (Italiano) Japanese (日本語) Korean (한국어) Polish (Polski) Portuguese (Português) Russian (Русский) Spanish (Español) Turkish (Türkçe) Welcome to authentik! zzPmWH3XcEQAZ2PFUB1a Not you? Password Form actions Continue Powered by authentik
E               
E       <ak-skip-to-content></ak-skip-to-content>
E       <ak-message-container theme="light"></ak-message-container>
E       
E       <div class="pf-c-page__drawer">
E           <div class="pf-c-drawer pf-m-collapsed" id="flow-drawer">
E               <div class="pf-c-drawer__main">
E                   <div class="pf-c-drawer__content">
E                       <div class="pf-c-drawer__body">
E                               <ak-flow-executor slug="default-authentication-flow" class="pf-c-login" data-layout="stacked" data-test-id="interface-root" theme="light">
E                                   
E       
E       <div class="ak-c-placeholder" id="ak-placeholder" slot="placeholder">
E           <span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
E               <span class="pf-c-spinner__clipper"></span>
E               <span class="pf-c-spinner__lead-ball"></span>
E               <span class="pf-c-spinner__tail-ball"></span>
E           </span>
E       </div>
E       
E       
E                                   <ak-brand-links slot="footer" exportparts="list:brand-links-list, list-item:brand-links-list-item" role="contentinfo" aria-label="Site footer" class="pf-c-login__footer pf-m-dark" theme="light"></ak-brand-links>
E                               </ak-flow-executor>
E                           </div>
E                       </div>
E       
E                       <ak-flow-inspector id="flow-inspector" data-registration="lazy" class="pf-c-drawer__panel pf-m-width-33" slug="default-authentication-flow"></ak-flow-inspector>
E                   </div>
E               </div>
E           </div>

.../hostedtoolcache/Python/3.13.11............/x64/lib/python3.13/unittest/case.py:732: AssertionError
View the full list of 1 ❄️ flaky test(s)
authentik.providers.proxy.tests.ProxyProviderTests::test_sa_perms_cert

Flake rate in main: 100.00% (Passed 0 times, Failed 1 times)

Stack Traces | 2.32s run time
self = <django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>})

    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)
            else:
>               return self.cursor.execute(sql, params)

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

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [INTRANS] (host=localhost user=authentik database=test_authentik) at 0x7fec115e5610>
args = ('SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)', ['public::1:goauthentik.io/enterprise/license'])
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] [INTRANS] (host=localhost user=authentik database=test_authentik) at 0x7fec115e5610>
query = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']

    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.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

self = <unittest.case._Outcome object at 0x7fec14215e10>
test_case = <authentik.providers.proxy.tests.ProxyProviderTests testMethod=test_sa_perms_cert>
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 = <authentik.providers.proxy.tests.ProxyProviderTests testMethod=test_sa_perms_cert>
result = <TestCaseFunction test_sa_perms_cert>

    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 = <authentik.providers.proxy.tests.ProxyProviderTests testMethod=test_sa_perms_cert>
method = <bound method ProxyProviderTests.test_sa_perms_cert of <authentik.providers.proxy.tests.ProxyProviderTests testMethod=test_sa_perms_cert>>

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

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

self = <authentik.providers.proxy.tests.ProxyProviderTests testMethod=test_sa_perms_cert>

    def test_sa_perms_cert(self):
        """Test permissions to access a configured certificate"""
        cert = create_test_cert()
        outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
        provider = ProxyProvider.objects.create(name=generate_id(), certificate=cert)
        Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
        outpost.providers.add(provider)
    
        res = self.client.get(
            reverse("authentik_api:proxyprovideroutpost-list"),
            HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
        )
        body = loads(res.content)
        self.assertEqual(body["pagination"]["count"], 1)
        cert_id = body["results"][0]["certificate"]
        self.assertEqual(cert_id, str(cert.pk))
    
>       res = self.client.get(
            reverse(
                "authentik_api:certificatekeypair-view-certificate",
                kwargs={
                    "pk": cert_id,
                },
            ),
            HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
        )

.../providers/proxy/tests.py:166: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
path = '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'
data = None, follow = False
extra = {'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9awLAW0h7q0OJmOQGjd'}

    def get(self, path, data=None, follow=False, **extra):
>       response = super().get(path, data=data, **extra)

.venv/lib/python3.13.............../site-packages/rest_framework/test.py:292: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
path = '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'
data = None
extra = {'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9awLAW0h7q0OJmOQGjd'}
r = {'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9awLAW0h7q0OJmOQGjd', 'QUERY_STRING': ''}

    def get(self, path, data=None, **extra):
        r = {
            'QUERY_STRING': urlencode(data or {}, doseq=True),
        }
        if not data and '?' in path:
            # Fix to support old behavior where you have the arguments in the
            # url. See #1461.
            query_string = force_bytes(path.split('?')[1])
            query_string = query_string.decode('iso-8859-1')
            r['QUERY_STRING'] = query_string
        r.update(extra)
>       return self.generic('GET', path, **r)

.venv/lib/python3.13.............../site-packages/rest_framework/test.py:209: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>, method = 'GET'
path = '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'
data = '', content_type = 'application/octet-stream', secure = False
extra = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9awLAW0h7q0OJmOQGjd', 'QUERY_STRING': ''}

    def generic(self, method, path, data='',
                content_type='application/octet-stream', secure=False, **extra):
        # Include the CONTENT_TYPE, regardless of whether or not data is empty.
        if content_type is not None:
            extra['CONTENT_TYPE'] = str(content_type)
    
>       return super().generic(
            method, path, data, content_type, secure, **extra)

.venv/lib/python3.13.............../site-packages/rest_framework/test.py:237: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>, method = 'GET'
path = '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'
data = b'', content_type = 'application/octet-stream', secure = False
headers = None, query_params = None
extra = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9awLAW0h7q0OJmOQGjd', 'QUERY_STRING': ''}
parsed = SplitResult(scheme='', netloc='', path='.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', query='', fragment='')
r = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9a...: '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', 'QUERY_STRING': '', ...}

    def generic(
        self,
        method,
        path,
        data="",
        content_type="application/octet-stream",
        secure=False,
        *,
        headers=None,
        query_params=None,
        **extra,
    ):
        """Construct an arbitrary HTTP request."""
        parsed = urlsplit(str(path))  # path can be lazy
        data = force_bytes(data, settings.DEFAULT_CHARSET)
        r = {
            "PATH_INFO": self._get_path(parsed),
            "REQUEST_METHOD": method,
            "SERVER_PORT": "443" if secure else "80",
            "wsgi.url_scheme": "https" if secure else "http",
        }
        if data:
            r.update(
                {
                    "CONTENT_LENGTH": str(len(data)),
                    "CONTENT_TYPE": content_type,
                    "wsgi.input": FakePayload(data),
                }
            )
        if headers:
            extra.update(HttpHeaders.to_wsgi_names(headers))
        if query_params:
            extra["QUERY_STRING"] = urlencode(query_params, doseq=True)
        r.update(extra)
        # If QUERY_STRING is absent or empty, we want to extract it from the URL.
        if not r.get("QUERY_STRING"):
            # WSGI requires latin-1 encoded strings. See get_path_info().
            r["QUERY_STRING"] = parsed.query.encode().decode("iso-8859-1")
>       return self.request(**r)

.venv/lib/python3.13.../django/test/client.py:671: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9a...: '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', 'QUERY_STRING': '', ...}

    def request(self, **kwargs):
        # Ensure that any credentials set get added to every request.
        kwargs.update(self._credentials)
>       return super().request(**kwargs)

.venv/lib/python3.13.............../site-packages/rest_framework/test.py:289: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9a...: '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', 'QUERY_STRING': '', ...}

    def request(self, **kwargs):
>       request = super().request(**kwargs)

.venv/lib/python3.13.............../site-packages/rest_framework/test.py:241: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
request = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9a...: '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', 'QUERY_STRING': '', ...}
environ = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_AUTHORIZATION': 'Bearer 4su6E6KdyNdoBQXKwr3LFhGEJppBXGtmidmCG3K7Y9a...tujjiz', 'PATH_INFO': '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/', ...}
data = {'context': [[{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: <function csrf.<locals>.......">, <Template template_string="{% load static %}{%...">, <Template template_string="{% extends 'login/ba...">, ...]}
on_template_render = functools.partial(<function store_rendered_templates at 0x7fec1e895a80>, {'templates': [<Template template_string="{% ...tik.core.views.error.ServerErrorView object at 0x7fec133b25d0>, 'title': 'Server Error', 'LANGUAGE_CODE': 'en'}, {}]]})
signal_uid = 'template-render-140651861344128'
exception_uid = 'request-exception-140651861344128'
response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8">

    def request(self, **request):
        """
        Make a generic request. Compose the environment dictionary and pass
        to the handler, return the result of the handler. Assume defaults for
        the query environment, which can be overridden using the arguments to
        the request.
        """
        environ = self._base_environ(**request)
    
        # Curry a data dictionary into an instance of the template renderer
        # callback function.
        data = {}
        on_template_render = partial(store_rendered_templates, data)
        signal_uid = "template-render-%s" % id(request)
        signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid)
        # Capture exceptions created by the handler.
        exception_uid = "request-exception-%s" % id(request)
        got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid)
        try:
            response = self.handler(environ)
        finally:
            signals.template_rendered.disconnect(dispatch_uid=signal_uid)
            got_request_exception.disconnect(dispatch_uid=exception_uid)
        # Check for signaled exceptions.
>       self.check_exception(response)

.venv/lib/python3.13.../django/test/client.py:1087: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <rest_framework.test.APIClient object at 0x7fec1162d860>
response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8">

    def check_exception(self, response):
        """
        Look for a signaled exception, clear the current context exception
        data, re-raise the signaled exception, and clear the signaled exception
        from the local cache.
        """
        response.exc_info = self.exc_info
        if self.exc_info:
            _, exc_value, _ = self.exc_info
            self.exc_info = None
            if self.raise_request_exception:
>               raise exc_value

.venv/lib/python3.13.../django/test/client.py:802: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <WSGIRequest: GET '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'>

    @wraps(get_response)
    def inner(request):
        try:
>           response = get_response(request)

.venv/lib/python3.13.../core/handlers/exception.py:55: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware object at 0x7fec11a65550>
args = (<WSGIRequest: GET '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'>,)
kwargs = {}
f = <bound method AuditMiddleware.__call__ of <authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware object at 0x7fec115fa6f0>>
middleware_span = <Span(op='middleware.django', description:'authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware.__call__', ...fad6db76ab68', span_id='9c3365d343740ad0', parent_span_id='8c08f3f28733ac46', sampled=None, origin='auto.http.django')>

    def __call__(self, *args, **kwargs):
        # type: (*Any, **Any) -> Any
        if hasattr(self, "async_route_check") and self.async_route_check():
            return self.__acall__(*args, **kwargs)
    
        f = self._call_method
        if f is None:
            self._call_method = f = self._inner.__call__
    
        middleware_span = _check_middleware_span(old_method=f)
    
        if middleware_span is None:
            return f(*args, **kwargs)
    
        with middleware_span:
>           return f(*args, **kwargs)

.venv/lib/python3.13.../integrations/django/middleware.py:177: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware object at 0x7fec115fa6f0>
request = <WSGIRequest: GET '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'>

    def __call__(self, request: HttpRequest) -> HttpResponse:
        _CTX_REQUEST.set(request)
        self.connect(request)
    
        response = self.get_response(request)
    
>       self.disconnect(request)

authentik/events/middleware.py:158: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware object at 0x7fec115fa6f0>
request = <WSGIRequest: GET '.../certificatekeypairs/32b353ad-8c54-4da2-bad1-97b74c2dba7f/view_certificate/'>

    def disconnect(self, request: HttpRequest):
        super().disconnect(request)
>       if not self.enabled:

.../enterprise/audit/middleware.py:41: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <authentik.enterprise.audit.middleware.EnterpriseAuditMiddleware object at 0x7fec115fa6f0>

    @property
    def enabled(self):
        """Check if audit logging is enabled"""
>       return apps.get_app_config("authentik_enterprise").enabled()

.../enterprise/audit/middleware.py:25: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AuthentikEnterpriseConfig: authentik_enterprise>

    def enabled(self):
        """Return true if enterprise is enabled and valid"""
>       return self.check_enabled() or settings.TEST

authentik/enterprise/apps.py:34: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AuthentikEnterpriseConfig: authentik_enterprise>

    def check_enabled(self):
        """Actual enterprise check, cached"""
        from authentik.enterprise.license import LicenseKey
    
>       return LicenseKey.cached_summary().status.is_valid

authentik/enterprise/apps.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @staticmethod
    def cached_summary() -> LicenseSummary:
        """Helper method which looks up the last summary"""
>       summary = cache.get(CACHE_KEY_ENTERPRISE_LICENSE)

authentik/enterprise/license.py:232: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = ('goauthentik.io/enterprise/license',), kwargs = {}

    @functools.wraps(original_method)
    def sentry_method(*args, **kwargs):
        # type: (*Any, **Any) -> Any
>       return _instrument_call(
            cache, method_name, original_method, args, kwargs, address, port
        )

.venv/lib/python3.13.../integrations/django/caching.py:95: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>, 'get', <bound method DatabaseCache.get of <dj...postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>>, ('goauthentik.io/enterprise/license',), {}, None, ...)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cache = <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>
method_name = 'get'
original_method = <bound method DatabaseCache.get of <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>>
args = ('goauthentik.io/enterprise/license',), kwargs = {}, address = None
port = None

    @ensure_integration_enabled(DjangoIntegration, original_method)
    def _instrument_call(
        cache, method_name, original_method, args, kwargs, address, port
    ):
        # type: (CacheHandler, str, Callable[..., Any], tuple[Any, ...], dict[str, Any], Optional[str], Optional[int]) -> Any
        is_set_operation = method_name.startswith("set")
        is_get_operation = not is_set_operation
    
        op = OP.CACHE_PUT if is_set_operation else OP.CACHE_GET
        description = _get_span_description(method_name, args, kwargs)
    
        with sentry_sdk.start_span(
            op=op,
            name=description,
            origin=DjangoIntegration.origin,
        ) as span:
>           value = original_method(*args, **kwargs)

.venv/lib/python3.13.../integrations/django/caching.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>
key = 'goauthentik.io/enterprise/license', default = None, version = None

    def get(self, key: str, default: Any | None = None, version: int | None = None) -> Any:
        try:
>           return super().get(key, default=default, version=version)

.../django-postgres-cache/django_postgres_cache/backend.py:31: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>
key = 'goauthentik.io/enterprise/license', default = None, version = None

    def get(self, key, default=None, version=None):
>       return self.get_many([key], version).get(key, default)

.venv/lib/python3.13.../cache/backends/db.py:53: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (['goauthentik.io/enterprise/license'], None), kwargs = {}

    @functools.wraps(original_method)
    def sentry_method(*args, **kwargs):
        # type: (*Any, **Any) -> Any
>       return _instrument_call(
            cache, method_name, original_method, args, kwargs, address, port
        )

.venv/lib/python3.13.../integrations/django/caching.py:95: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>, 'get_many', <bound method DatabaseCache.get_m...s_cache.backend.DatabaseCache object at 0x7fec1adc17f0>>, (['goauthentik.io/enterprise/license'], None), {}, None, ...)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cache = <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>
method_name = 'get_many'
original_method = <bound method DatabaseCache.get_many of <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>>
args = (['goauthentik.io/enterprise/license'], None), kwargs = {}
address = None, port = None

    @ensure_integration_enabled(DjangoIntegration, original_method)
    def _instrument_call(
        cache, method_name, original_method, args, kwargs, address, port
    ):
        # type: (CacheHandler, str, Callable[..., Any], tuple[Any, ...], dict[str, Any], Optional[str], Optional[int]) -> Any
        is_set_operation = method_name.startswith("set")
        is_get_operation = not is_set_operation
    
        op = OP.CACHE_PUT if is_set_operation else OP.CACHE_GET
        description = _get_span_description(method_name, args, kwargs)
    
        with sentry_sdk.start_span(
            op=op,
            name=description,
            origin=DjangoIntegration.origin,
        ) as span:
>           value = original_method(*args, **kwargs)

.venv/lib/python3.13.../integrations/django/caching.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django_postgres_cache.backend.DatabaseCache object at 0x7fec1adc17f0>
keys = ['goauthentik.io/enterprise/license'], version = None

    def get_many(self, keys, version=None):
        if not keys:
            return {}
    
        key_map = {
            self.make_and_validate_key(key, version=version): key for key in keys
        }
    
        db = router.db_for_read(self.cache_model_class)
        connection = connections[db]
        quote_name = connection.ops.quote_name
        table = quote_name(self._table)
    
        with connection.cursor() as cursor:
>           cursor.execute(
                "SELECT %s, %s, %s FROM %s WHERE %s IN (%s)"
                % (
                    quote_name("cache_key"),
                    quote_name("value"),
                    quote_name("expires"),
                    table,
                    quote_name("cache_key"),
                    ", ".join(["%s"] * len(key_map)),
                ),
                list(key_map),
            )

.venv/lib/python3.13.../cache/backends/db.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>, 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)', ['public::1:goauthentik.io/enterprise/license'])
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 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']

    @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 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']

    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 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license'], many = False
executor = <bound method CursorWrapper._execute of <django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>>

    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 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>})

    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 0x7fec1adc1160>
exc_type = <class 'psycopg.errors.InFailedSqlTransaction'>
exc_value = InFailedSqlTransaction('current transaction is aborted, commands ignored until end of transaction block')
traceback = <traceback object at 0x7fec1180af40>

    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 0x7fec116473b0>
sql = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']
ignored_wrapper_args = (False, {'connection': <DatabaseWrapper vendor='postgresql' alias='default'>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7fec116473b0>})

    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)
            else:
>               return self.cursor.execute(sql, params)

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

self = <django_prometheus.db.common.ExportingCursorWrapper.<locals>.CursorWrapper [closed] [INTRANS] (host=localhost user=authentik database=test_authentik) at 0x7fec115e5610>
args = ('SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)', ['public::1:goauthentik.io/enterprise/license'])
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] [INTRANS] (host=localhost user=authentik database=test_authentik) at 0x7fec115e5610>
query = 'SELECT "cache_key", "value", "expires" FROM "django_postgres_cache_cacheentry" WHERE "cache_key" IN (%s)'
params = ['public::1:goauthentik.io/enterprise/license']

    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.InternalError: current transaction is aborted, commands ignored until end of transaction block

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

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

@rissson rissson disabled auto-merge December 16, 2025 17:10
@rissson rissson merged commit 49b1952 into version-2025.12 Dec 16, 2025
80 of 86 checks passed
@rissson rissson deleted the cherry-pick/18805-to-version-2025.12 branch December 16, 2025 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants