Skip to content
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

Can No Longer Login To Robinhood via robin-stocks API #513

Open
Two20Two21 opened this issue Dec 7, 2024 · 6 comments
Open

Can No Longer Login To Robinhood via robin-stocks API #513

Two20Two21 opened this issue Dec 7, 2024 · 6 comments

Comments

@Two20Two21
Copy link

Two20Two21 commented Dec 7, 2024

Problem Statement:

I have been running robin-stocks API to access Robinhood for a few months. I've had no issues until a few days ago. Now, I cannot login.

Previously, Robinhood has asked me to verify it's me and then I have to input an MFA code. Now, it still asks me to verify it's me but before asking me to input the MFA code, it stops due to an unhandled exception. Looking at the API code, it appears the code expects an 'access_token' to be returned. When there isn't one, it throws the exception. I get back code 403.

I have not changed anything I am aware of. I've even gone back to a compiled version that's been working since 11/21/24 and it now does not work either. I've tried inputting a new Authenticator App code. My email and password are correct. The problem is occurring on two different computers, both previously working fine.

I'm stumped. Please help.

My Code:

import robin_stocks.robinhood
import pyotp
from datetime import *

now = datetime.now()
totp = pyotp.TOTP("KACUJH6EKZZCVZ6K").now()
print("TOTP: " + str(totp))
login = robin_stocks.robinhood.login(my_user_name, my_password, store_session=True)

Console Error:

Connected to pydev debugger (build 242.23339.19)
TOTP: 699780
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm 2024.2.3\plugins\python-ce\helpers\pydev\pydevd.py", line 1570, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\JetBrains\PyCharm 2024.2.3\plugins\python-ce\helpers\pydev_pydev_imps_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:\Users\kbens\OneDrive\Earnie TradeBot\Code\Earnie\StockTest.py", line 13, in
login = robin_stocks.robinhood.login(my_user_name, my_password, store_session=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\kbens\AppData\Local\Programs\Python\Python312\Lib\site-packages\robin_stocks\robinhood\authentication.py", line 197, in login
raise Exception(data['detail'])
~~~~^^^^^^^^^^
KeyError: 'detail'

Process finished with exit code 1

API Code: last line with "raise Exception" is where the error occurs, no 'detail' in data, there was no access_token which seems to be the fundamental problem.

    if 'access_token' in data:
        token = '{0} {1}'.format(data['token_type'], data['access_token'])
        update_session('Authorization', token)
        set_login_state(True)
        data['detail'] = "logged in with brand new authentication code."
        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': payload['device_token']}, f)
    else:
        raise Exception(data['detail'])    <<<<<<<<<<<<<<<<<<< Exception is here
@Christopher-C-Robinson
Copy link

Try using #510. This worked for me.

@Two20Two21
Copy link
Author

Two20Two21 commented Dec 8, 2024 via email

@Christopher-C-Robinson
Copy link

if that doesn't work. Seems this works for those that #510 doesn't work for:
#511 (comment)

@Two20Two21
Copy link
Author

Two20Two21 commented Dec 11, 2024 via email

@joeld1
Copy link

joeld1 commented Jan 2, 2025

if that doesn't work. Seems this works for those that #510 doesn't work for: #511 (comment)

This works.

I think this issue can now be closed. The main take-away is to make sure that if one is setting store_session=False when using PyOTP to automatically generate and input MFA Codes when calling robin_stocks.robinhood.login.

@finlop
Copy link

finlop commented Jan 12, 2025

anyone know how to make the code work for device approvals. i only get device approvals now from my iphone robinhood app. and then the code i run waits for the device approval to be approved and then checks to see if i can log in successfully. but it never sees that i approved the device.

import random
import getpass
import os
import pickle
import requests
import time

Generate a unique device token
def generate_device_token():
"""Generates a unique device token."""
rands = []
for _ in range(16):
rand = random.randint(0, 255)
rands.append(rand)

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
Respond to challenges
def respond_to_challenge(challenge_id, sms_code):
"""Responds to a challenge with the provided code."""
url = f"https://api.robinhood.com/challenge/{challenge_id}/respond/"
payload = {"response": sms_code}
response = requests.post(url, json=payload)
return response.json()

Save and load session data
def save_session(data, pickle_name="robinhood_session.pickle"):
"""Saves session data to a pickle file."""
with open(pickle_name, "wb") as f:
pickle.dump(data, f)

def load_session(pickle_name="robinhood_session.pickle"):
"""Loads session data from a pickle file if it exists."""
if os.path.exists(pickle_name):
with open(pickle_name, "rb") as f:
return pickle.load(f)
return None

def handle_verification_workflow(device_token, workflow_id):
"""Handles verification workflow for device approval."""
print("Handling verification workflow. Please approve the login request on your device.")
url = f"https://api.robinhood.com/pathfinder/user_machine/"
payload = {
"device_id": device_token,
"flow": "suv",
"input": {"workflow_id": workflow_id}
}
response = requests.post(url, json=payload)
if response.status_code == 200:
print("Verification workflow initiated successfully. Waiting for approval...")
else:
raise Exception(f"Failed to initiate verification workflow: {response.text}")

inquiries_url = f"https://api.robinhood.com/pathfinder/inquiries/{response.json()['id']}/user_view/"
max_attempts = 5 # Increase retries to 30 attempts (5 minutes total)
delay_between_attempts = 10 # 10 seconds between checks

for attempt in range(max_attempts):
print(f"Attempt {attempt + 1}: Checking approval status...")
try:
inquiry_response = requests.get(inquiries_url, timeout=10).json()
status = inquiry_response["type_context"]["context"]["sheriff_challenge"]["status"]
print(f"Device approval status: {status}")

    if status == "approved":
        print("Device approval successful!")
        return

    if status == "denied":
        raise Exception("Device approval was denied.")

except Exception as e:
    print(f"Error while checking approval status: {e}")

time.sleep(delay_between_attempts)

print("Device approval timed out. Please ensure you approved the login request.")

Login to Robinhood

def robinhood_login(username=None, password=None, expires_in=86400, scope="internal"):
"""Logs into Robinhood and handles verification workflows."""
device_token = generate_device_token()
print("Generated Device Token:", device_token)

url = "https://api.robinhood.com/oauth2/token/"
payload = {
"grant_type": "password",
"scope": scope,
"client_id": "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS",
"device_token": device_token,
"username": username,
"password": password,
}

session = load_session()
if session:
print("Using saved session.")
return session["access_token"]

response = requests.post(url, data=payload)
data = response.json()

if response.status_code == 200:
print("Login successful.")
save_session(data)
return data["access_token"]

if "verification_workflow" in data:
workflow_id = data["verification_workflow"]["id"]
print(f"Verification workflow triggered. Workflow ID: {workflow_id}")
handle_verification_workflow(device_token, workflow_id)
response = requests.post(url, data=payload)
data = response.json()
if response.status_code == 200:
print("Login successful after verification workflow.")
save_session(data)
return data["access_token"]

print("Failed to log in:", data)
return None
Main flow
if name == "main":
username = input("Enter your Robinhood username: ")
password = getpass.getpass("Enter your Robinhood password: ")
token = robinhood_login(username=username, password=password)
if token:
print("Access token:", token)
else:
print("Failed to log in.")

Verification workflow triggered. Workflow ID: 00e60f42-de4c-4282-ad7d-e9c01a0206e1
Handling verification workflow. Please approve the login request on your device.
Verification workflow initiated successfully. Waiting for approval...
Attempt 1: Checking approval status...
Device approval status: issued
Attempt 2: Checking approval status...
Device approval status: issued
Attempt 3: Checking approval status...
Device approval status: issued
Attempt 4: Checking approval status...
Device approval status: issued
Attempt 5: Checking approval status...
Device approval status: issued
Device approval timed out. Please ensure you approved the login request.
Failed to log in: {'verification_workflow': {'id': '00e60f42-de4c-4282-ad7d-e9c01a0206e1', 'workflow_status': 'workflow_status_internal_pending'}}
Failed to log in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants