-
Notifications
You must be signed in to change notification settings - Fork 490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
if challenge_respons['status']; key error 'status' #537
Comments
@clarin-ebtio800090 is this meant to be a working solution? _validate_sherrif_id is not defined and the time module is not imported in your solution. Thank you for digging into the issue however. |
i tried it and it didn't work |
Looks like only sms codes are used for authentication even though mfa is setup as authentication method. Not sure if its a new feature or bugged-release on RH |
any idea how to handle this? |
It seems to be that Robinhood insists on using the Trusted Device authentication method even after selecting and setting up the Authenticator App method and using the QR affiliated code. |
Does anyone know if it's the SMS code in addition to the authentication app? Or is it now just SMS? |
from what i can tell, it's both. when Authenticator App is selected in RobinHood it will set up with a QR & Code, but at login RobinHood will attempt to use Trusted Device instead. and with SMS Authentication set Stocks no longer prompts for input of the SMS code. |
Same issue here. Would anyone be able to help me out? |
I have been trying to check with robinhood support as they seem to have abandoned multi factor authentication option and using only sms, photo, gov-id, bank as valid form of authentication methods |
I am experiencing issues too. Bot will not log in and at the same time App will ask me to verify new device, which I do, but it dose not seem to go thru. |
Do we any way of using the pickle file from browser to be used instead of pickle file we are generating from app. If there is a way, can you please share the steps. |
workaround: Looks like this only works for 30 mins (sorry) def replace_pickle(code):
# Step 1: Create a Backup
shutil.copy(original_file_path, backup_file_path)
print(f"Backup created at: {backup_file_path}")
# Step 2: Load the Original Pickle File
with open(original_file_path, 'rb') as f:
data = pickle.load(f)
# Step 3: Replace the Bearer Token
new_bearer_token = code
if isinstance(data, dict) and "access_token" in data:
data["access_token"] = new_bearer_token
print("Bearer token replaced.")
else:
print("Bearer token not found in the pickle file.")
# Step 4: Save the Updated Pickle File
with open(original_file_path, 'wb') as f:
pickle.dump(data, f)
print(f"Updated pickle file saved at: {original_file_path}") |
I am not the best at usely solely network requests to login, so I'm going to automate the login via selenium, extract the token, and then just give it to the robinhood module. |
So what has changed from my perspective : I can log in via browser, when I do now I get a Robinhood app notification on my phone to verify it's me and a prompt to allow a new device, which I do and I am able to log in to the account via browser. Until now this did not exist and verification was done by entering / passing the 2FA code generated via totp = pyotp.TOTP(log_2FA).now() Now the Robinhood app notification on my phone happens the same when trying to log in via the script, however, after verifying new device in the app, I get an exception error and script just terminates it self ( have no procedure in place to handle that exception at this time ) . I still generate the 2FA code, the .token folder gets created but no pickle file is written, and the phone app validation fails and program just craps out on the exception created when trying to log in: login = r.login(log_User, log_Pass, mfa_code=totp) Seems Authentication.py will need to be modified again, whish I was smart enough to figure that one. Sorry Folks and thanks. |
Yep, indeed. Authentication.py needs to be modified to handle the new app notification method. I made the following modifications to the auth module and got it to work! bhyman67/Mods-to-robin-stocks-Authentication@02e5491 You should have 2 minutes to approve the login request in the Robinhood app from the moment that you call the login routine. |
This works perfectly! You truly saved my day, Mr. Hyman. Many thanks and much respect! |
brilliant stuff @bhyman67, where's the tip button on this thing? now the age old question: does anyone know how long these pickles last? ...and how can we make them last longer. |
I appreciate you! Wasn't looking forward to trying to figure out how to fix this. Now I just need to find a service that can grab the sms code for me. |
@bhyman67 If you have time to create a PR to fix this issue I would greatly appreciate it! I just merged another PR, so you should update your branch. |
Sounds good, @jmfernandes! I'll try to get to that sometime soon. |
Thanks so much, I got the Bot to log in. |
Is the new phone verification step intentional? That seems to defeat the point of having 2FA if they're going to just ask for a code with SMS anyways... |
What is the login flow now with these changes if we were to use external 2FA app?
|
is it possible to login by script without needing to manually approve the login request in the Robinhood app? |
I was able to successfully generate a fresh pickle file using this method - thank you for putting this solution together. However, I deleted the pickle file and reran to refresh the pickle, and app on phone is no longer generating the verification. Two minute window expires and no pickle file is generated. Anyone else experiencing this? I tried reverting to original authentication.py to see if it would work after having approved via app verification, but no success. I'm wondering if the revised authentication.py needs additional edits for handling cases where Robinhood account has already logged approval via the new verification process? |
Yeah I ran into the same problem... It worked initially, but then that same problem happened to me. Which is why I paused on going any further on the pull request. I think you're right that this needs further edits and testing... This doesn't seem to be a full working solution yet... |
For anyone who wants full automation, you can use something like selenium to manually plug in your username, password and possibly Auth app code and then plug in the sms code by linking robinhood to a Google voice number, having it forward the message to your Google email and then by using the Gmail app to read the code programmatically and plug it into the page. Convoluted, but that's the free option. Otherwise, you could use a Twilio number. |
Try a different IP? |
@ Kr1msonReaper; > For anyone who wants full automation, you can use something like selenium to manually plug in your username, password and possibly Auth app code and then plug in the sms code by linking robinhood to a Google voice number, having it forward the message to your Google email and then by using the Gmail app to read the code programmatically and plug it into the page.
Are you able to have your script log in without having to manually verify new device manually in the app ? |
I believe the SMS option is only available if you don't have the app. I haven't implemented the automation for myself because a single approval seems to be good for a while. Might get to it when I have the time and motivation. |
@mike-labadessa @nickreynolds84 @henryzhangpku @alex-l-zhou @schnup |
Triggering the rs.update_session() every 23 hours makes sense because 86400 seconds equal one day. Maybe Robinhood devs hardcoded 86400 on their end somewhere. Thanks @Gates8911 |
If anyone needs another authentication to try let me know, I made one more, wasnt fully convinced I had all possibilities covered. Didnt automatically post it bc i didnt want to spam this chat with more code. Just email me if you need another one to try. [email protected] |
Thanks so much for your time and contribution to the cause ;), same goes to all in here. |
Just curious why no one here uses alpaca instead |
Overpriced--Much better to use programming skills to do it yourself.
…On Thu, Feb 6, 2025, 12:54 PM Ebenezer Adams ***@***.***> wrote:
Just curious why no one here uses alpaca instead
—
Reply to this email directly, view it on GitHub
<#537 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AKFGJXLWNTLFP7KGHDZS5JD2OO4VTAVCNFSM6AAAAABVNEZ24CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMNBQHA2TIMZWGA>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
How specifically are they overpriced? Aren't their spreads similar to Robinhood's? Although actually building this unofficial Robinhood API has taken a lot of work, it doesn't seems to me like there is any difference in "programming skills" required to use it vs. using Alpaca's official one. |
@doctorcolossus @Kr1msonReaper @nickreynolds84 @mobeston @Adelantado @mike-labadessa @henryzhangpku @alex-l-zhou @ravi-bharadwaj @pielshawn @zabidin901 @HMSS013 @jmfernandes @cpasean @nat2k5us @schnup
|
Amazing Job, mate! Did you submit a pull request for this fix, by chance? |
Not that familiar with github, not sure how to do that, but figured if it
works well then someone else will
…On Wed, Feb 12, 2025, 04:38 Dylan-86 ***@***.***> wrote:
@doctorcolossus <https://github.com/doctorcolossus> @Kr1msonReaper
<https://github.com/Kr1msonReaper> @nickreynolds84
<https://github.com/nickreynolds84> @mobeston
<https://github.com/mobeston> @Adelantado <https://github.com/Adelantado>
@mike-labadessa <https://github.com/mike-labadessa> @henryzhangpku
<https://github.com/henryzhangpku> @alex-l-zhou
<https://github.com/alex-l-zhou> @ravi-bharadwaj
<https://github.com/ravi-bharadwaj> @pielshawn
<https://github.com/pielshawn> @zabidin901 <https://github.com/zabidin901>
@HMSS013 <https://github.com/HMSS013> @jmfernandes
<https://github.com/jmfernandes> @cpasean <https://github.com/cpasean>
@nat2k5us <https://github.com/nat2k5us> @schnup
<https://github.com/schnup> Since I see theres still a few issues people
are having ill post my latest one that has had zero issues now across 3
different accounts, one using device approvals, another using email, and
another using sms, (this code can be used with 3rd party 2fa but since
robinhoods api doesnt support 2fa with the last update it is not necessary
to include 2fa but will still work, I kept the option in bc the case may be
that robinhood found security concerns with their 2fa and may fix it and
change back to it, in which case it would be nice to still have some of the
2fa code, but this script really just needs username and pass even though
2fa is still an option). I also realize this is quite a bit longer than
previous versions but it has to check for all 3 verification methods and
added retries to the logic with various events and more output logs for
different scenarios to better diagnose a failure. If this doesnt work for
you then I would check login method thoroughly (I say this because I
literally cant improve this script any further, if someone else can more
power to ya, if this works for everyone I think it should be merged for now)
import getpass
import os
import pickle
import secrets
import time
from robin_stocks.robinhood.helper import *
from robin_stocks.robinhood.urls import *
def generate_device_token():
"""Generates a cryptographically secure device token."""
rands = [secrets.randbelow(256) for _ in range(16)]
hexa = [str(hex(i + 256)).lstrip("0x")[1:] for i in range(256)]
token = ""
for i, r in enumerate(rands):
token += hexa[r]
if i in [3, 5, 7, 9]:
token += "-"
return token
def _get_sherrif_id(data):
"""Extracts the sheriff verification ID from the response."""
if "id" in data:
return data["id"]
raise Exception("Error: No verification ID returned in user-machine response")
def _validate_sherrif_id(device_token: str, workflow_id: str):
"""Handles Robinhood's verification workflow, including email, SMS, and app-based approvals."""
print("Starting verification process...")
pathfinder_url = "https://api.robinhood.com/pathfinder/user_machine/"
machine_payload = {'device_id': device_token, 'flow': 'suv', 'input': {'workflow_id': workflow_id}}
machine_data = request_post(url=pathfinder_url, payload=machine_payload, json=True)
machine_id = _get_sherrif_id(machine_data)
inquiries_url = f"https://api.robinhood.com/pathfinder/inquiries/{machine_id}/user_view/"
start_time = time.time()
while time.time() - start_time < 120: # 2-minute timeout
time.sleep(5)
inquiries_response = request_get(inquiries_url)
if not inquiries_response: # Handle case where response is None
print("Error: No response from Robinhood API. Retrying...")
continue
if "context" in inquiries_response and "sheriff_challenge" in inquiries_response["context"]:
challenge = inquiries_response["context"]["sheriff_challenge"]
challenge_type = challenge["type"]
challenge_status = challenge["status"]
challenge_id = challenge["id"]
if challenge_type == "prompt":
print("Check robinhood app for device approvals method...")
prompt_url = f"https://api.robinhood.com/push/{challenge_id}/get_prompts_status/"
while True:
time.sleep(5)
prompt_challenge_status = request_get(url=prompt_url)
if prompt_challenge_status["challenge_status"] == "validated":
break
break
if challenge_status == "validated":
print("Verification successful!")
break # Stop polling once verification is complete
if challenge_type in ["sms", "email"] and challenge_status == "issued":
user_code = input(f"Enter the {challenge_type} verification code sent to your device: ")
challenge_url = f"https://api.robinhood.com/challenge/{challenge_id}/respond/"
challenge_payload = {"response": user_code}
challenge_response = request_post(url=challenge_url, payload=challenge_payload)
if challenge_response.get("status") == "validated":
break
# **Now poll the workflow status to confirm final approval**
inquiries_url = f"https://api.robinhood.com/pathfinder/inquiries/{machine_id}/user_view/"
retry_attempts = 5 # Allow up to 5 retries in case of 500 errors
while time.time() - start_time < 120: # 2-minute timeout
try:
inquiries_payload = {"sequence": 0, "user_input": {"status": "continue"}}
inquiries_response = request_post(url=inquiries_url, payload=inquiries_payload,json=True)
if "type_context" in inquiries_response and inquiries_response["type_context"]["result"] == "workflow_status_approved":
print("Verification successful!")
return
else:
time.sleep(5) # **Increase delay between requests to prevent rate limits**
except requests.exceptions.RequestException as e:
time.sleep(5)
print(f"API request failed: {e}")
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
print("Retrying workflow status check...")
continue
if not inquiries_response: # Handle None response
time.sleep(5)
print("Error: No response from Robinhood API. Retrying...")
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
continue
workflow_status = inquiries_response.get("verification_workflow", {}).get("workflow_status")
if workflow_status == "workflow_status_approved":
print("Workflow status approved! Proceeding with login...")
return
elif workflow_status == "workflow_status_internal_pending":
print("Still waiting for Robinhood to finalize login approval...")
else:
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
raise TimeoutError("Timeout reached. Assuming login is approved and proceeding.")
def login(username=None, password=None, expiresIn=86400, scope='internal', store_session=True, mfa_code=None, pickle_path="", pickle_name=""):
"""Handles the login process to Robinhood, including multi-factor authentication, session persistence, and verification handling."""
print("Starting login process...")
device_token = generate_device_token()
home_dir = os.path.expanduser("~")
data_dir = os.path.join(home_dir, ".tokens")
if pickle_path:
if not os.path.isabs(pickle_path):
pickle_path = os.path.normpath(os.path.join(os.getcwd(), pickle_path))
data_dir = pickle_path
if not os.path.exists(data_dir):
os.makedirs(data_dir)
creds_file = "robinhood" + pickle_name + ".pickle"
pickle_path = os.path.join(data_dir, creds_file)
url = login_url()
login_payload = {
'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
'expires_in': expiresIn,
'grant_type': 'password',
'password': password,
'scope': scope,
'username': username,
'device_token': device_token,
'try_passkeys': False,
'token_request_path': '/login',
'create_read_only_secondary_token': True,
}
if mfa_code:
login_payload['mfa_code'] = mfa_code
# If authentication has been stored in pickle file then load it. Stops login server from being pinged so much.
if os.path.isfile(pickle_path):
# **Load cached authentication session if available**
if store_session:
try:
with open(pickle_path, 'rb') as f:
pickle_data = pickle.load(f)
access_token = pickle_data['access_token']
token_type = pickle_data['token_type']
refresh_token = pickle_data['refresh_token']
pickle_device_token = pickle_data['device_token']
login_payload['device_token'] = pickle_device_token
set_login_state(True)
update_session(
'Authorization', '{0} {1}'.format(token_type, access_token))
# Try to load account profile to check that authorization token is still valid.
res = request_get(
positions_url(), 'pagination', {'nonzero': 'true'}, jsonify_data=False)
# Raises exception if response code is not 200.
res.raise_for_status()
return({'access_token': access_token, 'token_type': token_type,
'expires_in': expiresIn, 'scope': scope,
'detail': 'logged in using authentication in {0}'.format(creds_file),
'backup_code': None, 'refresh_token': refresh_token})
except Exception:
print(
"ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally.", file=get_output())
set_login_state(False)
update_session('Authorization', None)
else:
os.remove(pickle_path)
# **Attempt to login normally**
if not username:
username = input("Robinhood username: ")
login_payload['username'] = username
if not password:
password = getpass.getpass("Robinhood password: ")
login_payload['password'] = password
data = request_post(url, login_payload)
if data:
try:
if 'verification_workflow' in data:
print("Verification required, handling challenge...")
workflow_id = data['verification_workflow']['id']
_validate_sherrif_id(device_token, workflow_id)
# Reattempt login after verification
data = request_post(url, login_payload)
if 'access_token' in data:
token = '{0} {1}'.format(data['token_type'], data['access_token'])
update_session('Authorization', token)
set_login_state(True)
if store_session:
with open(pickle_path, 'wb') as f:
pickle.dump({'token_type': data['token_type'],
'access_token': data['access_token'],
'refresh_token': data['refresh_token'],
'device_token': login_payload['device_token']}, f)
return data
except Exception as e:
print(f"Error during login verification: {e}")
print("Login failed. Check credentials and try again.")
return None
@login_required
def logout():
"""Logs out from Robinhood by clearing session data."""
set_login_state(False)
update_session('Authorization', None)
print("Logged out successfully.")
Amazing Job, mate!
Just tried and it works like a charm.
Did you submit a pull request for this fix, by chance?
—
Reply to this email directly, view it on GitHub
<#537 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BG5BJITRV4TP76GO7Q2TWZ32PMJC5AVCNFSM6AAAAABVNEZ24CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMNJTGEZTONRVGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
[image: Dylan-86]*Dylan-86* left a comment (jmfernandes/robin_stocks#537)
<#537 (comment)>
@doctorcolossus <https://github.com/doctorcolossus> @Kr1msonReaper
<https://github.com/Kr1msonReaper> @nickreynolds84
<https://github.com/nickreynolds84> @mobeston
<https://github.com/mobeston> @Adelantado <https://github.com/Adelantado>
@mike-labadessa <https://github.com/mike-labadessa> @henryzhangpku
<https://github.com/henryzhangpku> @alex-l-zhou
<https://github.com/alex-l-zhou> @ravi-bharadwaj
<https://github.com/ravi-bharadwaj> @pielshawn
<https://github.com/pielshawn> @zabidin901 <https://github.com/zabidin901>
@HMSS013 <https://github.com/HMSS013> @jmfernandes
<https://github.com/jmfernandes> @cpasean <https://github.com/cpasean>
@nat2k5us <https://github.com/nat2k5us> @schnup
<https://github.com/schnup> Since I see theres still a few issues people
are having ill post my latest one that has had zero issues now across 3
different accounts, one using device approvals, another using email, and
another using sms, (this code can be used with 3rd party 2fa but since
robinhoods api doesnt support 2fa with the last update it is not necessary
to include 2fa but will still work, I kept the option in bc the case may be
that robinhood found security concerns with their 2fa and may fix it and
change back to it, in which case it would be nice to still have some of the
2fa code, but this script really just needs username and pass even though
2fa is still an option). I also realize this is quite a bit longer than
previous versions but it has to check for all 3 verification methods and
added retries to the logic with various events and more output logs for
different scenarios to better diagnose a failure. If this doesnt work for
you then I would check login method thoroughly (I say this because I
literally cant improve this script any further, if someone else can more
power to ya, if this works for everyone I think it should be merged for now)
import getpass
import os
import pickle
import secrets
import time
from robin_stocks.robinhood.helper import *
from robin_stocks.robinhood.urls import *
def generate_device_token():
"""Generates a cryptographically secure device token."""
rands = [secrets.randbelow(256) for _ in range(16)]
hexa = [str(hex(i + 256)).lstrip("0x")[1:] for i in range(256)]
token = ""
for i, r in enumerate(rands):
token += hexa[r]
if i in [3, 5, 7, 9]:
token += "-"
return token
def _get_sherrif_id(data):
"""Extracts the sheriff verification ID from the response."""
if "id" in data:
return data["id"]
raise Exception("Error: No verification ID returned in user-machine response")
def _validate_sherrif_id(device_token: str, workflow_id: str):
"""Handles Robinhood's verification workflow, including email, SMS, and app-based approvals."""
print("Starting verification process...")
pathfinder_url = "https://api.robinhood.com/pathfinder/user_machine/"
machine_payload = {'device_id': device_token, 'flow': 'suv', 'input': {'workflow_id': workflow_id}}
machine_data = request_post(url=pathfinder_url, payload=machine_payload, json=True)
machine_id = _get_sherrif_id(machine_data)
inquiries_url = f"https://api.robinhood.com/pathfinder/inquiries/{machine_id}/user_view/"
start_time = time.time()
while time.time() - start_time < 120: # 2-minute timeout
time.sleep(5)
inquiries_response = request_get(inquiries_url)
if not inquiries_response: # Handle case where response is None
print("Error: No response from Robinhood API. Retrying...")
continue
if "context" in inquiries_response and "sheriff_challenge" in inquiries_response["context"]:
challenge = inquiries_response["context"]["sheriff_challenge"]
challenge_type = challenge["type"]
challenge_status = challenge["status"]
challenge_id = challenge["id"]
if challenge_type == "prompt":
print("Check robinhood app for device approvals method...")
prompt_url = f"https://api.robinhood.com/push/{challenge_id}/get_prompts_status/"
while True:
time.sleep(5)
prompt_challenge_status = request_get(url=prompt_url)
if prompt_challenge_status["challenge_status"] == "validated":
break
break
if challenge_status == "validated":
print("Verification successful!")
break # Stop polling once verification is complete
if challenge_type in ["sms", "email"] and challenge_status == "issued":
user_code = input(f"Enter the {challenge_type} verification code sent to your device: ")
challenge_url = f"https://api.robinhood.com/challenge/{challenge_id}/respond/"
challenge_payload = {"response": user_code}
challenge_response = request_post(url=challenge_url, payload=challenge_payload)
if challenge_response.get("status") == "validated":
break
# **Now poll the workflow status to confirm final approval**
inquiries_url = f"https://api.robinhood.com/pathfinder/inquiries/{machine_id}/user_view/"
retry_attempts = 5 # Allow up to 5 retries in case of 500 errors
while time.time() - start_time < 120: # 2-minute timeout
try:
inquiries_payload = {"sequence": 0, "user_input": {"status": "continue"}}
inquiries_response = request_post(url=inquiries_url, payload=inquiries_payload,json=True)
if "type_context" in inquiries_response and inquiries_response["type_context"]["result"] == "workflow_status_approved":
print("Verification successful!")
return
else:
time.sleep(5) # **Increase delay between requests to prevent rate limits**
except requests.exceptions.RequestException as e:
time.sleep(5)
print(f"API request failed: {e}")
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
print("Retrying workflow status check...")
continue
if not inquiries_response: # Handle None response
time.sleep(5)
print("Error: No response from Robinhood API. Retrying...")
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
continue
workflow_status = inquiries_response.get("verification_workflow", {}).get("workflow_status")
if workflow_status == "workflow_status_approved":
print("Workflow status approved! Proceeding with login...")
return
elif workflow_status == "workflow_status_internal_pending":
print("Still waiting for Robinhood to finalize login approval...")
else:
retry_attempts -= 1
if retry_attempts == 0:
raise TimeoutError("Max retries reached. Assuming login approved and proceeding.")
raise TimeoutError("Timeout reached. Assuming login is approved and proceeding.")
def login(username=None, password=None, expiresIn=86400, scope='internal', store_session=True, mfa_code=None, pickle_path="", pickle_name=""):
"""Handles the login process to Robinhood, including multi-factor authentication, session persistence, and verification handling."""
print("Starting login process...")
device_token = generate_device_token()
home_dir = os.path.expanduser("~")
data_dir = os.path.join(home_dir, ".tokens")
if pickle_path:
if not os.path.isabs(pickle_path):
pickle_path = os.path.normpath(os.path.join(os.getcwd(), pickle_path))
data_dir = pickle_path
if not os.path.exists(data_dir):
os.makedirs(data_dir)
creds_file = "robinhood" + pickle_name + ".pickle"
pickle_path = os.path.join(data_dir, creds_file)
url = login_url()
login_payload = {
'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
'expires_in': expiresIn,
'grant_type': 'password',
'password': password,
'scope': scope,
'username': username,
'device_token': device_token,
'try_passkeys': False,
'token_request_path': '/login',
'create_read_only_secondary_token': True,
}
if mfa_code:
login_payload['mfa_code'] = mfa_code
# If authentication has been stored in pickle file then load it. Stops login server from being pinged so much.
if os.path.isfile(pickle_path):
# **Load cached authentication session if available**
if store_session:
try:
with open(pickle_path, 'rb') as f:
pickle_data = pickle.load(f)
access_token = pickle_data['access_token']
token_type = pickle_data['token_type']
refresh_token = pickle_data['refresh_token']
pickle_device_token = pickle_data['device_token']
login_payload['device_token'] = pickle_device_token
set_login_state(True)
update_session(
'Authorization', '{0} {1}'.format(token_type, access_token))
# Try to load account profile to check that authorization token is still valid.
res = request_get(
positions_url(), 'pagination', {'nonzero': 'true'}, jsonify_data=False)
# Raises exception if response code is not 200.
res.raise_for_status()
return({'access_token': access_token, 'token_type': token_type,
'expires_in': expiresIn, 'scope': scope,
'detail': 'logged in using authentication in {0}'.format(creds_file),
'backup_code': None, 'refresh_token': refresh_token})
except Exception:
print(
"ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally.", file=get_output())
set_login_state(False)
update_session('Authorization', None)
else:
os.remove(pickle_path)
# **Attempt to login normally**
if not username:
username = input("Robinhood username: ")
login_payload['username'] = username
if not password:
password = getpass.getpass("Robinhood password: ")
login_payload['password'] = password
data = request_post(url, login_payload)
if data:
try:
if 'verification_workflow' in data:
print("Verification required, handling challenge...")
workflow_id = data['verification_workflow']['id']
_validate_sherrif_id(device_token, workflow_id)
# Reattempt login after verification
data = request_post(url, login_payload)
if 'access_token' in data:
token = '{0} {1}'.format(data['token_type'], data['access_token'])
update_session('Authorization', token)
set_login_state(True)
if store_session:
with open(pickle_path, 'wb') as f:
pickle.dump({'token_type': data['token_type'],
'access_token': data['access_token'],
'refresh_token': data['refresh_token'],
'device_token': login_payload['device_token']}, f)
return data
except Exception as e:
print(f"Error during login verification: {e}")
print("Login failed. Check credentials and try again.")
return None
@login_required
def logout():
"""Logs out from Robinhood by clearing session data."""
set_login_state(False)
update_session('Authorization', None)
print("Logged out successfully.")
Amazing Job, mate!
Just tried and it works like a charm.
Did you submit a pull request for this fix, by chance?
—
Reply to this email directly, view it on GitHub
<#537 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BG5BJITRV4TP76GO7Q2TWZ32PMJC5AVCNFSM6AAAAABVNEZ24CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMNJTGEZTONRVGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Posted @Gates8911 change as a PR here: #549 |
Has anyone tried the API key method to see if it will help with not having to authenticate every time our bots run? https://docs.robinhood.com/crypto/trading/ This is the API for Robinhood crypto trading. But I'm hoping the calls to the stock side will also work with the API key provided for this. Just haven't had any time to experiment with it. Wondering if anyone else tried this already. My apologies if this was already brought up and tried. |
Started getting this error since yesterday. No notification on phone for this. |
No longer works.
|
Updated. @Gates8911 Thank you for sharing the code. This is very insightful! I briefly tested today and I found one tiny issue that stop robinhood client from sending email as expected. The issue is in
A limitation of the latest implementation is that it still requires user to manually enter the email code in the terminal after a period of time which is not always possible and sometimes inconvenient. To bypass this limitation, I improved the script to automatically login your email (im using gmail but you can check the code to adapt to your email provider, and you would need your environment to have those variables, for instance, gmail address and gmail app password), find the email with verification code and parse it, and automatically respond to the robin's client to login. I also want other people in the community to further test it out (e.g., testing with
|
hi all. read all the comments, but still not sure if there is a working solution. any solution... SMS, non-SMS, with authenticator, without, with robinhood app installed, should I delete it?... what should I do? thanks. |
ok these changes actually solved it for me. Will use this for now, and wait patientlessly for official fix 👍 |
Well, I couldn't believe it, but they must really hate us automating logins. They've removed SMS as a method to verify. Unreal. SMS was able to be automated, now I have no idea how to workaround this while they're only allowing device approvals. EDIT: If you are logged out of Robinhood on your phone, leaving NO ability for the device approvals function to work, then the authentication.py script will default to SMS. So it still exists, but isn't a selectable option in the app. |
I automated my main script by triggering a logout and re-login after 84000 seconds (which is before the shortest possible approved session expires which is 86400, typically robinhoods backend will give you a longer session but my current understanding is it varies even though the input is 86400 and under those circumstances in order to ensure a re-login is always triggered before session is we must assume and account for shortest session time which is why i had mine trigger at 84000). anyway this is my latest authentication script, keep in mind if you use mfa with this auth, robinhoods backend forces the in-app prompt verification, if you use sms, last I tried it worked as expected, same as email.
|
Yo Gates, what’s up |
Is there any way to use/force sms to login? I don't get device approvals and some of the scripts above only send me an sms after I manually exit the authentication process with control c. |
Login is not operating as before. The RH app prompts me to confirm that I'm attempting to log in but the program does not authenticate.
The text was updated successfully, but these errors were encountered: