Skip to content

[🐛 Bug]: Selenium Stucks / Hangs at Login #15449

@Oefuli

Description

@Oefuli

What happened?

I am trying to perform an automatic login with Selenium. My code keeps getting stuck at password_field.send_keys(Keys.RETURN): The browser window accepts that the data has been entered and return has been pressed, but then the page loads and stays in this mode forever without going any further. The lines after this are therefore not executed. I have already written the code so that password_field.send_keys(Keys.RETURN) and the remaining part are separated and it runs asynchronously, but the same problem occurs.

import requests
import base64
import secrets
import hashlib
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# Generate PKCE code verifier and challenge (recommended for public clients)
def generate_pkce_pair():
    code_verifier = secrets.token_urlsafe(64)
    code_challenge = (
        base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest())
        .decode()
        .rstrip("=")
    )
    return code_verifier, code_challenge


# Callback handler to receive the authorization code
class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.end_headers()

        # Extract authorization code from URL query parameters
        query_components = parse_qs(urlparse(self.path).query)

        if "code" in query_components:
            # Store the auth code in the server instance
            self.server.auth_code = query_components["code"][0]

            # Display success message to the user
            self.wfile.write(
                b"<html><body><h1>Authentication successful!</h1><p>You can close this window now.</p></body></html>"
            )
        else:
            # Handle error or cancel
            self.wfile.write(
                b"<html><body><h1>Authentication failed!</h1><p>No authorization code received.</p></body></html>"
            )
            self.server.auth_code = None

        # Signal that we have processed the callback
        self.server.handled = True


def get_webpage_resp():

# Generate PKCE code pair
    code_verifier, code_challenge = generate_pkce_pair()

    # Create the authorization URL
    auth_url = (
        f"{auth_endpoint}"
        f"?client_id={CLIENT_ID}"
        f"&response_type=code"
        f"&redirect_uri={REDIRECT_URI}"
        f"&scope={SCOPE}"
        f"&code_challenge={code_challenge}"
        f"&code_challenge_method=S256"
        f"&state={secrets.token_urlsafe(16)}"  # State parameter to prevent CSRF
    )

    # Replace 'your_path_to_webdriver' with the path to your WebDriver executable
    driver = webdriver.Chrome()

    # Open the webpage
    driver.get(auth_url)

    # Fill out form fields by finding them by name, id, or other attributes
    username_field = driver.find_element(By.ID, 'username')
    password_field = driver.find_element(By.ID, 'password')

    username_field.send_keys('blabla_user')
    password_field.send_keys('blabla_pwd') 

    # Start local server to receive the callback
    server = HTTPServer(("localhost", 8000), CallbackHandler)
    server.auth_code = None
    server.handled = False        

    # Send the Enter/Return key to any of the form fields
    password_field.send_keys(Keys.RETURN)   

    # Wait for the callback to be processed
    print("Waiting for authentication callback...")
    while not server.handled:
        server.handle_request()

    # Close the browser
    driver.quit()

    auth_code = server.auth_code
    if not auth_code:
        print("Authentication failed: No authorization code received")
        return

    print(f"Authorization code received: {auth_code}")

    # Exchange authorization code for tokens
    token_data = {
        "grant_type": "authorization_code",
        "code": auth_code,
        "redirect_uri": REDIRECT_URI,
        "client_id": CLIENT_ID,
        "code_verifier": code_verifier,
    }

    # Add client secret for confidential clients
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    if CLIENT_SECRET:
        token_data["client_secret"] = CLIENT_SECRET

    # Request tokens
    response = requests.post(token_endpoint, data=token_data, headers=headers)

    if response.status_code == 200:
        tokens = response.json()
        print("Authentication successful!")
        print(f"Access Token: {tokens['access_token'][:20]}...")

        if "refresh_token" in tokens:
            print(f"Refresh Token: {tokens['refresh_token'][:20]}...")

        # Now you can use the access token to call your API
        # Example API call:
        api_response = requests.get(
            "https://some-adress",
            headers={"Authorization": f"Bearer {tokens['access_token']}"},
        )
        print(api_response.text)

    else:
        print(f"Token request failed: {response.status_code}")
        print(response.text)

    return response

If I wait, I get the following error message:

ReadTimeoutError                          Traceback (most recent call last)
Cell In[2], line 1
----> 1 get_webpage_resp()

File /media/tw/Daten/Peak/locust/oauth_webpage_click_simulator.py:110, in get_webpage_resp()
    103     server.handled = False
    105     # muss gemacht werden, sonst hängt die Seite
    106     #driver.set_page_load_timeout(10)
    107 #     driver.refresh()
    108 
    109     # Send the Enter/Return key to any of the form fields
--> 110     password_field.send_keys(Keys.RETURN)
    112     driver.get(REDIRECT_URI)    
    114     # Wait for the callback to be processed

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/selenium/webdriver/remote/webelement.py:303, in WebElement.send_keys(self, *value)
    300             remote_files.append(self._upload(file))
    301         value = tuple("\n".join(remote_files))
--> 303 self._execute(
    304     Command.SEND_KEYS_TO_ELEMENT, {"text": "".join(keys_to_typing(value)), "value": keys_to_typing(value)}
    305 )

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/selenium/webdriver/remote/webelement.py:572, in WebElement._execute(self, command, params)
    570     params = {}
    571 params["id"] = self._id
--> 572 return self._parent.execute(command, params)

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py:427, in WebDriver.execute(self, driver_command, params)
    424     elif "sessionId" not in params:
    425         params["sessionId"] = self.session_id
--> 427 response = self.command_executor.execute(driver_command, params)
    428 if response:
    429     self.error_handler.check_response(response)

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/selenium/webdriver/remote/remote_connection.py:404, in RemoteConnection.execute(self, command, params)
    402 trimmed = self._trim_large_entries(params)
    403 LOGGER.debug("%s %s %s", command_info[0], url, str(trimmed))
--> 404 return self._request(command_info[0], url, body=data)

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/selenium/webdriver/remote/remote_connection.py:428, in RemoteConnection._request(self, method, url, body)
    425     body = None
    427 if self._client_config.keep_alive:
--> 428     response = self._conn.request(method, url, body=body, headers=headers, timeout=self._client_config.timeout)
    429     statuscode = response.status
    430 else:

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/_request_methods.py:143, in RequestMethods.request(self, method, url, body, fields, headers, json, **urlopen_kw)
    135     return self.request_encode_url(
    136         method,
    137         url,
   (...)
    140         **urlopen_kw,
    141     )
    142 else:
--> 143     return self.request_encode_body(
    144         method, url, fields=fields, headers=headers, **urlopen_kw
    145     )

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/_request_methods.py:278, in RequestMethods.request_encode_body(self, method, url, fields, headers, encode_multipart, multipart_boundary, **urlopen_kw)
    274     extra_kw["headers"].setdefault("Content-Type", content_type)
    276 extra_kw.update(urlopen_kw)
--> 278 return self.urlopen(method, url, **extra_kw)

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/poolmanager.py:443, in PoolManager.urlopen(self, method, url, redirect, **kw)
    441     response = conn.urlopen(method, url, **kw)
    442 else:
--> 443     response = conn.urlopen(method, u.request_uri, **kw)
    445 redirect_location = redirect and response.get_redirect_location()
    446 if not redirect_location:

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/connectionpool.py:841, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    838 elif isinstance(new_e, (OSError, HTTPException)):
    839     new_e = ProtocolError("Connection aborted.", new_e)
--> 841 retries = retries.increment(
    842     method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2]
    843 )
    844 retries.sleep()
    846 # Keep track of the error for the retry warning.

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/util/retry.py:474, in Retry.increment(self, method, url, response, error, _pool, _stacktrace)
    471 elif error and self._is_read_error(error):
    472     # Read retry?
    473     if read is False or method is None or not self._is_method_retryable(method):
--> 474         raise reraise(type(error), error, _stacktrace)
    475     elif read is not None:
    476         read -= 1

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/util/util.py:39, in reraise(tp, value, tb)
     37     if value.__traceback__ is not tb:
     38         raise value.with_traceback(tb)
---> 39     raise value
     40 finally:
     41     value = None  # type: ignore[assignment]

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/connectionpool.py:787, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, preload_content, decode_content, **response_kw)
    784 response_conn = conn if not release_conn else None
    786 # Make the request on the HTTPConnection object
--> 787 response = self._make_request(
    788     conn,
    789     method,
    790     url,
    791     timeout=timeout_obj,
    792     body=body,
    793     headers=headers,
    794     chunked=chunked,
    795     retries=retries,
    796     response_conn=response_conn,
    797     preload_content=preload_content,
    798     decode_content=decode_content,
    799     **response_kw,
    800 )
    802 # Everything went great!
    803 clean_exit = True

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/connectionpool.py:536, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
    534     response = conn.getresponse()
    535 except (BaseSSLError, OSError) as e:
--> 536     self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
    537     raise
    539 # Set properties that are used by the pooling layer.

File ~/miniconda3/envs/conenv_dat_science/lib/python3.12/site-packages/urllib3/connectionpool.py:367, in HTTPConnectionPool._raise_timeout(self, err, url, timeout_value)
    364 """Is the error actually a timeout? Will raise a ReadTimeout or pass"""
    366 if isinstance(err, SocketTimeout):
--> 367     raise ReadTimeoutError(
    368         self, url, f"Read timed out. (read timeout={timeout_value})"
    369     ) from err
    371 # See the above comment about EAGAIN in Python 3.
    372 if hasattr(err, "errno") and err.errno in _blocking_errnos:

ReadTimeoutError: HTTPConnectionPool(host='localhost', port=33877): Read timed out. (read timeout=120)

How can we reproduce the issue?

import requests
import base64
import secrets
import hashlib
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# Generate PKCE code verifier and challenge (recommended for public clients)
def generate_pkce_pair():
    code_verifier = secrets.token_urlsafe(64)
    code_challenge = (
        base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest())
        .decode()
        .rstrip("=")
    )
    return code_verifier, code_challenge


# Callback handler to receive the authorization code
class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.end_headers()

        # Extract authorization code from URL query parameters
        query_components = parse_qs(urlparse(self.path).query)

        if "code" in query_components:
            # Store the auth code in the server instance
            self.server.auth_code = query_components["code"][0]

            # Display success message to the user
            self.wfile.write(
                b"<html><body><h1>Authentication successful!</h1><p>You can close this window now.</p></body></html>"
            )
        else:
            # Handle error or cancel
            self.wfile.write(
                b"<html><body><h1>Authentication failed!</h1><p>No authorization code received.</p></body></html>"
            )
            self.server.auth_code = None

        # Signal that we have processed the callback
        self.server.handled = True


def get_webpage_resp():

# Generate PKCE code pair
    code_verifier, code_challenge = generate_pkce_pair()

    # Create the authorization URL
    auth_url = (
        f"{auth_endpoint}"
        f"?client_id={CLIENT_ID}"
        f"&response_type=code"
        f"&redirect_uri={REDIRECT_URI}"
        f"&scope={SCOPE}"
        f"&code_challenge={code_challenge}"
        f"&code_challenge_method=S256"
        f"&state={secrets.token_urlsafe(16)}"  # State parameter to prevent CSRF
    )

    # Replace 'your_path_to_webdriver' with the path to your WebDriver executable
    driver = webdriver.Chrome()

    # Open the webpage
    driver.get(auth_url)

    # Fill out form fields by finding them by name, id, or other attributes
    username_field = driver.find_element(By.ID, 'username')
    password_field = driver.find_element(By.ID, 'password')

    username_field.send_keys('blabla_user')
    password_field.send_keys('blabla_pwd') 

    # Start local server to receive the callback
    server = HTTPServer(("localhost", 8000), CallbackHandler)
    server.auth_code = None
    server.handled = False        

    # Send the Enter/Return key to any of the form fields
    password_field.send_keys(Keys.RETURN)   

    # Wait for the callback to be processed
    print("Waiting for authentication callback...")
    while not server.handled:
        server.handle_request()

    # Close the browser
    driver.quit()

    auth_code = server.auth_code
    if not auth_code:
        print("Authentication failed: No authorization code received")
        return

    print(f"Authorization code received: {auth_code}")

    # Exchange authorization code for tokens
    token_data = {
        "grant_type": "authorization_code",
        "code": auth_code,
        "redirect_uri": REDIRECT_URI,
        "client_id": CLIENT_ID,
        "code_verifier": code_verifier,
    }

    # Add client secret for confidential clients
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    if CLIENT_SECRET:
        token_data["client_secret"] = CLIENT_SECRET

    # Request tokens
    response = requests.post(token_endpoint, data=token_data, headers=headers)

    if response.status_code == 200:
        tokens = response.json()
        print("Authentication successful!")
        print(f"Access Token: {tokens['access_token'][:20]}...")

        if "refresh_token" in tokens:
            print(f"Refresh Token: {tokens['refresh_token'][:20]}...")

        # Now you can use the access token to call your API
        # Example API call:
        api_response = requests.get(
            "https://some-adress",
            headers={"Authorization": f"Bearer {tokens['access_token']}"},
        )
        print(api_response.text)

    else:
        print(f"Token request failed: {response.status_code}")
        print(response.text)

    return response

Relevant log output

No logs

Operating System

Ubuntu 24.04

Selenium version

4.29.0

What are the browser(s) and version(s) where you see this issue?

Chromium Version 134.0.6998.35 (Official Build) (64-bit)

What are the browser driver(s) and version(s) where you see this issue?

ChromeDriver 134.0.6998.35, geckodriver 0.36.0 ( 2025-02-25)

Are you using Selenium Grid?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-needs-triagingA Selenium member will evaluate this soon!I-defectSomething is not working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions