-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Closed
Labels
A-needs-triagingA Selenium member will evaluate this soon!A Selenium member will evaluate this soon!I-defectSomething is not working as intendedSomething is not working as intended
Description
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 responseRelevant log output
No logsOperating 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
Labels
A-needs-triagingA Selenium member will evaluate this soon!A Selenium member will evaluate this soon!I-defectSomething is not working as intendedSomething is not working as intended