Skip to content

Commit

Permalink
Added protected login when initial phase gets a 403 (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
starkevin authored and nicoschmitt committed Jul 8, 2017
1 parent 17d19f5 commit c1f5be3
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 20 deletions.
120 changes: 102 additions & 18 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,68 @@
const querystring = require('querystring');
const url = require('url');
const CryptoJS = require("crypto-js");
const crypto = require('crypto');
const request = require('request');
const nodeRSA = require("node-rsa");
const ursa = require("ursa");

var AUTH_URL = 'https://android.clients.google.com/auth';

var USER_AGENT = 'Dalvik/2.1.0 (Linux; U; Android 5.1.1; Andromax I56D2G Build/LMY47V';

var oauthUtil = {};
oauthUtil.parseKeyValues = function (body) {
/**
* Parse the values
* @param {*} body
*/
function parseKeyValues(body) {
var obj = {};
body.split("\n").forEach(function (line) {
var pos = line.indexOf("=");
if (pos > 0) obj[line.substr(0, pos)] = line.substr(pos + 1);
});
return obj;
};
oauthUtil.Base64 = {
_map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
stringify: CryptoJS.enc.Base64.stringify,
parse: CryptoJS.enc.Base64.parse
};
oauthUtil.salt = function (len) {
return Array.apply(0, Array(len)).map(function () {
return (function (charset) {
return charset.charAt(Math.floor(Math.random() * charset.length));
}('abcdefghijklmnopqrstuvwxyz0123456789'));
}).join('');
};

/**
* Generates a signature to be passed into GPS
*/
function generateSignature(email, password) {
let googleDefaultPublicKey = 'AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pKRI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/6rmf5AAAAAwEAAQ==';
let keyBuffer = Buffer.from(googleDefaultPublicKey, 'base64');

let sha = crypto.createHash('sha1');
sha.update(keyBuffer);

let hash = sha.digest().slice(0, 4);

let modLength = keyBuffer.readUInt32BE(0);
let mod = keyBuffer.slice(4, 4 + modLength);

let expLength = keyBuffer.readUInt32BE(4 + modLength);
let exp = keyBuffer.slice(8 + modLength, 8 + modLength + expLength);

let pem = ursa
.createPublicKeyFromComponents(mod, exp)
.toPublicPem()
.toString();

let rsa = new nodeRSA(pem);
let encrypted = rsa.encrypt(email + '\x00' + password);

let base64Output = Buffer.concat([
Buffer.from([0]),
hash,
encrypted
]).toString('base64');

base64Output = base64Output.replace(/\+/g, '-');
base64Output = base64Output.replace(/\//g, '_');

return base64Output;
}

/**
* New instance
*/
var GoogleOauth = function() {
this.request = request.defaults({
headers: {
Expand All @@ -39,12 +72,18 @@ var GoogleOauth = function() {
});
};

/**
* Holds the proxy for this session
*/
GoogleOauth.prototype.setProxy = function(proxy) {
this.request = this.request.defaults({
proxy: proxy,
});
};

/**
* OAuth against your application
*/
GoogleOauth.prototype.oauth = function (email, master_token, android_id, service, app, client_sig, callback) {
var data = {
accountType: "HOSTED_OR_GOOGLE",
Expand All @@ -66,11 +105,16 @@ GoogleOauth.prototype.oauth = function (email, master_token, android_id, service
url: AUTH_URL,
form: data,
}, function(err, response, body) {
callback(err, err ? null : oauthUtil.parseKeyValues(body));
callback(err, err ? null : parseKeyValues(body));
});
};

/**
* Logs the user in. If it fails, falls back to a secure login
*/
GoogleOauth.prototype.login = function (email, password, android_id, callback) {
var _this = this;

var data = {
accountType: "HOSTED_OR_GOOGLE",
Email: email.trim(),
Expand All @@ -90,9 +134,49 @@ GoogleOauth.prototype.login = function (email, password, android_id, callback) {
url: AUTH_URL,
form: data,
}, function(err, response, body) {
let content = oauthUtil.parseKeyValues(body);
callback(err, err ? null : {androidId: android_id, masterToken: content.Token});

if(err || body.indexOf("Error") > -1) {
_this.loginForProtected(email, password, android_id, function(err, response) {
callback(err, response);
})
} else {
const content = parseKeyValues(body);
callback(err, {androidId: android_id, masterToken: content.Token});
}
});
};

/**
* Secure login
*/
GoogleOauth.prototype.loginForProtected = function(username, password, android_id, callback) {
var data = {
"Email": "[email protected]",
"EncryptedPasswd": generateSignature(username, password),
"accountType": "HOSTED_OR_GOOGLE",
"add_account": "1",
"androidId": android_id,
"device_country": "us",
"has_permission": "1",
"lang": "en",
"operatorCountry": "us",
"sdk_version": "17",
"service": "ac2dm",
"source": "android"
};

this.request.post({
url: AUTH_URL,
form: data,
}, function(err, response, body) {

if(err) {
callback(err);
} else {
const content = parseKeyValues(body);
callback(err, {androidId: android_id, masterToken: content.Token});
}
});
}

module.exports = exports = GoogleOauth;
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
},
"homepage": "https://github.com/pogosandbox/gpsoauth#readme",
"dependencies": {
"crypto-js": "^3.1.9-1",
"request": "^2.81.0"
"node-rsa": "^0.4.2",
"request": "^2.81.0",
"ursa": "^0.9.4"
}
}

0 comments on commit c1f5be3

Please sign in to comment.