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

Login Issue #7

Open
firstlast912 opened this issue May 6, 2019 · 5 comments
Open

Login Issue #7

firstlast912 opened this issue May 6, 2019 · 5 comments

Comments

@firstlast912
Copy link

Hi,

I had been using your code to trade until recently. Does your login still work? I have modified the code for the latest changes but I get an error. Could you please check the following code and see where it is going wrong?

Thanks

var deviceToken = generateDeviceToken();
var robinhoodApiBaseUrl = 'https://api.robinhood.com';

function generateDeviceToken(){
rands = []

for(i=0; i<16; i++){
r = Math.random()
rand = 4294967296 * r
rands.push((parseInt(rand) >> ((3 & i) << 3)) & 255)
}

hexa = []
for(i=0; i<256; ++i){
hexa.push((parseInt(i) + 256).toString(16).substring(1));
}

id = ""
for(i=0; i<16; i++){
id += hexa[rands[i]]
if ((i == 3) || (i == 5) || (i == 7) || (i == 9)){
id += "-"
}
}
Logger.log(id);
return id;
}

function getClassicToken() {
//Trying to login with the new device id for the first time. This is where I get a prompt to select sms or email. This part works.
var url = robinhoodApiBaseUrl + '/oauth2/token/';
var payload = {
'username': robinhoodUsername,
'password': robinhoodPassword,
'device_token': deviceToken,
'expires_in': 86400,
'grant_type': 'password',
'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
'scope': 'internal'
};
var options = {
'method': 'post',
'payload': payload,
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);

Utilities.sleep(1000);

//This portion says I would like to receive the code by email. I am able to get the challenge id in resonse. This works too
var payload = {
'challenge_type': 'email',
'username': robinhoodUsername,
'password': robinhoodPassword,
'device_token': deviceToken,
'expires_in': 86400,
'grant_type': 'password',
'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
'scope': 'internal'
};
var options = {
'method': 'post',
'payload': payload,
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
var responseJson = JSON.parse(response.getContentText());
var id = responseJson["challenge"]["id"];
Logger.log('Challenge ID: ' + id);

Utilities.sleep(20000);

//This portion checks my email and gets extracts the code. This works too
var threads = GmailApp.search("subject:Your Email Verification Code");
Logger.log(threads.length);
threads = threads[threads.length-1];
var message = threads.getMessages();
Logger.log(message.length);
message = message[message.length-1];
var body = message.getPlainBody();
var ind = body.indexOf("you.");
var code = body.substring(ind+8, ind+14);
Logger.log(code);
threads.moveToTrash();

Utilities.sleep(10000);

//This portion posts the challenge id and I get the validated response. I also get an email saying that I have logged in from another device with the IP but the browser says Other and location United States
url = robinhoodApiBaseUrl + '/challenge/' + id + '/respond/';
var payload = {
'response': code
};
var options = {
'method': 'post',
'payload': payload,
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);

Utilities.sleep(10000);

//When I post the device id along with the challenge id, I do not get the token, instead I am getting Server Error 500
var url = robinhoodApiBaseUrl + '/oauth2/token/';
var payload = {
'challenge_type': 'email',
'username': robinhoodUsername,
'password': robinhoodPassword,
'device_token': deviceToken,
'expires_in': 86400,
'grant_type': 'password',
'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
'scope': 'internal'
};

var options = {
'method': 'post',
'payload': payload,
'muteHttpExceptions': true,
'headers': {
'X-ROBINHOOD-CHALLENGE-RESPONSE-ID': id
}
};
Logger.log('Device Token: ' + deviceToken);
Logger.log('URL: ' + url);
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
var responseJson = JSON.parse(response.getContentText());
var classicToken = responseJson.access_token;
Logger.log('classic token: ' + classicToken);

return classicToken;
}

@mcmire
Copy link

mcmire commented Jun 12, 2019

Yeah the login code doesn't quite work anymore because Robinhood changed some stuff under the hood (presumably to prevent so many people from using the API — haha).

You'll need to run some commands from the command line to get this to work properly. Make sure you have curl installed on your computer.

  1. Run this command, filling in your username and password:

    curl -v -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{"username": "<your username>", "password": "<your password>", "grant_type": "password", "scope": "internal", "client_id": "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS", "expires_in": 86400, "device_token": "ec79ec78-a0e4-456a-9897-9a58ab8c6bee", "challenge_type": "sms"}' https://api.robinhood.com/oauth2/token/
    
  2. UPDATE: If you get a response that contains an access_token, then you're good to go, skip down to step 5.

  3. At the bottom of the output there will be a JSON response, which contains a challenge id. You'll also be sent an SMS message with a 6-digit number in it. Once you get this message, run:

    curl -v -X POST -H "Accept: application/json" -H "Content-Type: application/json" -d '{"response": "<number in SMS message>"}' https://api.robinhood.com/challenge/<challenge id>/respond/
    
  4. Check the status in the response — if it's "validated" you're good to go. Now you can issue the first request again, only using the challenge id:

    curl -v -X POST -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Robinhood-Challenge-Response-ID: <challenge id>" -d '{"username": "<your username>", "password": "<your password>", "grant_type": "password", "scope": "internal", "client_id": "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS", "expires_in": 86400, "device_token": "ec79ec78-a0e4-456a-9897-9a58ab8c6bee", "challenge_type": "sms"}' https://api.robinhood.com/oauth2/token/
    
  5. This spits out a response that contains an access token. Now add

    var accessToken = "<the access token>"

    at the top of robinhood.gs underneath var robinhoodPassword, then modify line 76 so that instead of

    'Authorization': 'Bearer ' + getOAuthToken_()

    it's just

    'Authorization': 'Bearer ' + accessToken

    And you should be good to go.

Note that you will need to change this access token every day (86400 seconds). You could probably raise this number in the request, but I haven't tried that yet.

There's a lot more information in this issue here: robinhood-unofficial/pyrh#176

@mcmire
Copy link

mcmire commented Dec 7, 2019

Also, if you're trying to use the ROBINHOOD_GET_ORDERS function, it doesn't seem to be working, but if you replace this:

function ROBINHOOD_GET_ORDERS(datetime) {
  return getRobinhoodData_('orders', ['instrument', 'position']);
}

with this:

function ROBINHOOD_GET_ORDERS(datetime) {
  return getRobinhoodData_('orders', ['instrument']);
}

then everything should be fine and it should give you what you need.

@rghuckins
Copy link
Owner

@mcmire You seem well-versed in Robinhood's recent API changes. If you have a working version, would you be interested in opening a PR updating the apps script code and/or readme (detailing preliminary auth steps)? No worries if not, of course!

@mcmire
Copy link

mcmire commented Feb 8, 2020

@rghuckins Yeah, and in fact, a while back I tried to take your code and upgrade it to ES6 syntax and modernize it a bit, as I wanted to be able to write tests against it. I ran into some issues, so I paused on that effort, but I did learn some things along the way. I'll take a look at this closer again and see if I can submit some changes that would fix this.

@rghuckins
Copy link
Owner

Awesome, much appreciated @mcmire!

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

3 participants