diff --git a/src/index.js b/src/index.js index 56c4a4a..829b51a 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,6 @@ import Promise from 'bluebird' import assert from 'assert' import utils from './lib/utils' -import Login from './lib/login' import MobileLogin from './lib/mobile-login' import _ from 'underscore' import Methods from './lib/methods' @@ -55,13 +54,7 @@ let Fut = class Fut extends Methods { this.isReady = false // instance will be ready after we called _init func Object.assign(this.options, defaultOptions, options) - if (this.options.loginType === 'web') { - this.loginLib = Promise.promisifyAll(new Login({proxy: options.proxy})) - } else if (this.options.loginType === 'mobile') { - this.loginLib = new MobileLogin({...options, tfCodeHandler: options.tfAuthHandler}) - } else { - throw new Error(`Unknown loginType ${this.options.loginType}`) - } + this.loginLib = new MobileLogin({...options, tfCodeHandler: options.tfAuthHandler}) } async loadVariable (key) { @@ -86,15 +79,13 @@ let Fut = class Fut extends Methods { async login () { await this._init() - const loginMethod = this.options.loginType === 'web' ? 'loginAsync' : 'login' + const loginMethod = 'login' const loginResponse = await this.loginLib[loginMethod](this.options.email, this.options.password, this.options.secret, this.options.platform, this.options.tfAuthHandler, this.options.captchaHandler) - await this.saveVariable('cookie', this.loginLib.getCookieJarJSON()) this.rawApi = loginResponse.apiRequest const loginDefaults = _.omit(this.loginLib.getLoginDefaults(), 'jar') await this.saveVariable('loginDefaults', loginDefaults) - if (this.options.loginType === 'web') this.rawApi = Promise.promisify(this.rawApi, this) this.isReady = true const user = await this.getUser() if (user.userInfo.feature.trade === 0) { diff --git a/src/lib/login.js b/src/lib/login.js deleted file mode 100644 index 5a6fe8e..0000000 --- a/src/lib/login.js +++ /dev/null @@ -1,490 +0,0 @@ -'use strict' - -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.prototype.login = function (email, password, secret, platform, tfCodeCb, captchaCb, loginCb, version = 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_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 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, { maxRedirects: 20 }, function (error, response, body) { - if (error) return loginDetails.loginCb(error) - - if (body.indexOf('FIFA Football | Football Club | EA SPORTS') > 0) return getNucleus() - - if (body.indexOf('FIFA Football | FUT Web App | EA SPORTS') > 0) return getNucleus() - - if (body.indexOf('Log In') > 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, - 'country': 'US', // is it important? - 'phoneNumber': '', // TODO: add phone code verification - 'passwordForPhone': '', - 'gCaptchaResponse': '', - 'isPhoneNumberLogin': 'false', // TODO: add phone login - 'isIncompletePhone': '', - '_rememberMe': 'on', - 'rememberMe': 'on', - '_eventId': 'submit' - } - }, function (error, response, body) { - if (error) return loginDetails.loginCb(error) - - if (body.indexOf('var redirectUri') > 0) return handleRedirect(body) - - if (body.indexOf('FIFA Football | FUT Web App | EA SPORTS') > 0) return getNucleus() - - if (body.indexOf('Log In') > 0) return loginDetails.loginCb(new Error('Unable to log in.')) - - if (body.indexOf('Set Up an App Authenticator') > 0) return cancelLoginVerificationUpdate(response.request.href) - - if (body.indexOf('Account Update') > 0) return acceptAccountUpdate(response.request.href) - - if (body.indexOf('Login Verification') > 0) { - loginDetails.tfCodeCb().then((tfCode) => sendTwoFactorCode(response.request.href, tfCode)) - return - } - loginDetails.loginCb(new Error(JSON.stringify(response, null, 2))) - }) - } - - function handleRedirect (body) { - var url = body.match(/var redirectUri = '(https?:\/\/.+\/p\/web[0-9]+\/login\?execution=.+?)'/) - defaultRequest.get(url[1] + '&_eventId=end', function (error, response, body) { - if (error) return loginDetails.loginCb(error) - - if (body.indexOf('FIFA Football | FUT Web App | EA SPORTS') > 0) return getNucleus() - - if (body.indexOf('Log In') > 0) return loginDetails.loginCb(new Error('Unable to log in.')) - - if (body.indexOf('Set Up an App Authenticator') > 0) return cancelLoginVerificationUpdate(response.request.href) - - if (body.indexOf('Account Update') > 0) return acceptAccountUpdate(response.request.href) - - if (body.indexOf('Login Verification') > 0) { - loginDetails.tfCodeCb().then((tfCode) => sendTwoFactorCode(response.request.href, tfCode)) - return - } - loginDetails.loginCb(new Error(JSON.stringify(response, null, 2))) - }) - } - - 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('FIFA Football | FUT Web App | EA SPORTS') > 0) return getNucleus() - - if (body.indexOf('Set Up an App Authenticator') > 0) return cancelLoginVerificationUpdate(response.request.href) - - if (body.indexOf('Login Verification') > 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('Login Verification') > 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('Login Verification') > 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('FIFA Football | FUT Web App | EA SPORTS') > 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, qs: { 'sku_a': `F${Login.version}` } }, 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} ${JSON.stringify(body)}`)) - - loginResponse.sessionData = body - - if (loginResponse.sessionData.sid) return phishing() - - loginDetails.loginCb(new Error(`Unknown response. Unable to login. ${response.statusCode} ${JSON.stringify(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: ${JSON.stringify(body)}`) - error.futLoginError = body - loginDetails.loginCb(error) - } - }) - } - - function getCaptcha (cb) { - const url = urls.login.captchaImg + new Date().getTime() - defaultRequest.get({ - 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() -} diff --git a/src/lib/mobile-login.js b/src/lib/mobile-login.js index 29e421d..54d0b4b 100644 --- a/src/lib/mobile-login.js +++ b/src/lib/mobile-login.js @@ -8,8 +8,6 @@ import eaHasher from './eaHasher' import {CookieJar} from 'tough-cookie' import lodash from 'lodash' -const crypto = Promise.promisifyAll(require('crypto')) - export default class MobileLogin { jar = request.jar() loginDefaults = {} @@ -18,10 +16,7 @@ export default class MobileLogin { * [constructor description] * @param {[type]} options.email [description] * @param {[type]} options.password [description] - * @param {[type]} options.secret [description] * @param {[type]} options.platform [description] - * @param {[type]} options.captchaHandler [description] - * @param {[type]} options.tfCodeHandler [description] * @param {[String]} options.proxy [description] * @return {[type]} [description] */ @@ -83,9 +78,7 @@ export default class MobileLogin { } async getLogin () { - // We will use machine key later at getNucleusCode - this.machineKey = await generateMachineKey() - const url = 'https://accounts.ea.com/connect/auth?client_id=FIFA-16-MOBILE-COMPANION&response_type=code&display=web2/login&scope=basic.identity+offline+signin&locale=en_US&prompt=login&machineProfileKey=' + this.machineKey + const url = 'https://accounts.ea.com/connect/auth?prompt=login&accessToken=&client_id=FIFA-18-WEBCLIENT&response_type=token&display=web2/login&locale=en_US&redirect_uri=https://www.easports.com/fifa/ultimate-team/web-app/auth.html&scope=basic.identity+offline+signin' const response = await this.defaultRequest.getAsync(url) const title = getTitle(response) @@ -99,7 +92,7 @@ export default class MobileLogin { const form = { email: this.options.email, password: this.options.password, - country: 'HU', + country: 'US', phoneNumber: '', passwordForPhone: '', _rememberMe: 'on', @@ -116,49 +109,62 @@ export default class MobileLogin { if (!response.body.includes('redirectUri')) { throw new Error(`Unknow response at 'postLogin' title was: ${title}`) } - return this.postLoginRedirect(response) + return await this.postLoginRedirect(response) } async postLoginRedirect (prevResponse) { - const urlRegex = new RegExp("var redirectUri = '(.*)'") - const qsRegex = /redirectUri = redirectUri \+ "(.*)";/ + const urlRegex = new RegExp(/(https?:\/\/(www\/.)?[-a-zA-Z0-9@:%._\/+~#=]{2,256}\/.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\/+.~#?&//=]*))/gm) let nextUrl try { - nextUrl = urlRegex.exec(prevResponse.body)[1] - nextUrl += qsRegex.exec(prevResponse.body)[1] + nextUrl = urlRegex.exec(prevResponse.body)[0] } catch (e) { throw new Error(`RegExp failed at 'postLogin' body was: ${prevResponse.body}`) } const response = await this.defaultRequest.getAsync(nextUrl) - const title = getTitle(response) - if (title === 'Login Verification') return this.handleTwoFactorCode(response.request.href) - else if (response.request.href.includes('code=')) { - try { - let code = urlModule.parse(response.request.href, true).query.code - return this.wtfLogin(code) - } catch (e) { - throw new Error(`Couldn't parse code from url at postLoginRedirect: ${e.message}`) + if (response.body.indexOf('Your credentials are incorrect') !== -1) { + throw new Error('Unable to login. Wrong email or password ?') + } + + return await this.sendEnd(response) + } + + async sendEnd (prevResponse) { + let url = prevResponse.request.href + '&_eventId=end' + + const response = await this.defaultRequest.getAsync(url) + if (response.body.indexOf('Login Verification') !== -1) { + if (response.body.indexOf('In order to verify your identity') !== -1) { + const form = { + codeType: 'APP', + _eventId: 'submit' + } + const response2 = await this.defaultRequest.postAsync(response.request.href, {form}) + + if (response2.body.indexOf('Login Verification') !== -1) { + await this.handleTwoFactorCode(response2.request.href) + } + + await this.wtfLogin() + + return response2.request.href } } - throw Error(`Unknow response at 'postLoginRedirect' title was: ${title}`) + + await this.wtfLogin() } async handleTwoFactorCode (url) { const tfCode = await this.options.tfCodeHandler() const form = { - twofactorCode: tfCode, + oneTimeCode: tfCode, trustThisDevice: 'on', + _trustThisDevice: 'on', _eventId: 'submit' } const response = await this.defaultRequest.postAsync(url, {form}) - const title = getTitle(response) - - if (title === 'Set Up an App Authenticator') return this.cancelLoginVerificationUpdate(response.request.href) - if (title === 'Login Verification') throw new Error('Wrong two factor code.') - - throw Error(`Unknow response at 'handleTwoFactorCode' title was: ${title}`) + if (response.body.indexOf('Login Verification') !== -1) throw new Error('Wrong two factor code.') } async cancelLoginVerificationUpdate (url) { @@ -180,27 +186,22 @@ export default class MobileLogin { return true } }) - return this.wtfLogin(code) + return await this.wtfLogin(code) } - async wtfLogin (code) { - const postUrl = `https://accounts.ea.com/connect/token?grant_type=authorization_code&code=${code}&client_id=FIFA-16-MOBILE-COMPANION&client_secret=KrEoFK9ssvXKRWnTMgAu1OAMn7Y37ueUh1Vy7dIk2earFDUDABCvZuNIidYxxNbhwbj3y8pq6pSf8zBW` - let response = await this.defaultRequest.postAsync(postUrl, {json: true, headers: {'content-type': 'application/x-www-form-urlencoded'}}) - let token = response.body.access_token + async wtfLogin () { + var url = 'https://accounts.ea.com/connect/auth?response_type=token&redirect_uri=nucleus%3Arest&prompt=none&client_id=ORIGIN_JS_SDK' + let response = await this.defaultRequest.getAsync(url) + let token = JSON.parse(response.body).access_token assert(token, 'Failed to get access token at `wtfLogin`') - // this stuff seems useless but let's just do it - const url1 = `https://signin.ea.com/p/mobile/fifa/companion/code?code=${code}` - await this.defaultRequest.getAsync(url1) - const nucleusUserId = await this.getPid(token) - const sidCode = await this.getSidCode(token) + // const sidCode = await this.getSidCode(token) const sidCode2 = await this.getSidCode(token) // We will get the api url after shards await this.getShards() - await this.getUserAccounts(nucleusUserId) - const powSessionId = await this.getPowSid(sidCode) - const nucleusPersonaId = await this.getNucleusPersonaId(nucleusUserId, powSessionId) + let userAccounts = await this.getUserAccounts(nucleusUserId) + const nucleusPersonaId = await this.getNucleusPersonaId(userAccounts.body.userAccountInfo.personas) const sid = await this.getSid(sidCode2, nucleusPersonaId) const requestConfigObj1 = { @@ -214,7 +215,7 @@ export default class MobileLogin { lodash.merge(this.loginDefaults, requestConfigObj1) this.api = Promise.promisifyAll(this.defaultRequest.defaults(requestConfigObj1)) - const phisingToken = await this.validate() + const phisingToken = await this.validate(sid) const requestConfigObj2 = { headers: { @@ -263,29 +264,23 @@ export default class MobileLogin { async getUserAccounts (nucleusUserId) { const timestamp = new Date().getTime() - const url = `${this.apiUrl}/ut/game/fifa16/user/accountinfo?sku=FUT16IOS&_=${timestamp}` + const url = `${this.apiUrl}/ut/game/fifa16/user/accountinfo?sku=FUT18WEB&_=${timestamp}` return await this.defaultRequest.getAsync(url, {json: true, headers: { 'Easw-Session-Data-Nucleus-Id': nucleusUserId, 'X-UT-SID': '' }}) } - async getNucleusPersonaId (nucleusUserId, powSessionId) { - const timestamp = new Date().getTime() - const url = `https://pas.mob.v5.easfc.ea.com:8095/pow/user/self/tiergp/NucleusId/tiertp/${nucleusUserId}?offset=0&count=50&_=${timestamp}` - const {body} = await this.defaultRequest.getAsync(url, {json: true, headers: { - 'Easw-Session-Data-Nucleus-Id': nucleusUserId, - 'X-POW-SID': powSessionId - }}) - - return _.findWhere(body.userData.data, {sku: this.options.gameSku}).nucPersId + async getNucleusPersonaId (personas) { + return personas[0].personaId + // return _.findWhere(personas, {sku: this.options.gameSku}).nucPersId } async getSid (code, nucleusPersonaId) { const requestBody = { - isReadOnly: true, - sku: 'FUT16IOS', - clientVersion: 20, + isReadOnly: false, + sku: 'FUT18WEB', + clientVersion: 1, locale: 'en-US', method: 'authcode', priorityLevel: 4, @@ -312,39 +307,14 @@ export default class MobileLogin { return response.body.sid } - async getPowSid (code) { - const requestBody = { - isReadOnly: true, - sku: 'FUT16IOS', - clientVersion: 20, - locale: 'en-US', - method: 'authcode', - priorityLevel: 4, - identification: { - authCode: code, - redirectUrl: 'nucleus:rest' + async validate (sid) { + const uri = `/ut/game/fifa18/phishing/validate?answer=${this.options.secret}` + const {body} = await this.api.postAsync(uri, { + body: this.options.secret, + headers: { + 'X-UT-SID': sid } - } - // 1474229595686 - const timestamp = new Date().getTime() - const url = `https://pas.mob.v5.easfc.ea.com:8095/pow/auth?timestamp=${timestamp}` - const response = await this.defaultRequest.postAsync(url, { - body: requestBody, - json: true - // headers: { - // 'X-UT-SID': '', - // 'X-POW-SID': '', - // Accept: 'text/plain, */*; q=0.01', - // Origin: 'file://' - // } }) - return response.body.sid - // return powSid - } - - async validate () { - const uri = `/ut/game/fifa16/phishing/validate?answer=${this.options.secret}` - const {body} = await this.api.postAsync(uri, {body: this.options.secret}) return body.token } } @@ -355,37 +325,18 @@ function getTitle (response) { return title } -// example EEA58055-E4E8-42E6-B89D-DFFBBD37AF57 -async function generateMachineKey () { - let parts = await Promise.all([ - randomHex(8), - randomHex(4), - randomHex(4), - randomHex(4), - randomHex(12) - ]) - return `${parts[0]}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}` -} - -async function randomHex (length, uppercase = true) { - let randomHexStr = await crypto.randomBytesAsync(48) - randomHexStr = randomHexStr.toString('hex').substring(0, length) - if (uppercase) randomHexStr = randomHexStr.toUpperCase() - return randomHexStr -} - function getGameSku (platform) { switch (platform) { case 'pc': - return 'FFA16PCC' + return 'FFA18PCC' case 'ps3': - return 'FFA16PS3' + return 'FFA18PS3' case 'ps4': - return 'FFA16PS4' + return 'FFA18PS4' case 'x360': - return 'FFA16XBX' + return 'FFA18XBX' case 'xone': - return 'FFA16XBO' + return 'FFA18XBO' } return null diff --git a/src/lib/urls.js b/src/lib/urls.js index b878ab2..04c0123 100644 --- a/src/lib/urls.js +++ b/src/lib/urls.js @@ -1,7 +1,7 @@ // @flow 'use strict' -const urls = (version:number = 17) => { +const urls = (version:number = 18) => { return { referer: `https://www.easports.com/iframe/fut${version}/?baseShowoffUrl=https%3A%2F%2Fwww.easports.com%2Fde%2Ffifa%2Fultimate-team%2Fweb-app%2Fshow-off&guest_app_uri=http%3A%2F%2Fwww.easports.com%2Fde%2Ffifa%2Fultimate-team%2Fweb-app&locale=en_US`, login: { @@ -9,7 +9,7 @@ const urls = (version:number = 17) => { nucleus: `https://www.easports.com/iframe/fut${version}/?locale=en_US&baseShowoffUrl=https%3A%2F%2Fwww.easports.com%2Fde%2Ffifa%2Fultimate-team%2Fweb-app%2Fshow-off&guest_app_uri=http%3A%2F%2Fwww.easports.com%2Fde%2Ffifa%2Fultimate-team%2Fweb-app`, personas: 'https://www.easports.com/fifa/api/personas', shards: `https://www.easports.com/iframe/fut${version}/p/ut/shards/v2`, - accounts: `https://www.easports.com/iframe/fut${version}/p/ut/game/fifa${version}/user/accountinfo?sku=FUT${version}WEB&returningUserGameYear=2015&_=`, + accounts: `https://www.easports.com/iframe/fut${version}/p/ut/game/fifa${version}/user/accountinfo?sku=FUT${version}WEB&returningUserGameYear=2017&_=`, session: `https://www.easports.com/iframe/fut${version}/p/ut/auth`, question: `https://www.easports.com/iframe/fut${version}/p/ut/game/fifa${version}/phishing/question?redirect=false&_=`, validate: `https://www.easports.com/iframe/fut${version}/p/ut/game/fifa${version}/phishing/validate?_=`,