Skip to content

providers/oauth2: fix kid always required for federation#17914

Merged
BeryJu merged 1 commit intomainfrom
providers/oauth2/fix-cc-fed-kid
Nov 3, 2025
Merged

providers/oauth2: fix kid always required for federation#17914
BeryJu merged 1 commit intomainfrom
providers/oauth2/fix-cc-fed-kid

Conversation

@BeryJu
Copy link
Member

@BeryJu BeryJu commented Nov 3, 2025

currently kid is always checked when using JWT federation even when no sources are configured

when federating with an OAuth2 provider only that has symmetric signing this fails

ref https://authentiksecurityinc.zendesk.com/agent/tickets/411

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
@BeryJu BeryJu requested a review from a team as a code owner November 3, 2025 14:15
@BeryJu BeryJu added area:backend backport/version-2025.10 Add this label to PRs to backport changes to version-2025.10 labels Nov 3, 2025
@netlify
Copy link

netlify bot commented Nov 3, 2025

Deploy Preview for authentik-storybook canceled.

Name Link
🔨 Latest commit d5cfe9a
🔍 Latest deploy log https://app.netlify.com/projects/authentik-storybook/deploys/6908b91a77f00200087b8102

@netlify
Copy link

netlify bot commented Nov 3, 2025

Deploy Preview for authentik-integrations canceled.

Name Link
🔨 Latest commit d5cfe9a
🔍 Latest deploy log https://app.netlify.com/projects/authentik-integrations/deploys/6908b91a0e51690008c4595c

@netlify
Copy link

netlify bot commented Nov 3, 2025

Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit d5cfe9a
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/6908b91a91d2de00084e3680
😎 Deploy Preview https://deploy-preview-17914--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.

@codecov
Copy link

codecov bot commented Nov 3, 2025

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
2195 2 2193 2
View the full list of 2 ❄️ flaky test(s)
tests.e2e.test_provider_radius.TestProviderRadius::test_radius_bind_fail

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

Stack Traces | 187s run time
self = <asgiref.sync.AsyncToSync object at 0x7febb298da70>
call_result = <Future at 0x7febaa2dc550 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7febaa7c1740>)
task_context = None, context = [<_contextvars.Context object at 0x7feba0f7eb80>]
awaitable = <coroutine object _async_proxy at 0x7febb291f010>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <asgiref.sync.AsyncToSync object at 0x7febaa4aaad0>
call_result = <Future at 0x7febab5d8e20 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7feba80e5340>)
task_context = None, context = [<_contextvars.Context object at 0x7febaa3b2bc0>]
awaitable = <coroutine object _async_proxy at 0x7febab57c5e0>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_fail(self):
        """Test simple bind (failed)"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username + "foo")
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:98: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7feba8190ad0>
pkt = AuthPacket({'User-Name': ['SuZYVdDgtjm8VCKybZwu'], 'NAS-Identifier': ['localhost'], 'User-Password': [b"\xb6\x16o\xf6\x02\x83*Q\xd6(e\xf8<4\xf7\xce\x04`n\xc6\xf8\x85'+[\x06\x11\x1f\xba\x05N\xdd"]})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7feba8190ad0>
pkt = AuthPacket({'User-Name': ['SuZYVdDgtjm8VCKybZwu'], 'NAS-Identifier': ['localhost'], 'User-Password': [b"\xb6\x16o\xf6\x02\x83*Q\xd6(e\xf8<4\xf7\xce\x04`n\xc6\xf8\x85'+[\x06\x11\x1f\xba\x05N\xdd"]})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout

During handling of the above exception, another exception occurred:

self = <asgiref.sync.AsyncToSync object at 0x7febab235d90>
call_result = <Future at 0x7feba810a5d0 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7febaaad0c80>)
task_context = None, context = [<_contextvars.Context object at 0x7febab5c7ec0>]
awaitable = <coroutine object _async_proxy at 0x7feba80ea4d0>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <asgiref.sync.AsyncToSync object at 0x7febaaa78c30>
call_result = <Future at 0x7febab3badd0 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7feba0fc7740>)
task_context = None, context = [<_contextvars.Context object at 0x7feba0f40200>]
awaitable = <coroutine object _async_proxy at 0x7feba80e9120>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_fail(self):
        """Test simple bind (failed)"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username + "foo")
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:98: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab8eb250>
pkt = AuthPacket({'User-Name': ['SDt5oF8IS4cydM1BZu46'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x9d\x18C\x88\xda\x06D\xfb.\xd3[\xbf\x9fK\x8a\x88c8\xf0\x83&08\x08\x97Y\xb3\xa6M\xff\x03\x11']})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab8eb250>
pkt = AuthPacket({'User-Name': ['SDt5oF8IS4cydM1BZu46'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x9d\x18C\x88\xda\x06D\xfb.\xd3[\xbf\x9fK\x8a\x88c8\xf0\x83&08\x08\x97Y\xb3\xa6M\xff\x03\x11']})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7febaba14ad0>
test_case = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
subTest = False

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

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

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
result = <TestCaseFunction test_radius_bind_fail>

    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.9........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
method = <bound method TestProviderRadius.test_radius_bind_fail of <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>>

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

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

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:337: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:337: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_fail>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_fail(self):
        """Test simple bind (failed)"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username + "foo")
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:98: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab8fb390>
pkt = AuthPacket({'User-Name': ['rMFCmwSr9SxIqc4AKkoz'], 'NAS-Identifier': ['localhost'], 'User-Password': [b',\x90\x16\xcb\x07\x85\x7f\xd2h:\x00+\xfeD\x96\xf3\x16u\xfc/\xd5\xa5&\x909\xa9\x83\x01\xb7\xaa\x83\x98']})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab8fb390>
pkt = AuthPacket({'User-Name': ['rMFCmwSr9SxIqc4AKkoz'], 'NAS-Identifier': ['localhost'], 'User-Password': [b',\x90\x16\xcb\x07\x85\x7f\xd2h:\x00+\xfeD\x96\xf3\x16u\xfc/\xd5\xa5&\x909\xa9\x83\x01\xb7\xaa\x83\x98']})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout
tests.e2e.test_provider_radius.TestProviderRadius::test_radius_bind_success

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

Stack Traces | 95.5s run time
self = <asgiref.sync.AsyncToSync object at 0x7feba80f9650>
call_result = <Future at 0x7febab82bed0 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7febaaf0b900>)
task_context = None, context = [<_contextvars.Context object at 0x7febaa8ea7c0>]
awaitable = <coroutine object _async_proxy at 0x7febaaf3bf10>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <asgiref.sync.AsyncToSync object at 0x7feba80f9350>
call_result = <Future at 0x7febab398550 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7febaa72b980>)
task_context = None, context = [<_contextvars.Context object at 0x7feba0f4d400>]
awaitable = <coroutine object _async_proxy at 0x7febaad35d50>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_success(self):
        """Test simple bind"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username)
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febaad648a0>
pkt = AuthPacket({'User-Name': ['cqRpKbLAEDepmwVLvIR0'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\xf4\x18\xb5\xa5@c\x9c\xcf\xfe\xe88\xd4\x8e\xcdT\xb3\xad\r]\xd6\xa8\xed_]\x00F\xdd\x88\xb6\xbbL\xdc']})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febaad648a0>
pkt = AuthPacket({'User-Name': ['cqRpKbLAEDepmwVLvIR0'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\xf4\x18\xb5\xa5@c\x9c\xcf\xfe\xe88\xd4\x8e\xcdT\xb3\xad\r]\xd6\xa8\xed_]\x00F\xdd\x88\xb6\xbbL\xdc']})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout

During handling of the above exception, another exception occurred:

self = <asgiref.sync.AsyncToSync object at 0x7febaa1575c0>
call_result = <Future at 0x7febaa941b50 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7febaa80a000>)
task_context = None, context = [<_contextvars.Context object at 0x7feba0f55200>]
awaitable = <coroutine object _async_proxy at 0x7febab849d50>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <asgiref.sync.AsyncToSync object at 0x7febaa1548c0>
call_result = <Future at 0x7feba8173750 state=finished returned NoneType>
exc_info = (<class 'pyrad.client.Timeout'>, Timeout(), <traceback object at 0x7feba0f31280>)
task_context = None, context = [<_contextvars.Context object at 0x7feba0f7e780>]
awaitable = <coroutine object _async_proxy at 0x7febaa156b60>

    async def main_wrap(
        self,
        call_result: "Future[_R]",
        exc_info: "OptExcInfo",
        task_context: "Optional[List[asyncio.Task[Any]]]",
        context: List[contextvars.Context],
        awaitable: Union[Coroutine[Any, Any, _R], Awaitable[_R]],
    ) -> None:
        """
        Wraps the awaitable with something that puts the result into the
        result/exception future.
        """
    
        __traceback_hide__ = True  # noqa: F841
    
        if context is not None:
            _restore_context(context[0])
    
        current_task = asyncio.current_task()
        if current_task is not None and task_context is not None:
            task_context.append(current_task)
    
        try:
            # If we have an exception, run the function inside the except block
            # after raising it so exc_info is correctly populated.
            if exc_info[1]:
                try:
>                   raise exc_info[1]

.venv/lib/python3.13............/site-packages/asgiref/sync.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_success(self):
        """Test simple bind"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username)
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febabc51940>
pkt = AuthPacket({'User-Name': ['KwPD0FpqohRqttX616IF'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x13\xc1t\x9f`)z\x92\x01/\xc2\xf7\x99\xee\x9eV\xcd1\x8a\xc9\xdf*\t({\xb2\xd7\x90MF\x8f\x92']})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febabc51940>
pkt = AuthPacket({'User-Name': ['KwPD0FpqohRqttX616IF'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x13\xc1t\x9f`)z\x92\x01/\xc2\xf7\x99\xee\x9eV\xcd1\x8a\xc9\xdf*\t({\xb2\xd7\x90MF\x8f\x92']})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout

During handling of the above exception, another exception occurred:

self = <unittest.case._Outcome object at 0x7febab8e8910>
test_case = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
subTest = False

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

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

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
result = <TestCaseFunction test_radius_bind_success>

    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.9........./x64/lib/python3.13/unittest/case.py:651: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
method = <bound method TestProviderRadius.test_radius_bind_success of <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>>

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

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

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:337: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:337: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>
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:324: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (<tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>,)
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: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tests.e2e.test_provider_radius.TestProviderRadius testMethod=test_radius_bind_success>

    @retry(exceptions=[Timeout])
    @apply_blueprint(
        "default/flow-default-authentication-flow.yaml",
        "default/flow-default-invalidation-flow.yaml",
    )
    def test_radius_bind_success(self):
        """Test simple bind"""
        self._prepare()
        srv = Client(
            server="localhost",
            secret=self.shared_secret.encode(),
            dict=Dictionary(".../radius/dictionaries/dictionary"),
        )
    
        req = srv.CreateAuthPacket(
            code=AccessRequest, User_Name=self.user.username, NAS_Identifier="localhost"
        )
        req["User-Password"] = req.PwCrypt(self.user.username)
    
>       reply = srv.SendPacket(req)

tests/e2e/test_provider_radius.py:76: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab46f410>
pkt = AuthPacket({'User-Name': ['8cVIfzeU6aKKs3RphSMs'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x90\xdeD\xf1\x0cQ\x9aH\xb0\xd7H\xefcq\xf7\x85\xcc(\x81\xf5\x8f\x94\xe6\x04\xeeB\x1b_\x9e\x1e~\xa7']})

    def SendPacket(self, pkt):
        """Send a packet to a RADIUS server.
    
        :param pkt: the packet to send
        :type pkt:  pyrad.packet.Packet
        :return:    the reply packet received
        :rtype:     pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        if isinstance(pkt, packet.AuthPacket):
            if pkt.auth_type == 'eap-md5':
                # Creating EAP-Identity
                password = pkt[2][0] if 2 in pkt else pkt[1][0]
                pkt[79] = [struct.pack('!BBHB%ds' % len(password),
                                       EAP_CODE_RESPONSE,
                                       packet.CurrentID,
                                       len(password) + 5,
                                       EAP_TYPE_IDENTITY,
                                       password)]
>           reply = self._SendPacket(pkt, self.authport)

.venv/lib/python3.13................../site-packages/pyrad/client.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyrad.client.Client object at 0x7febab46f410>
pkt = AuthPacket({'User-Name': ['8cVIfzeU6aKKs3RphSMs'], 'NAS-Identifier': ['localhost'], 'User-Password': [b'\x90\xdeD\xf1\x0cQ\x9aH\xb0\xd7H\xefcq\xf7\x85\xcc(\x81\xf5\x8f\x94\xe6\x04\xeeB\x1b_\x9e\x1e~\xa7']})
port = 1812

    def _SendPacket(self, pkt, port):
        """Send a packet to a RADIUS server.
    
        :param pkt:  the packet to send
        :type pkt:   pyrad.packet.Packet
        :param port: UDP port to send packet to
        :type port:  integer
        :return:     the reply packet received
        :rtype:      pyrad.packet.Packet
        :raise Timeout: RADIUS server does not reply
        """
        self._SocketOpen()
    
        for attempt in range(self.retries):
            if attempt and pkt.code == packet.AccountingRequest:
                if "Acct-Delay-Time" in pkt:
                    pkt["Acct-Delay-Time"] = \
                            pkt["Acct-Delay-Time"][0] + self.timeout
                else:
                    pkt["Acct-Delay-Time"] = self.timeout
    
            now = time.time()
            waitto = now + self.timeout
    
            self._socket.sendto(pkt.RequestPacket(), (self.server, port))
    
            while now < waitto:
                ready = self._poll.poll((waitto - now) * 1000)
    
                if ready:
                    rawreply = self._socket.recv(4096)
                else:
                    now = time.time()
                    continue
    
                try:
                    reply = pkt.CreateReply(packet=rawreply)
                    if pkt.VerifyReply(reply, rawreply):
                        return reply
                except packet.PacketError:
                    pass
    
                now = time.time()
    
>       raise Timeout
E       pyrad.client.Timeout

.venv/lib/python3.13................../site-packages/pyrad/client.py:173: Timeout

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 ddf463b into main Nov 3, 2025
92 of 96 checks passed
@BeryJu BeryJu deleted the providers/oauth2/fix-cc-fed-kid branch November 3, 2025 15:13
@github-project-automation github-project-automation bot moved this from Todo to Done in authentik Core Nov 3, 2025
authentik-automation bot pushed a commit that referenced this pull request Nov 3, 2025
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
@authentik-automation
Copy link
Contributor

🍒 Cherry-pick to version-2025.10 created: #17917

BeryJu added a commit that referenced this pull request Nov 3, 2025
…17914 to version-2025.10) (#17917)

providers/oauth2: fix kid always required for federation (#17914)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
kensternberg-authentik added a commit that referenced this pull request Nov 10, 2025
* main: (32 commits)
  website/docs: 2025.10.1 release notes (#17918)
  providers/oauth2: fix kid always required for federation (#17914)
  providers/radius: revert fix inverted message authenticator validation (#17855) (#17915)
  website: bump @types/node from 24.9.1 to 24.9.2 in /website (#17786)
  web: bump @rollup/plugin-commonjs from 28.0.8 to 28.0.9 in /web in the rollup group across 1 directory (#17788)
  web: bump validator from 13.15.15 to 13.15.20 in /packages/docusaurus-config (#17866)
  internal: add default go http server timeouts (#17858)
  providers/radius: fix inverted message authenticator validation (#17855)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#17871)
  web: fix package-lock.json (#17809)
  website/integrations: oracle cloud: cleanup (#17808)
  website/integrations: Add Keycloak integration (#17813)
  website: bump the build group across 1 directory with 9 updates (#17849)
  lifecycle/aws: bump aws-cdk from 2.1031.0 to 2.1031.1 in /lifecycle/aws (#17850)
  core: bump astral-sh/uv from 0.9.6 to 0.9.7 (#17851)
  internal: full openssl path (#17856)
  outpost: revert breaking signals change (#17847)
  web/a11y: Isolated Outpost Error Page (#17683)
  provider/saml: make signing kp singleton (#17703)
  tasks: sanitize log attributes (#17833)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:backend backport/version-2025.10 Add this label to PRs to backport changes to version-2025.10

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant