I am trying to replicate in Wolfram Language the functionality of the following Python code which uses Selenium:
tradestation_headless_auth.py
import requests
import json
from urllib.parse import unquote
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
API_KEY = '*******' # Also called the `client_id`
SECRET_KEY = '*******'
REDIRECT_URI = 'http://localhost:8080' # Arbitrary for headless.
BASE_URL = 'https://sim-api.tradestation.com/v2'
USERNAME = "*******"
PASSWORD = "*******"
#
# Security questions and answers...
#
QUESTION_ONE = "*******"
QUESTION_TWO = "*******"
QUESTION_THREE = "*******"
ANSWER_ONE = "*******"
ANSWER_TWO = "*******"
ANSWER_THREE = "*******"
#
# Once we have the auth code, we can exchange it for the refresh token and access token.
#
def get_token_response(access_code):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
post_data = {
'grant_type': 'authorization_code',
'client_id': API_KEY,
'client_secret': SECRET_KEY,
'redirect_uri': REDIRECT_URI,
'code': access_code
}
r = requests.post(f'{BASE_URL}/Security/Authorize', headers=headers, data=post_data)
try:
return r.json()
except:
return sys.exc_info()[0]
#
# Start Selenium...
#
options = Options()
options.headless = True
driver = webdriver.Firefox(options=options)
auth_url = f"{BASE_URL}/authorize?redirect_uri={REDIRECT_URI}&client_id={API_KEY}&response_type=code"
try:
driver.get(auth_url)
elem = driver.find_element_by_name("username")
elem.clear()
elem.send_keys(USERNAME)
elem = driver.find_element_by_name("password")
elem.clear()
elem.send_keys(PASSWORD)
driver.find_element_by_id("btnLogin").click()
elem = driver.find_element_by_name("ucQuestionAndAnswer$txtAnswer")
elem.clear()
if QUESTION_ONE in driver.page_source:
elem.send_keys(ANSWER_ONE)
elif QUESTION_TWO in driver.page_source:
elem.send_keys(ANSWER_TWO)
elif QUESTION_THREE in driver.page_source:
elem.send_keys(ANSWER_THREE)
else:
print("Uh oh...") # Should probabaly raise exception here.
driver.find_element_by_id("ucQuestionAndAnswer_btnLogin").click()
#
# Lol. If you know a better way to do this, please let me know.
#
except Exception as e:
s = unquote(e.msg) # `e.msg` is in UTF-8, using `unquote` just makes it easier to read/find the start of the access_code from the error message. Not necessary, however.
a, b = s.find("code="), s.find("&c=UTF-8")
access_code = (s[a+5:b]) # The `+5` here is just the length of the string you used to `find` the start of the access_code.
token_response = get_token_response(access_code)
refresh_token = token_response['refresh_token']
access_token = token_response['access_token']
print("--- Token Response ---")
print(token_response)
print("--- Access Code ---")
print(access_code)
print("--- Access Token ---")
print(access_token)
print("--- Refresh Token ---")
print(refresh_token)
driver.close()
I started with the following:
authURL =
signInURL <> "/authorize?" <> "response_type=code" <>
"&client_id=" <> apiKey <> "&audience=" <> apiURL <>
"&redirect_uri=" <> redirectURI <> "&scope=" <> scope;
response =
URLRead[HTTPRequest[
authURL, <|"Method" -> "POST",
"Headers" -> {"Content-Type" ->
"application/x-www-form-urlencoded"},
"Body" -> "username=" <> username <> "&password=" <> password|>],
"Body"];
authURL is correct, but response contains "Not Found", maybe because I an not using URLRead correctly.
Perhaps I need to take a completely different approach, possibly using StartWebSession[].
Any thoughts or guidance would be much appreciated.
URLRead
. (4) The giant dump of python code does not really belong to this site, since this is a site about Mathematica ... $\endgroup$