Open doctorcolossus opened 5 years ago
I did double-check my credentials.
I found a similar issue on Jamonek's repo. aamazie claims that he has resolved this in his fork. I believe this is the relevant commit. I don't understand the code at first glance. Maybe this information will help you. If not, I will try to work on it when I can find some more free time.
i will work on it during the weekend. Thanks for reporting this
hi, the current master now supports the mfa use case : https://github.com/wang-ye/robinhood-crypto/commit/bc6daecd6127625e7d0b3544d461219e0c54fcab
Hi wang-ye, I had been busy with school and just got a chance to test this.
It is possible that something else changed since you fixed this, or maybe I am overlooking something about how to use MFA authentication.
However, I'm still getting the same error as before with your latest code:
400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 180, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 51, in function_reauth
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 41, in function_reauth
res = f(*args, **kwargs)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 133, in session_request
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 180, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 51, in function_reauth
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 41, in function_reauth
res = f(*args, **kwargs)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 133, in session_request
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./robinhood-csv.py", line 28, in <module>
r = RobinhoodCrypto('**************', '**************')
File "robinhood_crypto_api/robinhood_crypto_api.py", line 99, in __init__
_access_token = self.get_access_token(self.username, self.password)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 187, in get_access_token
raise LoginException()
robinhood_crypto_api.robinhood_crypto_api.LoginException
I just checked and verified that self.GenerateDeviceToken() is indeed running and returning a device_token to self.device_token. It looks legit - it's a dash-separated hex value with digits {8}-{4}-{4}-{4}-{12}. Nevertheless, I am still getting the LoginException.
Okay, I just noticed I got a bunch of SMS verification codes from Robinhood - so I think that's the issue. You need to handle the challenge somehow. See lines 166-180 in aamazie's fork of Jamonek's repo.
Did you type in the verification codes in the terminal? https://github.com/wang-ye/robinhood-crypto/commit/bc6daecd6127625e7d0b3544d461219e0c54fcab#diff-7f3fc7d4172fcd78eb5c5dc07ac78047R181
I am never prompted for a verification code.
Using the latest code, I get an error due to self.device_token not being initialized.
>>> from robinhood_crypto_api import RobinhoodCrypto
>>> r = RobinhoodCrypto('**************', '**************')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 98, in __init__
_access_token = self.get_access_token(self.username, self.password)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 163, in get_access_token
if not self.device_token:
AttributeError: 'RobinhoodCrypto' object has no attribute 'device_token'
If I add self.device_token = None
between lines 94 & 95, it gets a little further, but then I receive the LoginException before being prompted for any verification code:
>>> from robinhood_crypto_api import RobinhoodCrypto
>>> r = RobinhoodCrypto('**************', '**************')
400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 180, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 51, in function_reauth
raise e
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 41, in function_reauth
res = f(*args, **kwargs)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 133, in session_request
raise e
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 180, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 51, in function_reauth
raise e
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 41, in function_reauth
res = f(*args, **kwargs)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 133, in session_request
raise e
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 129, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 99, in __init__
_access_token = self.get_access_token(self.username, self.password)
File "robinhood-crypto/robinhood_crypto_api/robinhood_crypto_api.py", line 187, in get_access_token
raise LoginException()
robinhood_crypto_api.robinhood_crypto_api.LoginException
On my local run I got the MFA prompt. Ideally, if given the correct parameters, the api call at line 180 should not throw an exception. Could you print out the payload parameters and try to simulate the same request using postman?
Hmm, interesting. I installed Postman, but I've never used it before. I used to use the Live HTTP Headers Firefox plugin, so I assume it's something similar to that. I'm pretty busy with schoolwork right now, but I'll try to dig deeper as soon as I can find and I'll let you know if I can find any clues about what could be causing this problem.
Sorry for the delay! I have been busy and was also procrastinating learning Postman. I decided to just be lazy and use the logging package. Here is the full output with httplib and requests debugging. I did receive the SMS code right after making this request.
DEBUG:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): api.robinhood.com
send: b'CONNECT api.robinhood.com:443 HTTP/1.0\r\n'
send: b'\r\n'
send: b'POST /oauth2/token/ HTTP/1.1\r\nHost: api.robinhood.com\r\naccept-encoding: gzip, deflate\r\naccept-language: en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5\r\ncontent-type: application/json\r\nconnection: keep-alive\r\norigin: https://robinhood.com\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\r\naccept: */*\r\nContent-Length: 268\r\n\r\n'
send: b'{"password": "**************", "username": "**************", "grant_type": "password", "scope": "internal", "client_id": "****************************************", "expires_in": 86400, "device_token": "********-****-****-****-************", "challenge_type": "sms"}'
reply: 'HTTP/1.1 400 Bad Request\r\n'
header: Date: Wed, 31 Jul 2019 05:08:14 GMT
header: Content-Type: application/json
header: Content-Length: 300
header: Connection: keep-alive
header: Server: openresty
header: Allow: POST, OPTIONS
header: X-Robinhood-API-Version: 0.0.0
header: Content-Security-Policy: default-src 'none'
header: X-Frame-Options: SAMEORIGIN
header: x-content-type-options: nosniff
header: x-xss-protection: 1; mode=block
header: Access-Control-Allow-Origin: https://robinhood.com
header: Vary: Origin
DEBUG:requests.packages.urllib3.connectionpool:https://api.robinhood.com:443 "POST /oauth2/token/ HTTP/1.1" 400 300
DEBUG:robinhood_crypto_api.robinhood_crypto_api:Error in session request calls. Request body b'{"password": "**************", "username": "**************", "grant_type": "password", "scope": "internal", "client_id": "****************************************", "expires_in": 86400, "device_token": "********-****-****-****-************", "challenge_type": "sms"}', headers {'accept-encoding': 'gzip, deflate', 'accept-language': 'en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5', 'content-type': 'application/json', 'connection': 'keep-alive', 'origin': 'https://robinhood.com', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36', 'accept': '*/*', 'Content-Length': '268'}, content b'{"detail":"Request blocked, challenge issued.","challenge":{"id":"********-****-****-****-************","user":"********-****-****-****-************","type":"sms","alternate_type":"email","status":"issued","remaining_retries":3,"remaining_attempts":3,"expires_at":"2019-07-31T01:13:14.631101-04:00"}}'
ERROR:robinhood_crypto_api.robinhood_crypto_api:400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 141, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
ERROR:robinhood_crypto_api.robinhood_crypto_api:400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 192, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 63, in function_reauth
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 53, in function_reauth
res = f(*args, **kwargs)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 145, in session_request
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 141, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
Traceback (most recent call last):
File "robinhood_crypto_api/robinhood_crypto_api.py", line 192, in get_access_token
data = self.session_request(RobinhoodCrypto.ENDPOINTS['auth'], json_payload=payload, timeout=5, method='post', request_session=auth_session)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 63, in function_reauth
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 53, in function_reauth
res = f(*args, **kwargs)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 145, in session_request
raise e
File "robinhood_crypto_api/robinhood_crypto_api.py", line 141, in session_request
resp.raise_for_status()
File "/usr/lib/python3.7/site-packages/requests/models.py", line 909, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.robinhood.com/oauth2/token/
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "robinhood_crypto_api/robinhood_crypto_api.py", line 111, in __init__
_access_token = self.get_access_token(self.username, self.password)
File "robinhood_crypto_api/robinhood_crypto_api.py", line 199, in get_access_token
raise LoginException()
robinhood_crypto_api.robinhood_crypto_api.LoginException
Below is a comparison between the failed authentication request made by robinhood-crypto with the successful one made by aamazie's Robinhood fork. Both requests resulted in receiving an SMS authentication code. The key differences I have been able to spot are the following:
robinhood-crypto:
POST /oauth2/token/ HTTP/1.1
Host: api.robinhood.com
accept-encoding: gzip, deflate
accept-language: en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5
content-type: application/json
connection: keep-alive
origin: https://robinhood.com
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
accept: */*
Content-Length: 268
{"password": "**************",
"username": "**************",
"grant_type": "password",
"scope": "internal",
"client_id": "****************************************",
"expires_in": 86400,
"device_token":
"********-****-****-****-************", "challenge_type": "sms"}
aamazie/Robinhood:
POST /oauth2/token/ HTTP/1.1
Host: api.robinhood.com
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en;q=1, fr;q=0.9, de;q=0.8, ja;q=0.7, nl;q=0.6, it;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=utf-8
X-Robinhood-API-Version: 1.0.0
Connection: keep-alive
User-Agent: Robinhood/823 (iPhone; iOS 7.1.2; Scale/2.00)
Content-Length: 221
password=**************
username=**************
grant_type=password
client_id=****************************************
expires_in=86400
scope=internal
device_token=********-****-****-****-************
challenge_type=sms
A-ha! I think I've figured it out. I just noticed that aamazie/Robinhood's request is also met with an HTTP/1.1 400 Bad Request
response! He's using a:
try:
res = self.session.post(endpoints.login(), data=payload, timeout=15)
...
res2 = self.session.post(sms_challenge_endpoint, data=challenge_res, timeout=15)
res2.raise_for_status()
res3 = self.session.post(endpoints.login(), data=payload, timeout=15)
res3.raise_for_status()
except requests.exceptions.HTTPError:
raise RH_exception.LoginFailed()
You're doing:
try:
resp = session.request(method, url, json=json_payload, timeout=timeout)
resp.raise_for_status()
except Exception as e:
...
raise e
In other words, we should expect a "bad request" response the first time, when the challenge is issued. Then we need to set the X-ROBINHOOD-CHALLENGE-RESPONSE-ID
header, respond to the challenge, and finally attempt a second login. Because you are calling resp.raise_for_status()
for every request, it's causing the error to be raised and the program to terminate before the user can be prompted to provide the SMS code. You also need to add an endpoint for the challenge, send it a POST request after the initial failed login, and finally make the second login request.
Hey doctorcolossus/wang-ye, did you ever find a solution to the SMS issue?
I am having the same issue with the LoginException, where I am not prompted to input my MFA prompt. I still get the SMS message from robinhood, but I get the login error at line 187 before I can input the code.
Thanks
I described what needs to be changed in my last comment - handle or ignore the first one (or two? iiirc) HTTP "400 Bad Request" responses. I am not sure when I'll have time to work on a fix, as I'm traveling now and busy with another project, then starting a new semester at school in a couple of weeks. I'm hoping wang-ye will find time, but if he doesn't and I find some time during next semester, I will have a go at it and report back here.
I had the same problem with the login exception. I was getting the SMS from Robinhood and everything you guys are describing. But still, I wasn't prompt to input my MFA. What I figure was that the Robinhood Two-Factor Authentication was off in my setting, even though I was getting SMS. When I turned that setting on in Robinhood the login started working and asking for the MFA. It is counter-intuitive because I was getting the SMS from Robinhood. I hope that helps some of you guys.
As of 2019/5/3 15:25 (UTC +2), I have been getting the following robinhood_crypto_api.robinhood_crypto_api.LoginException:
Had been busy, unfortunately didn't check my script and notice till today.
Any ideas?