UNPKG

@proak/fut

Version:

Node implementation of Fifa Ultimate Team

406 lines (360 loc) 10.3 kB
'use strict' const { format } = require('url') const urls = require('./urls') const { merge } = require('lodash') const { parse } = require('./utils') const defaults = require('./defaults') const request = require('request-promise') const queryString = require('query-string') const { CookieJar } = require('tough-cookie') const requestInternal = request.defaults({ json: true, gzip: true, followAllRedirects: true, headers: { DNT: '1', Referer: urls.HOME, Connection: 'keep-alive', 'Cache-Control': 'max-age=0', Origin: urls.fut19.EASPORTS, Accept: defaults.DEFAULT_ACCEPT, 'Accept-Language': 'en-US,en;q=0.9', 'Accept-Encoding': 'gzip, deflate, br', 'User-Agent': defaults.DEFAULT_USER_AGENT } }) module.exports = () => { let cookieJar = request.jar() const serializeCookieJar = _ => JSON.stringify(cookieJar._jar) const updateCookieJar = jar => { if (jar === '') { return } cookieJar = merge(cookieJar, { _jar: CookieJar.deserializeSync(jar) }) } const getLoginFormUrl = async _ => { const { request, body } = await requestInternal.get({ jar: cookieJar, resolveWithFullResponse: true, uri: urls.accounts.AUTH_PROMPT, headers: { Referer: urls.HOME, Host: 'accounts.ea.com', 'Upgrade-Insecure-Requests': '1' } }) const title = parse.title(body) if (title !== 'Log In') { throw new Error('Unknown Fifa FUT 19 auth prompt response') } return request.href } const getVerificationUrl = async ({ redirectUrl, referer }) => { const { body, request } = await requestInternal.get({ jar: cookieJar, uri: redirectUrl, headers: { Referer: referer, Host: 'signin.ea.com', Origin: 'https://signin.ea.com', 'Upgrade-Insecure-Requests': '1' }, resolveWithFullResponse: true }) const title = parse.title(body) if (title !== 'Login Verification') { throw new Error('Unknown Fifa FUT 19 login form verify response title') } return request.href } const sendVerificationCode = async verificationUrl => { const { body, request } = await requestInternal.post({ json: true, jar: cookieJar, uri: verificationUrl, headers: { Referer: verificationUrl, Host: 'signin.ea.com', Origin: 'https://signin.ea.com', 'Upgrade-Insecure-Requests': '1' }, form: { codeType: 'EMAIL', _eventId: 'submit' }, resolveWithFullResponse: true }) const email = parse.email(body) return { email, verificationUrl: request.href } } const loginWithCredentials = async ({ email, password }) => { const formUrl = await getLoginFormUrl() const { body, request } = await requestInternal.post({ uri: formUrl, jar: cookieJar, form: { email, password, country: 'GH', phoneNumber: '', passwordForPhone: '', _rememberMe: 'on', rememberMe: 'on', _eventId: 'submit', gCaptchaResponse: '', isPhoneNumberLogin: 'false', isIncompletePhone: '' }, resolveWithFullResponse: true, headers: { Referer: formUrl, Host: 'signin.ea.com', Origin: 'https://signin.ea.com', 'Content-Type': 'application/x-www-form-urlencoded', 'Upgrade-Insecure-Requests': '1' } }) const title = parse.title(body) if (title === 'Log In') { throw new Error('Invalid Fifa FUT 19 Login Credentials') } const redirectUrl = parse.redirectUrl(body) return { redirectUrl, referer: request.href } } const loginInternal = async ({ email, password }) => { const { redirectUrl, referer } = await loginWithCredentials({ email, password }) console.log('==> after with creds', referer, redirectUrl) const verificationUrl = await getVerificationUrl({ referer, redirectUrl }) console.log('===> verification url', verificationUrl) const details = await sendVerificationCode(verificationUrl) console.log('==> send code url', details) return details } const sendSecurityCode = async ({ uri, code = '' }) => { const { request } = await requestInternal.post({ uri, jar: cookieJar, headers: { Referer: uri, Origin: 'https://signin.ea.com', 'Upgrade-Insecure-Requests': '1' }, form: { oneTimeCode: code, _eventId: 'submit', trustThisDevice: 'on', _trustThisDevice: 'on' }, resolveWithFullResponse: true }) const { token_type: tokenType, access_token: accessToken } = queryString.parse(request.href.split('#')[1]) if (!(tokenType && accessToken)) { throw new Error('Unable to retrieve tokens') } return { tokenType, accessToken } } const getNucleusCreds = async ({ jar = '' } = {}) => { updateCookieJar(jar) const body = await requestInternal.get({ jar: cookieJar, url: urls.accounts.AUTH, resolveWithFullResponse: false }) if (!body.access_token || !body.token_type) { throw new Error('Could not get access tokens') } return body } const getIdentity = async ({ token, jar = '' } = {}) => { updateCookieJar(jar) return requestInternal.get({ jar: cookieJar, headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', Accept: 'application/json; charset=utf-8; */*; q=0.01' }, resolveWithFullResponse: false, url: urls.accounts.PROXY_IDENTITY }) } const getAccountInfo = async ({ pidId, jar = '' } = {}, platform) => { updateCookieJar(jar) const { accounts } = urls try { const { userAccountInfo: { personas } } = await requestInternal.get({ url: format({ protocol: 'https', host: getHost(platform), pathname: accounts.ACCOUNT_INFO_PATH, query: { filterConsoleLogin: true, sku: 'FUT19WEB', returningUserGameYear: 2018 } }), jar: cookieJar, resolveWithFullResponse: false, headers: { 'Content-Type': 'application/json', 'Easw-Session-Data-Nucleus-Id': pidId, Accept: 'application/json; charset=utf-8; */*; q=0.01' } }) return personas[0] } catch (error) { console.log('Trying to [FifaFut.getAccountInfo]', error) const message = (error.error && error.error.reason) || `Account not available for platform ${platform}` throw new Error(message) } } const getAuthCode = async token => { return requestInternal.get({ json: true, jar: cookieJar, resolveWithFullResponse: false, url: `${urls.accounts.AUTH_CHECK}${token}`, headers: { 'Content-Type': 'application/json', Accept: 'application/json; charset=utf-8; */*; q=0.01' } }) } const getGameSKU = platform => { let suffix = 'PCC' switch (platform.toLowerCase()) { case 'xbox': suffix = 'XBO' break case 'xbox360': suffix = 'XBX' break case 'ps3': suffix = 'PS3' break case 'ps4': suffix = 'PS4' break } return `FFA19${suffix}` } const getHost = platform => { const { XBOX, OTHERS } = urls.hosts return platform.toLowerCase().includes('xbox') ? XBOX : OTHERS } const getSession = async ({ code, personaId, jar = '', platform } = {}) => { updateCookieJar(jar) return requestInternal.post({ jar: cookieJar, url: format({ protocol: 'https', pathname: urls.accounts.SESSION_AUTH_PATH, host: getHost(platform), query: { sku_c: 'fut19' } }), headers: { 'Content-Type': 'application/json', Accept: 'application/json; charset=utf-8; */*; q=0.01' }, body: { locale: 'en-US', sku: 'FUT19WEB', priorityLevel: 4, clientVersion: 1, isReadOnly: false, method: 'authcode', gameSku: getGameSKU(platform), nucleusPersonaId: personaId, identification: { authCode: code, redirectUrl: 'nucleus:rest' } } }) } const getUserMassInfo = async ({ sid }, platform) => { return requestInternal.get({ jar: cookieJar, url: format({ protocol: 'https', host: getHost(platform), pathname: urls.accounts.USER_MASS_INFO_PATH }), headers: { 'X-UT-SID': sid, 'Content-Type': 'application/json', Accept: 'application/json; charset=utf-8; */*; q=0.01' } }) } const getAccount = async ({ jar = '', platform = 'PS4' } = {}) => { updateCookieJar(jar) const { access_token: accessToken } = await getNucleusCreds() const { pid } = await getIdentity({ token: accessToken }) const accountInfo = await getAccountInfo(pid, platform) const { code } = await getAuthCode(accessToken) const session = await getSession({ code, personaId: accountInfo.personaId, platform }) const massInfo = await getUserMassInfo(session, platform) return { ...massInfo, accessToken, sid: session.sid, pidId: pid.pidId, cookieJar: serializeCookieJar(), phishingToken: session.phishingToken } } return { getAccount, getIdentity, getAccountInfo, getNucleusCreds, async login (details) { const data = await loginInternal(details) return { ...data, success: true, cookieJar: serializeCookieJar(), message: 'Awaiting security code' } }, async verifySecurityCode ({ verificationUrl, code, jar = '' }) { cookieJar = merge(request.jar(), { _jar: CookieJar.deserializeSync(jar) }) const tokenDetails = await sendSecurityCode({ code, uri: verificationUrl }) return { cookieJar: serializeCookieJar(), ...tokenDetails } } } }