VYPR
Moderate severityNVD Advisory· Published Nov 19, 2025· Updated Nov 20, 2025

authentik invitation expiry is delayed by at least 5 minutes

CVE-2025-64708

Description

authentik is an open-source Identity Provider. Prior to versions 2025.8.5 and 2025.10.2, in previous authentik versions, invitations were considered valid regardless if they are expired or not, thus relying on background tasks to clean up expired ones. In a normal scenario this can take up to 5 minutes because the cleanup of expired objects is scheduled to run every 5 minutes. However, with a large amount of tasks in the backlog, this might take longer. authentik versions 2025.8.5 and 2025.10.2 fix this issue. A workaround involves creating a policy that explicitly checks whether the invitation is still valid, and then bind it to the invitation stage on the invitation flow, and denying access if the invitation is not valid.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

In authentik, expired invitations remain valid until a background cleanup runs, creating a window where attackers can use stale invitations.

Vulnerability

In authentik, an open-source Identity Provider, invitations are considered valid regardless of their expiration status. The system relies on a background task that runs every 5 minutes to clean up expired invitations, meaning that an expired invitation remains usable until the next cleanup cycle. In a normal scenario this can take up to 5 minutes, but with a large backlog of tasks the delay may be longer [1][4].

Exploitation

An attacker who possesses an expired invitation token can use it to authenticate or gain access during the window between expiration and cleanup. No additional authentication is required beyond possession of the token. The vulnerability is present in all versions prior to 2025.8.5 and 2025.10.2 [1][2].

Impact

Successful exploitation allows an attacker to bypass the intended expiration policy of invitations, potentially gaining unauthorized access to flows or resources that should have been restricted after the invitation's expiry [1][4].

Mitigation

The issue is fixed in authentik versions 2025.8.5 and 2025.10.2. The fix modifies the invitation lookup to filter out expired invitations at query time rather than relying on a new filter_not_expired method [2]. As a workaround, administrators can create a policy that explicitly checks whether the invitation is still valid and bind it to the invitation stage, denying access if the invitation is expired [1][4].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
goauthentik.ioGo
< 0.0.0-20251119135424-6672e6aaa41e0.0.0-20251119135424-6672e6aaa41e

Affected products

2
  • Range: prior to 2025.8.5 and 2025.10.2
  • goauthentik/authentikv5
    Range: < 2025.10.2

Patches

1
6672e6aaa41e

internal: Automated internal backport: 1487-invitation-expiry.sec.patch to authentik-main (#18264)

https://github.com/goauthentik/authentikauthentik-automation[bot]Nov 19, 2025via ghsa
3 files changed · +55 1
  • authentik/stages/invitation/stage.py+1 1 modified
    @@ -38,7 +38,7 @@ def get_invite(self) -> Invitation | None:
             if not token:
                 return None
             try:
    -            invite: Invitation = Invitation.objects.filter(pk=token).first()
    +            invite: Invitation | None = Invitation.filter_not_expired(pk=token).first()
             except ValidationError:
                 self.logger.debug("invalid invitation", token=token)
                 return None
    
  • authentik/stages/invitation/tests.py+27 0 modified
    @@ -1,9 +1,11 @@
     """invitation tests"""
     
    +from datetime import timedelta
     from unittest.mock import MagicMock, patch
     
     from django.urls import reverse
     from django.utils.http import urlencode
    +from django.utils.timezone import now
     from guardian.shortcuts import get_anonymous_user
     from rest_framework.test import APITestCase
     
    @@ -79,6 +81,31 @@ def test_without_invitation_continue(self):
             self.stage.continue_flow_without_invitation = False
             self.stage.save()
     
    +    def test_with_invitation_expired(self):
    +        """Test with invitation, expired"""
    +        plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
    +        session = self.client.session
    +        session[SESSION_KEY_PLAN] = plan
    +        session.save()
    +
    +        data = {"foo": "bar"}
    +        invite = Invitation.objects.create(
    +            created_by=get_anonymous_user(),
    +            fixed_data=data,
    +            expires=now() - timedelta(hours=1),
    +        )
    +
    +        base_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
    +        args = urlencode({QS_INVITATION_TOKEN_KEY: invite.pk.hex})
    +        response = self.client.get(base_url + f"?query={args}")
    +
    +        self.assertEqual(response.status_code, 200)
    +        self.assertStageResponse(
    +            response,
    +            flow=self.flow,
    +            component="ak-stage-access-denied",
    +        )
    +
         def test_with_invitation_get(self):
             """Test with invitation, check data in session"""
             plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
    
  • website/docs/security/cves/CVE-2025-64708.md+27 0 added
    @@ -0,0 +1,27 @@
    +# CVE-2025-64708
    +
    +_Reported by [@melizeche](https://github.com/melizeche)_
    +
    +## Invitation expiry is delayed by at least 5 minutes
    +
    +### Summary
    +
    +In previous authentik versions, invitations were considered valid regardless if they are expired or not, thus relying on background tasks to clean up expired ones. In a normal scenario this can take up to 5 minutes because the cleanup of expired objects is scheduled to run every 5 minutes. However, with a large amount of tasks in the backlog, this might take longer.
    +
    +### Patches
    +
    +authentik 2025.8.5 and 2025.10.2 fix this issue; for other versions the workaround below can be used.
    +
    +### Workarounds
    +
    +You can create a policy that explicitly checks whether the invitation is still valid, and then bind it to the invitation stage on your invitation flow, and deny access if the invitation is not valid.
    +
    +```python
    +return not context['flow_plan'].context['invitation'].is_expired
    +```
    +
    +### For more information
    +
    +If you have any questions or comments about this advisory:
    +
    +- Email us at [security@goauthentik.io](mailto:security@goauthentik.io).
    

Vulnerability mechanics

Root cause

"The system failed to validate the expiration status of invitation tokens at the time of use, relying instead on delayed background cleanup tasks."

Attack vector

An attacker can use an expired invitation token to access invitation flows that should be restricted. Because the system relied on periodic background tasks to remove expired invitations, tokens remained valid for at least five minutes after their expiration time, or longer if the task queue was congested. This allows unauthorized access to invitation-based workflows [patch_id=30507].

Affected code

The vulnerability exists in `authentik/stages/invitation/stage.py` within the `get_invite` method. The code previously fetched invitations without checking their expiration status, relying solely on background cleanup tasks [patch_id=30507].

What the fix does

The patch updates the `get_invite` method in `authentik/stages/invitation/stage.py` to use `Invitation.filter_not_expired()` instead of a standard query [patch_id=30507]. This ensures that expired invitations are immediately rejected during the flow execution, rather than waiting for a background cleanup task to delete them. This change effectively enforces invitation expiration in real-time [patch_id=30507].

Preconditions

  • inputThe attacker must possess an invitation token that has passed its expiration time.

Generated on May 17, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.