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

LoginException: 400 Client Error: Bad Request for https://api.robinhood.com/oauth2/token/ #7

Open
doctorcolossus opened this issue May 30, 2019 · 17 comments

Comments

@doctorcolossus
Copy link
Contributor

doctorcolossus commented May 30, 2019

As of 2019/5/3 15:25 (UTC +2), I have been getting the following robinhood_crypto_api.robinhood_crypto_api.LoginException:

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 133, 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 157, 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 50, in function_reauth
    raise e
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 40, in function_reauth
    res = f(*args, **kwargs)
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 137, in session_request
    raise e
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 133, 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 157, 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 50, in function_reauth
    raise e
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 40, in function_reauth
    res = f(*args, **kwargs)
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 137, in session_request
    raise e
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 133, 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.py", line 39, in <module>
    r = RobinhoodCrypto('**************', '****************')
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 103, in __init__
    _access_token = self.get_access_token(self.username, self.password)
  File "robinhood_crypto_api/robinhood_crypto_api.py", line 162, in get_access_token
    raise LoginException()
robinhood_crypto_api.robinhood_crypto_api.LoginException

Had been busy, unfortunately didn't check my script and notice till today.

Any ideas?

@doctorcolossus
Copy link
Contributor Author

I did double-check my credentials.

@doctorcolossus
Copy link
Contributor Author

doctorcolossus commented May 31, 2019

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.

@wang-ye
Copy link
Owner

wang-ye commented Jun 8, 2019

i will work on it during the weekend. Thanks for reporting this

@wang-ye
Copy link
Owner

wang-ye commented Jun 10, 2019

hi, the current master now supports the mfa use case : bc6daec

@wang-ye wang-ye closed this as completed Jun 10, 2019
@doctorcolossus
Copy link
Contributor Author

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

@doctorcolossus
Copy link
Contributor Author

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.

@doctorcolossus
Copy link
Contributor Author

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.

@wang-ye
Copy link
Owner

wang-ye commented Jul 8, 2019

Did you type in the verification codes in the terminal? bc6daec#diff-7f3fc7d4172fcd78eb5c5dc07ac78047R181

@doctorcolossus
Copy link
Contributor Author

doctorcolossus commented Jul 8, 2019

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

@wang-ye
Copy link
Owner

wang-ye commented Jul 8, 2019

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?

@wang-ye wang-ye reopened this Jul 8, 2019
@doctorcolossus
Copy link
Contributor Author

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.

@doctorcolossus
Copy link
Contributor Author

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

@doctorcolossus
Copy link
Contributor Author

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:

  • Content-Type
    • robinhood-crypto:
      • application/json
    • aamazie/Robinhood:
      • application/x-www-form-urlencoded; charset=utf-8
  • User-Agent:
    • robinhood-crypto:
      • 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
    • aamazie/Robinhood:
      • Robinhood/823 (iPhone; iOS 7.1.2; Scale/2.00)
  • X-Robinhood-API-Version:
    • robinhood-crypto:
      • {missing!}
    • aamazie/Robinhood:
      • 1.0.0

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

@doctorcolossus
Copy link
Contributor Author

doctorcolossus commented Jul 31, 2019

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.

@Dalton2264
Copy link

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

@doctorcolossus
Copy link
Contributor Author

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.

@CodeMasterFelipe
Copy link

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.

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