authentik invitation expiry is delayed by at least 5 minutes
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.
| Package | Affected versions | Patched versions |
|---|---|---|
goauthentik.ioGo | < 0.0.0-20251119135424-6672e6aaa41e | 0.0.0-20251119135424-6672e6aaa41e |
Affected products
2- goauthentik/authentikv5Range: < 2025.10.2
Patches
16672e6aaa41einternal: Automated internal backport: 1487-invitation-expiry.sec.patch to authentik-main (#18264)
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- github.com/advisories/GHSA-ch7q-53v8-73pcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-64708ghsaADVISORY
- github.com/goauthentik/authentik/commit/6672e6aaa41e0f2c9bfb1e4d8b51cf114969e830ghsax_refsource_MISCWEB
- github.com/goauthentik/authentik/security/advisories/GHSA-ch7q-53v8-73pcghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.