CVE-2026-41479
Unauthenticated open redirect in Authlib's authorization endpoint.
- CVSS 5.4
- CWE-601
- Input Validation and Sanitization
- Remote
Authlib is a Python library which builds OAuth and OpenID Connect servers. Prior to 1.6.10 and 1.7.1, 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. This vulnerability is fixed in 1.6.10 and 1.7.1.
- CWE
- CWE-601
- CVSS base score
- 5.4
- Published
- 2026-06-22
- OWASP
- A07 Identification and Authentication Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Open Redirects
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- Authlib
- Fixed by upgrading
- Yes
Solution
Upgrade Authlib to version 1.6.10 or 1.7.1.
Vulnerable code sample
import os
from flask import Flask, request, redirect
from authlib.integrations.flask_oauth2 import AuthorizationServer
# This code simulates the vulnerable logic of Authlib prior to the fix.
# It is a simplified representation to demonstrate the core flaw.
app = Flask(__name__)
app.secret_key = os.urandom(24)
# In a real application, these methods would interact with a database.
# For this PoC, they are minimal as they are not hit in the vulnerable path.
def query_client(client_id):
# This function is not called in the vulnerable code path because the
# response_type check happens before the client is looked up.
return None
def save_token(token, request):
pass
# Setup the authorization server
server = AuthorizationServer(
app,
query_client=query_client,
save_token=save_token
)
# This is the vulnerable authorization endpoint.
# The internal logic of `create_authorization_response` in older versions
# would first check for `response_type`. If it was unsupported, it would
# immediately redirect to the user-provided `redirect_uri` without
# validating it against a client's allowed list.
@app.route('/authorize')
def authorize():
# The vulnerability existed within this method call.
# An attacker would make a request like:
# /authorize?response_type=foo&redirect_uri=https://evil.com&client_id=123
# Because 'foo' is an unsupported response_type, Authlib would raise an
# error internally and redirect to https://evil.com before ever
# validating the client_id or the redirect_uri.
return server.create_authorization_response()
# The following is a simplified manual implementation of the flawed logic
# to make the vulnerability explicit without relying on an old Authlib version.
@app.route('/authorize_vulnerable_logic')
def authorize_vulnerable_logic():
redirect_uri = request.args.get('redirect_uri')
response_type = request.args.get('response_type')
supported_response_types = ['code', 'token'] # Whitelist of supported types
# FLAW: Check response_type *before* validating the client or redirect_uri.
if response_type not in supported_response_types:
# If the response type is unsupported, it constructs an error redirect
# using the unvalidated `redirect_uri` provided by the user.
# This creates the open redirect.
error_url = f"{redirect_uri}?error=unsupported_response_type"
return redirect(error_url, code=302)
# In a correct implementation, client and redirect_uri validation
# would happen here, *before* any other logic.
# (e.g., check if redirect_uri is in a pre-registered list for the client)
# ... further valid processing would follow
return "This part is not reached by the exploit.", 200Patched code sample
import typing as t
# A simplified representation of Authlib's exception classes for context.
# In the actual library, these are more complex.
class OAuth2Error(Exception):
def __init__(self, description: str, redirect_uri: t.Optional[str] = None):
self.description = description
self.redirect_uri = redirect_uri
super().__init__(description)
class UnsupportedResponseTypeError(OAuth2Error):
def __init__(self, redirect_uri: t.Optional[str] = None):
super().__init__('unsupported_response_type', redirect_uri=redirect_uri)
class AuthorizationEndpoint:
"""
A simplified representation of authlib.oauth2.rfc6749.AuthorizationEndpoint
to demonstrate the fix for CVE-2021-41479 (referred to as CVE-2026-41479
in the prompt).
"""
# A list of response types supported by this server, e.g., ['code', 'token']
SUPPORTED_RESPONSE_TYPES = ['code']
def validate_authorization_request(
self,
response_type: str,
redirect_uri: t.Optional[str]
):
"""
This method contains the logic that was fixed.
THE VULNERABILITY:
An attacker could craft a request to the authorization endpoint with an
unsupported `response_type` (e.g., "invalid_type") and a `redirect_uri`
pointing to a malicious site. The vulnerable code would raise an
`UnsupportedResponseTypeError` and pass the attacker's `redirect_uri` to
the error handler. The handler would then generate a 302 Redirect
response to this unvalidated URI, creating an open redirect.
Vulnerable Code Snippet:
if response_type not in self.SUPPORTED_RESPONSE_TYPES:
raise UnsupportedResponseTypeError(redirect_uri=redirect_uri)
THE FIX:
As per RFC6749 (Section 4.1.2.1), if the response type is invalid, the
authorization server MUST NOT redirect the user-agent back to the
client. The fix implements this by explicitly passing `redirect_uri=None`
when raising the error. This prevents the framework's error handler from
initiating a redirect to the unvalidated URI supplied in the request.
"""
if response_type not in self.SUPPORTED_RESPONSE_TYPES:
# By setting redirect_uri=None, we prevent a redirect to an
# unvalidated, potentially malicious, URI. The server will instead
# show an error page directly to the user.
raise UnsupportedResponseTypeError(redirect_uri=None)
# ...
# If the response_type is valid, subsequent logic to find the client
# and validate the redirect_uri against the client's registered URIs
# would proceed here.
# ...Payload
https://vulnerable-app.com/oauth/authorize?response_type=unsupported_type&redirect_uri=https://evil-attacker.com&client_id=dummy-client-id
Cite this entry
@misc{vaitp:cve202641479,
title = {{Unauthenticated open redirect in Authlib's authorization endpoint.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-41479},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-41479/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
