CVE-2025-53942: Ghost Access Vulnerability in Authentik Lets Deactivated Users In

You’ve done everything right. An employee has left the company, and as a diligent admin, you’ve navigated to your authentik dashboard, found their user account, and clicked "Deactivate." The user is disabled, their access revoked. Or so you thought. What if I told you that a ghost of that user account could linger, capable of accessing your secure applications?

This isn't a horror story; it's a real vulnerability in authentik, the popular open-source Identity Provider. Tracked as CVE-2025-53942, this flaw allows deactivated users to maintain partial access, creating a security blind spot that could have serious consequences. Let's exorcise this ghost together.

TL;DR / Executive Summary

  • Vulnerability: CVE-2025-53942 - Insufficient check for account active status in authentik.
  • Affected Systems: authentik versions prior to 2025.4.4 and 2025.6.4.
  • Impact: Deactivated users who authenticate via an external OAuth or SAML provider (like Google, Azure AD, or Okta) can enter a "half-authenticated" state. While they cannot access the authentik API, they can still authorize and access downstream applications if they know the direct URL. This is a significant access control bypass.
  • Severity: High. It undermines the core security promise of user deprovisioning.
  • Mitigation: The primary solution is to upgrade to a patched version (2025.4.4 or 2025.6.4). If you can't upgrade immediately, a workaround using an expression policy can temporarily secure your instance.

Introduction: The Deprovisioning Fallacy

In the world of Identity and Access Management (IAM), the "deactivate user" button is one of the most critical security controls. It's the digital equivalent of changing the locks, revoking a keycard, and escorting someone out of the building. We trust that when we click it, access is severed—completely and irrevocably.

authentik is a powerful tool that centralizes this control for countless organizations. It manages authentication for everything from internal dashboards to critical infrastructure. But CVE-2025-53942 introduces a crack in this foundation. The vulnerability specifically affects users federated through external providers like Google or Microsoft. The system correctly deactivates the local authentik user but fails to enforce this status during a federated login flow.

The result is a "zombie user"—an account that looks dead in the admin UI but can still perform actions. For any organization relying on authentik for security, this is a nightmare scenario that could lead to data breaches and compliance failures.

Technical Deep Dive: Anatomy of a Ghost Session

So, how does a deactivated user come back to haunt the system? The issue lies in a subtle gap in the authentication state machine, specifically within the OAuth/SAML login flow.

Root Cause Analysis

Let's use an analogy. Imagine your company's office building (your applications) uses a central security desk (authentik) to grant access. Most employees have a company ID badge (local password) that the desk checks. Some, however, are contractors who use a badge from their own company (an external IdP like Google).

The standard procedure for a contractor is:

  1. Show up at the security desk (authentik).
  2. The desk tells them to get clearance from their own company's security office (redirect to Google for login).
  3. They return with a signed note from their company saying, "Yep, this person is with us" (a successful OAuth/SAML assertion).
  4. The security desk at your company then checks its own internal "banned list" before letting them in.

The vulnerability in CVE-2025-53942 is that the security desk (authentik) was so impressed with the signed note from Google that it forgot to check its own banned list (the is_active flag) before creating a temporary access pass.

The user ends up in a "half-authenticated" state. They are in the building's lobby but don't have full credentials. They can't access the security office's records (the authentik API), but if they know the direct path to a specific conference room (an application's authorization URL), they can walk right in.

Attack Vector and Business Impact

The most likely attack scenario involves a recently offboarded employee or contractor.

  1. The employee is terminated, and an admin deactivates their authentik account.
  2. The ex-employee, knowing the URL for a sensitive internal application (e.g., grafana.company.com), navigates to it.
  3. The application redirects them to authentik for authentication.
  4. authentik, seeing they are a federated user, redirects them to their Google login page.
  5. They log in with their still-active Google account.
  6. Google sends a successful assertion back to authentik.
  7. The Flaw: The vulnerable authentik version fails to check that user.is_active is False. It establishes a session and redirects the user, now with a valid token, back to the Grafana application.
  8. Impact: The ex-employee now has access to sensitive company metrics, customer data, or whatever the application holds.

This bypasses the entire deprovisioning process, potentially leading to data exfiltration, intellectual property theft, and serious compliance violations under regulations like GDPR, HIPAA, or SOX.

Proof of Concept (Theoretical)

Here’s how you could demonstrate this vulnerability in a lab environment.

Setup:

  • A vulnerable instance of authentik (e.g., 2025.6.3).
  • An application (e.g., a simple web app) configured as a provider in authentik.
  • An OAuth/SAML source configured (e.g., Google Workspace).
  • A test user in authentik, linked to a Google account.

Exploitation Steps:

  1. Deactivate User: As an admin in authentik, find the test user and set their status to "inactive."
  2. Initiate Login: In a clean browser session, navigate directly to the protected application's URL.
  3. Federated Auth: The application will redirect you to authentik, which in turn redirects you to Google for authentication.
  4. Authenticate: Log in using the test user's Google credentials.
  5. Bypass: Upon successful Google login, you are redirected back to authentik. A vulnerable version will create a session and grant you access to the application, completely ignoring the "inactive" status of the user.
  6. Verify Limited Access: If you then try to navigate to the authentik user interface or make an API call, it will likely fail, confirming the "half-authenticated" state.

Mitigation and Remediation: Banishing the Ghost

Fortunately, the fix is straightforward. The authentik team has released patches and provided a solid workaround.

1. Immediate Fix: Upgrade!

The best and most complete solution is to upgrade your authentik instance to a patched version:

  • 2025.4.4 or newer
  • 2025.6.4 or newer

This should be your top priority.

2. Temporary Workaround: The Expression Policy

If you cannot upgrade immediately, you can mitigate the vulnerability by adding an Expression Policy to your authentication flow. This policy manually inserts the is_active check where it was missing.

  1. Navigate to your OAuth/SAML authentication flow in the authentik admin interface.
  2. Select the "User login" stage.
  3. Create a new Expression Policy and add the following code:
    # This policy ensures the login stage only proceeds if the user is active.
    return request.context["pending_user"].is_active
    
  4. Bind this policy to the "User login" stage.

This expression tells authentik: "Before you finalize this login, check the is_active property of the user attempting to log in. If it's False, stop the entire process."

3. Patch Analysis: How the Fix Works

The official patch implements two key changes, demonstrating a great defense-in-depth strategy.

Change 1: The Direct Fix (in authentik/stages/user_login/stage.py)

The code in the user login stage was modified to explicitly stop the flow for inactive users.

--- a/authentik/stages/user_login/stage.py
+++ b/authentik/stages/user_login/stage.py
@@ -91,6 +91,7 @@
         user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
         if not user.is_active:
             self.logger.warning("User is not active, login will not work.")
+            return self.executor.stage_invalid()
         delta = self.set_session_duration(remember)
         self.set_session_ip()
         # the `user_logged_in` signal will update the user to write the `last_login` field

Previously, the code would only log a warning but proceed. The addition of return self.executor.stage_invalid() ensures the login flow is properly terminated.

Change 2: The Safety Net (in authentik/core/middleware.py)

A new check was added to the core middleware, which processes every authenticated request.

--- a/authentik/core/middleware.py
+++ b/authentik/core/middleware.py
@@ -58,6 +59,11 @@
         request.user = SimpleLazyObject(lambda: get_user(request))
         request.auser = partial(aget_user, request)
 
+        user = request.user
+        if user and user.is_authenticated and not user.is_active:
+            logout(request)
+            raise AssertionError()
+

This is a fantastic belt-and-suspenders fix. It ensures that even if some other flow accidentally authenticates an inactive user, the very next request they make will be caught, their session will be destroyed, and the request will be denied.

Timeline

  • Discovery: Reported by GitHub user @pascalwei prior to July 2025.
  • Vendor Notification: Prior to July 2025.
  • Patch Release: July 22, 2025 (Versions 2025.4.4 & 2025.6.4).
  • Public Disclosure: July 22, 2025.

Lessons Learned

This vulnerability serves as a powerful reminder of several key security principles:

  1. Trust, But Verify (Especially in Auth Flows): Never assume a user's state is valid just because one step of a complex authentication process succeeded. Critical attributes like is_active or group memberships should be re-validated at every major security boundary.
  2. Defense-in-Depth is Not a Buzzword: The patch for CVE-2025-53942 is a perfect example of this. It fixed the immediate bug in the login stage and added a global middleware check to catch any similar issues in the future.
  3. Audit Your Deprovisioning Process: Don't just assume your deprovisioning works. Regularly test it. Try to log in as a deactivated user through every method available: password, social login, SAML, etc. Does it fail everywhere, every time?

The one key takeaway? "Deactivated" must mean "Denied." There is no room for nuance. Your security posture depends on the absolute certainty that when you revoke access, it stays revoked.

References and Further Reading

Read more