Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions packages/google-auth-oauthlib/google_auth_oauthlib/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
from random import SystemRandom

from string import ascii_letters, digits

import socket
import sys
import webbrowser
import wsgiref.simple_server
import wsgiref.util
Expand Down Expand Up @@ -432,10 +435,15 @@ def run_local_server(
authorization server.
"""
wsgi_app = _RedirectWSGIApp(success_message)
# Fail fast if the address is occupied
# Fail fast if the address/port is occupied
wsgiref.simple_server.WSGIServer.allow_reuse_address = False
# Use _ExclusiveWSGIServer so that other apps cannot bind to the same address/port on Windows.
Comment on lines +438 to +440

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Modifying the global class attribute wsgiref.simple_server.WSGIServer.allow_reuse_address is risky because it mutates the behavior of the standard library's WSGIServer globally for the entire Python process. This can lead to unexpected side effects in other parts of the application or other libraries that rely on the default behavior of WSGIServer.

Since we are now using a custom subclass _ExclusiveWSGIServer, we can define allow_reuse_address = False directly as a class attribute on _ExclusiveWSGIServer. This achieves the same "fail fast" behavior locally without global side effects.

Suggested change
# Fail fast if the address/port is occupied
wsgiref.simple_server.WSGIServer.allow_reuse_address = False
# Use _ExclusiveWSGIServer so that other apps cannot bind to the same address/port on Windows.
# Use _ExclusiveWSGIServer to fail fast if the address/port is occupied, and to prevent other apps from binding to it on Windows.

local_server = wsgiref.simple_server.make_server(
bind_addr or host, port, wsgi_app, handler_class=_WSGIRequestHandler
bind_addr or host,
port,
wsgi_app,
server_class=_ExclusiveWSGIServer,
handler_class=_WSGIRequestHandler,
)

try:
Expand Down Expand Up @@ -478,6 +486,21 @@ def run_local_server(
return self.credentials


class _ExclusiveWSGIServer(wsgiref.simple_server.WSGIServer):
"""Custom WSGIServer.

Enforces exclusive address binding on Windows.
Setting `WSGIServer.allow_reuse_address` is not enough, since it sets `SO_REUSEADDR`
and not `SO_EXCLUSIVEADDRUSE`. `SO_REUSEADDR` alone allows other processes to bind
to the same address and port on Windows.
"""

def server_bind(self):
if sys.platform == "win32" and hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
super().server_bind()
Comment on lines +489 to +501

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To avoid modifying the global WSGIServer.allow_reuse_address attribute and to ensure that SO_EXCLUSIVEADDRUSE works correctly on Windows, we should define allow_reuse_address = False directly on _ExclusiveWSGIServer.

On Windows, if SO_REUSEADDR is set (which happens if allow_reuse_address is True), attempting to set SO_EXCLUSIVEADDRUSE will fail with WSAEINVAL (OSError: [WinError 10022]). Explicitly setting allow_reuse_address = False on this subclass guarantees that SO_REUSEADDR is not set, preventing this error.

Suggested change
class _ExclusiveWSGIServer(wsgiref.simple_server.WSGIServer):
"""Custom WSGIServer.
Enforces exclusive address binding on Windows.
Setting `WSGIServer.allow_reuse_address` is not enough, since it sets `SO_REUSEADDR`
and not `SO_EXCLUSIVEADDRUSE`. `SO_REUSEADDR` alone allows other processes to bind
to the same address and port on Windows.
"""
def server_bind(self):
if sys.platform == "win32" and hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
super().server_bind()
class _ExclusiveWSGIServer(wsgiref.simple_server.WSGIServer):
"""Custom WSGIServer.
Enforces exclusive address binding on Windows.
Setting WSGIServer.allow_reuse_address is not enough, since it sets SO_REUSEADDR
and not SO_EXCLUSIVEADDRUSE. SO_REUSEADDR alone allows other processes to bind
to the same address and port on Windows.
"""
allow_reuse_address = False
def server_bind(self):
if sys.platform == "win32" and hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
super().server_bind()



class _WSGIRequestHandler(wsgiref.simple_server.WSGIRequestHandler):
"""Custom WSGIRequestHandler.

Expand Down
Loading