fut
Version:
fifa 17 web-app api
468 lines (373 loc) • 15.4 kB
JavaScript
;
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
module.exports = function (options) {
var __ = require('underscore');
var request = require('request');
var hasher = require('./eaHasher');
var urls = require('./urls')();
var CookieJar = require('tough-cookie').CookieJar;
var utils = require('./utils');
const lodash = require('lodash');
var defaultRequest = null;
var loginDetails = {};
var loginResponse = {
nucleusId: null,
shardInfos: null,
shard: null,
persona: null,
sessionData: null,
apiRequest: null
};
var jar = request.jar();
var loginDefaults = {};
var Login = function Login() {};
Login.prototype.login = function (email, password, secret, platform, tfCodeCb, captchaCb, loginCb) {
let version = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 17;
Login.version = version;
if (!email || !__.isString(email) || email.trim().length <= 0) return loginCb(new Error('Email is empty.'));
if (!password || !__.isString(password) || password.trim().length <= 0) return loginCb(new Error('Password is empty.'));
if (!secret || !__.isString(secret) || email.trim().length <= 0) return loginCb(new Error('Secret is empty.'));
if (!platform || !__.isString(platform) || platform.trim().length <= 0) return loginCb(new Error('Platform is empty.'));
if (!getPlatform(platform)) return loginCb(new Error('Platform is invalid.'));
if (!__.isFunction(tfCodeCb)) return loginCb(new Error('tfCodeCb is not a function.'));
if (!__.isFunction(captchaCb)) return loginCb(new Error('captchaCb is not a function.'));
if (!__.isFunction(loginCb)) return loginCb(new Error('loginCb is not a function.'));
loginDetails = {
'email': email,
'password': password,
'secret': hasher(secret),
'tfCodeCb': tfCodeCb,
'loginCb': loginCb,
'gameSku': getGameSku(platform, version),
'platform': getPlatform(platform),
captchaCb: captchaCb
};
let requestConfigObj = {
jar: jar,
followAllRedirects: true,
gzip: true,
headers: {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36',
'Accept': 'text/html, application/xhtml+xml, */*',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'en-US,en;q=0.8',
'Connection': 'keep-alive',
'DNT': '1',
'Cache-Control': 'no-cache'
}
};
if (options.proxy) {
requestConfigObj.proxy = options.proxy;
}
defaultRequest = request.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
getMain();
};
Login.prototype.getCookieJarJSON = function () {
return jar._jar.serializeSync();
};
Login.prototype.setCookieJarJSON = function (json) {
jar._jar = CookieJar.deserializeSync(json);
};
Login.prototype.getLoginDefaults = function () {
return loginDefaults;
};
Login.prototype.getCaptcha = function (cb) {
const url = urls.login.getCaptcha + new Date().getTime();
defaultRequest.get(url, function (error, response, body) {
if (error) return cb(error);
if (response.statusCode !== 200) return cb(new Error(`Captcha error ${ response.statusCode } ${ body }`));
cb(body);
});
};
function getGameSku(platform, version) {
switch (platform) {
case 'pc':
return `FFA${ version }PCC`;
case 'ps3':
return `FFA${ version }PS3`;
case 'ps4':
return `FFA${ version }PS4`;
case 'x360':
return `FFA${ version }XBX`;
case 'xone':
return `FFA${ version }XBO`;
}
return null;
}
function getPlatform(platform) {
switch (platform) {
case 'pc':
return 'pc';
case 'ps3':
case 'ps4':
return 'ps3';
case 'x360':
case 'xone':
return '360';
}
return null;
}
function getMain() {
defaultRequest.get(urls.login.main, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>FIFA Football | Football Club | EA SPORTS</title>') > 0) return getNucleus();
if (body.indexOf('<title>FIFA Football | FUT Web App | EA SPORTS</title>') > 0) return getNucleus();
if (body.indexOf('<title>Log In</title>') > 0) return loginForm(response.request.href);
loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
});
}
function loginForm(url) {
defaultRequest.post(url, {
form: {
'email': loginDetails.email,
'password': loginDetails.password,
'_rememberMe': 'on',
'rememberMe': 'on',
'_eventId': 'submit',
'facebookAuth': ''
}
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>FIFA Football | FUT Web App | EA SPORTS</title>') > 0) return getNucleus();
if (body.indexOf('<title>Log In</title>') > 0) return loginDetails.loginCb(new Error('Unable to log in.'));
if (body.indexOf('<title>Set Up an App Authenticator</title>') > 0) return cancelLoginVerificationUpdate(response.request.href);
if (body.indexOf('<title>Account Update</title>') > 0) return acceptAccountUpdate(response.request.href);
if (body.indexOf('<title>Login Verification</title>') > 0) {
loginDetails.tfCodeCb().then(tfCode => sendTwoFactorCode(response.request.href, tfCode));
return;
}
loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
});
}
function sendTwoFactorCode(url, tfCode) {
defaultRequest.post(url, {
form: {
'twofactorCode': tfCode,
'twoFactorCode': tfCode,
'_eventId': 'submit',
'_trustThisDevice': 'on',
'trustThisDevice': 'on'
}
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>FIFA Football | FUT Web App | EA SPORTS</title>') > 0) return getNucleus();
if (body.indexOf('<title>Set Up an App Authenticator</title>') > 0) return cancelLoginVerificationUpdate(response.request.href);
if (body.indexOf('<title>Login Verification</title>') > 0) return loginDetails.loginCb(new Error('Wrong two factor code.'));
loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
});
}
function acceptAccountUpdate(url) {
defaultRequest.post(url, {
form: {
'_eventId': 'submit'
}
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>Login Verification</title>') > 0) return chooseEmailSending(response.request.href);
loginDetails.loginCb(new Error('Unknown response. Unable to login. At acceptAccountUpdate'));
});
}
function chooseEmailSending(url) {
defaultRequest.post(url, {
form: {
tfa_type: '',
twofactorType: 'EMAIL',
country: 0,
phoneNumber: '',
_eventId: 'submit',
appleDevice: 'IPHONE'
}
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>Login Verification</title>') > 0) {
loginDetails.tfCodeCb().then(tfCode => sendTwoFactorCode(response.request.href, tfCode));
return;
}
loginDetails.loginCb(new Error('Unknown response. Unable to login. At chooseEmailSending'));
});
}
function cancelLoginVerificationUpdate(url) {
defaultRequest.post(url, {
form: {
'_eventId': 'cancel',
'appDevice': 'IPHONE'
}
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (body.indexOf('<title>FIFA Football | FUT Web App | EA SPORTS</title>') > 0) return getNucleus();
loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
});
}
function getNucleus() {
defaultRequest.get(urls.login.nucleus, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
var match = body.match(/EASW_ID\W*=\W*'(\d*)'/);
if (match === null || match[1] === null) return loginDetails.loginCb(new Error("Unable to get the 'EASW_ID'. Unable to login."));
loginResponse.nucleusId = match[1];
getShards();
});
}
function getShards() {
let requestConfigObj = {
json: true,
headers: {
'Easw-Session-Data-Nucleus-Id': loginResponse.nucleusId,
'X-UT-Embed-Error': true,
'X-UT-Route': 'https://utas.external.fut.ea.com',
'X-Requested-With': 'XMLHttpRequest',
'Referer': urls.referer
}
};
defaultRequest = defaultRequest.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
defaultRequest.get(urls.login.shards, function (error, responsse, body) {
if (error) return loginDetails.loginCb(error);
if (!body || !body.shardInfo) return loginDetails.loginCb(new Error('Unable to get shards. Unable to login.'));
loginResponse.shardInfos = body.shardInfo;
getAccount();
});
}
function getAccount() {
loginResponse.shard = __.find(loginResponse.shardInfos, function (si) {
return si.skus.indexOf(loginDetails.gameSku) >= 0;
});
if (!loginResponse.shard) return loginDetails.loginCb(new Error('Unable to find shardInfo.'));
let requestConfigObj = {
headers: {
'X-UT-Route': 'https://' + loginResponse.shard.clientFacingIpPort
}
};
defaultRequest = defaultRequest.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
defaultRequest.get(urls.login.accounts, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (!body.userAccountInfo) return loginDetails.loginCb(new Error('Unable to get account infos.'));
loginResponse.persona = __.find(body.userAccountInfo.personas, function (persona) {
return __.some(persona.userClubList, function (userClub) {
return userClub.platform === loginDetails.platform;
});
});
if (!loginResponse.persona) return loginDetails.loginCb(new Error('Unable to get account info persona.'));
getSession();
});
}
function getSession() {
var data = {
'isReadOnly': false,
'sku': `FUT${ Login.version }WEB`,
'clientVersion': 1,
'nucleusPersonaId': loginResponse.persona.personaId,
'nucleusPersonaDisplayName': loginResponse.persona.personaName,
'gameSku': loginDetails.gameSku,
'nucleusPersonaPlatform': getPlatform(loginDetails.platform),
'locale': 'en-GB',
'method': 'authcode',
'priorityLevel': 4,
'identification': { 'authCode': '' }
};
defaultRequest.post(urls.login.session, { body: data }, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (response.statusCode !== 200) return loginDetails.loginCb(new Error(`Unknown response. Unable to login. ${ response.statusCode } ${ (0, _stringify2.default)(body) }`));
loginResponse.sessionData = body;
if (loginResponse.sessionData.sid) return phishing();
loginDetails.loginCb(new Error(`Unknown response. Unable to login. ${ response.statusCode } ${ (0, _stringify2.default)(body) }`));
});
}
function phishing() {
let requestConfigObj = {
headers: {
'X-UT-SID': loginResponse.sessionData.sid
}
};
defaultRequest = defaultRequest.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
defaultRequest.get(urls.login.question, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (utils.isApiMessage(body) && body.token) {
loginResponse.token = body.token;
let requestConfigObj = {
baseUrl: 'https://' + loginResponse.sessionData.ipPort.split(':')[0],
headers: {
'X-UT-PHISHING-TOKEN': loginResponse.token,
'X-HTTP-Method-Override': 'GET',
'X-UT-Route': 'https://' + loginResponse.sessionData.ipPort.split(':')[0],
'x-flash-version': '20,0,0,272'
}
};
loginResponse.apiRequest = defaultRequest.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
return loginDetails.loginCb(null, loginResponse);
}
if (body.question) return validate();else if (body.code === '459') {
// validate captcha
// then do the phishing() again
getCaptcha((err, rawCaptcha) => {
if (err) loginDetails.loginCb(err);
loginDetails.captchaCb(rawCaptcha, captcha => {
validateCaptcha(captcha, err => {
if (err) loginDetails.loginCb(err);
return phishing();
});
});
});
} else {
let error = new Error(`Unknown response. Unable to login. response: ${ (0, _stringify2.default)(body) }`);
error.futLoginError = body;
loginDetails.loginCb(error);
}
});
}
function getCaptcha(cb) {
const url = urls.login.captchaImg + new Date().getTime();
defaultRequest.get({
url: url,
encoding: null
}, function (error, response, body) {
if (error) return cb(error);
if (response.statusCode !== 200) return cb(new Error(`Captcha error ${ response.statusCode } ${ body }`));
cb(null, body);
});
}
function validateCaptcha(captcha, cb) {
const url = urls.login.validateCaptcha;
defaultRequest.post(url, {
json: true,
body: {
'token': 'AAAA',
'answer': captcha
}
}, (error, response, body) => {
if (error) return cb(error);
if (response.statusCode !== 200) return cb(new Error(`Captcha is not ok ${ response.statusCode } ${ body }`));
cb();
});
}
function validate() {
defaultRequest.post(urls.login.validate, {
form: { answer: loginDetails.secret }
}, function (error, response, body) {
if (error) return loginDetails.loginCb(error);
if (!body) return loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
if (body.string !== 'OK') return loginDetails.loginCb(new Error('Wrong secret. Unable to login.'));
if (body.string === 'OK') loginResponse.token = body.token;
if (!loginResponse.token) return loginDetails.loginCb(new Error('Unknown response. Unable to login.'));
let requestConfigObj = {
baseUrl: 'https://' + loginResponse.sessionData.ipPort.split(':')[0],
headers: {
'X-UT-PHISHING-TOKEN': loginResponse.token,
'X-HTTP-Method-Override': 'GET',
'X-UT-Route': 'https://' + loginResponse.sessionData.ipPort.split(':')[0],
'x-flash-version': '20,0,0,272',
'Accept': 'application/json'
}
};
loginResponse.apiRequest = defaultRequest.defaults(requestConfigObj);
lodash.merge(loginDefaults, requestConfigObj);
loginDetails.loginCb(null, loginResponse);
});
}
return new Login();
};