From c966e90b414fa5cb0cbbb61b1028cdabc0441e10 Mon Sep 17 00:00:00 2001 From: Ciensprog Date: Mon, 22 Apr 2024 21:25:40 -0600 Subject: [PATCH] fix: generate exchange code with launcherAppClient2 --- src/kernel/core/launcher.ts | 116 +++++++++++++++++++------- src/kernel/core/manifest.ts | 50 +++++++++++ src/kernel/main.ts | 9 +- src/services/config/caldera.ts | 12 +++ src/services/config/oauth.ts | 8 ++ src/services/config/public-account.ts | 12 +++ src/services/endpoints/oauth.ts | 17 ++-- 7 files changed, 187 insertions(+), 37 deletions(-) create mode 100644 src/kernel/core/manifest.ts diff --git a/src/kernel/core/launcher.ts b/src/kernel/core/launcher.ts index 97f4be6..d71e97a 100644 --- a/src/kernel/core/launcher.ts +++ b/src/kernel/core/launcher.ts @@ -4,61 +4,117 @@ import childProcess from 'node:child_process' import { BrowserWindow } from 'electron' import { ElectronAPIEventKeys } from '../../config/constants/main-process' +import { launcherAppClient2 } from '../../config/fortnite/clients' import { DataDirectory } from '../startup/data-directory' import { Authentication } from './authentication' +// import { Manifest } from './manifest' -import { getExchangeCodeUsingAccessToken } from '../../services/endpoints/oauth' +import { + getAccessTokenUsingExchangeCode, + getExchangeCodeUsingAccessToken, +} from '../../services/endpoints/oauth' export class FortniteLauncher { static async start(currentWindow: BrowserWindow, account: AccountData) { + const sendError = () => { + currentWindow.webContents.send( + ElectronAPIEventKeys.LauncherNotification, + { + account, + status: false, + } + ) + } + try { + // const manifest = Manifest.get() + + // if (!manifest) { + // sendError() + + // return + // } + const { settings } = await DataDirectory.getSettingsFile() const accessToken = await Authentication.verifyAccessToken(account) if (!accessToken) { - currentWindow.webContents.send( - ElectronAPIEventKeys.LauncherNotification, - { - account, - status: false, - } - ) + sendError() return } - const exchange = await getExchangeCodeUsingAccessToken(accessToken) + const accountExchangeCode = + await getExchangeCodeUsingAccessToken(accessToken) - if (exchange.data.code) { - childProcess.exec( - `start "" FortniteLauncher.exe -AUTH_LOGIN=unused -AUTH_TYPE=exchangecode -epicapp=Fortnite -epicenv=Prod -epicsandboxid=fn -EpicPortal -steamimportavailable -AUTH_PASSWORD=${exchange.data.code} -epicuserid=${account.accountId} -epicusername="${account.displayName}"`, - { - cwd: settings.path, - } - ) + if (!accountExchangeCode.data.code) { + sendError() - currentWindow.webContents.send( - ElectronAPIEventKeys.LauncherNotification, - { - account, - status: true, - } - ) + return + } + + const launcherAccessToken = await getAccessTokenUsingExchangeCode( + accountExchangeCode.data.code, + { + headers: { + Authorization: `Basic ${launcherAppClient2.auth}`, + }, + } + ) + + if (!launcherAccessToken.data.access_token) { + sendError() return } + + const launcherExchangeCode = await getExchangeCodeUsingAccessToken( + launcherAccessToken.data.access_token + ) + + if (!launcherExchangeCode.data.code) { + sendError() + + return + } + + const command = [ + 'start', + '""', + 'FortniteLauncher.exe', + // `${manifest.LaunchCommand}`, + '-AUTH_LOGIN=unused', + `-AUTH_PASSWORD=${launcherExchangeCode.data.code}`, + '-AUTH_TYPE=exchangecode', + '-epicapp=Fortnite', + '-epicenv=Prod', + '-EpicPortal', + // '-steamimportavailable', + `-epicusername="${account.displayName}"`, + `-epicuserid=${account.accountId}`, + // '-epiclocale=en', + // '-epicsandboxid=fn', + ].join(' ') + + childProcess.exec(command, { + cwd: settings.path, + }) + + currentWindow.webContents.send( + ElectronAPIEventKeys.LauncherNotification, + { + account, + status: true, + } + ) + + return } catch (error) { // } - currentWindow.webContents.send( - ElectronAPIEventKeys.LauncherNotification, - { - account, - status: false, - } - ) + sendError() } } diff --git a/src/kernel/core/manifest.ts b/src/kernel/core/manifest.ts new file mode 100644 index 0000000..f052df8 --- /dev/null +++ b/src/kernel/core/manifest.ts @@ -0,0 +1,50 @@ +import { readdirSync, readFileSync } from 'node:fs' +import path from 'node:path' + +export class Manifest { + static get() { + try { + const manifestsDirectory = + 'C:\\ProgramData\\Epic\\EpicGamesLauncher\\Data\\Manifests' + + const getFile = (filename: string) => { + const filePath = path.join(manifestsDirectory, filename) + const file = JSON.parse(readFileSync(filePath).toString()) as { + AppVersionString: string + LaunchCommand: string + DisplayName: string + } + const appVersionString = file.AppVersionString?.trim() + + return { + AppVersionString: appVersionString ?? '', + DisplayName: file.DisplayName?.trim().toLowerCase() ?? '', + LaunchCommand: file.LaunchCommand?.trim() ?? '', + UserAgent: appVersionString + ? `Fortnite/${appVersionString}` + : '', + } + } + + const responseItem = readdirSync(manifestsDirectory).find( + (filename) => { + if (filename.endsWith('item')) { + return getFile(filename).DisplayName === 'fortnite' + } + + return false + } + ) + + if (responseItem) { + const manifestItem = getFile(responseItem) + + return manifestItem + } + } catch (error) { + // + } + + return null + } +} diff --git a/src/kernel/main.ts b/src/kernel/main.ts index 2f274ff..4600de9 100644 --- a/src/kernel/main.ts +++ b/src/kernel/main.ts @@ -5,13 +5,13 @@ import type { Settings } from '../types/settings' import path from 'node:path' import { app, BrowserWindow, ipcMain, Menu, shell } from 'electron' import schedule from 'node-schedule' -// import { updateElectronApp } from 'update-electron-app' import { ElectronAPIEventKeys } from '../config/constants/main-process' import { AntiCheatProvider } from './core/anti-cheat-provider' import { Authentication } from './core/authentication' import { FortniteLauncher } from './core/launcher' +import { Manifest } from './core/manifest' import { AccountsManager } from './startup/accounts' import { Application } from './startup/application' import { DataDirectory } from './startup/data-directory' @@ -38,6 +38,12 @@ async function createWindow() { }, }) + const manifest = Manifest.get() + + if (manifest) { + mainWindow.webContents.setUserAgent(manifest.UserAgent) + } + // and load the index.html of the app. if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { mainWindow.webContents.openDevTools({ @@ -58,7 +64,6 @@ async function createWindow() { } Menu.setApplicationMenu(null) -// updateElectronApp() // This method will be called when Electron has finished // initialization and is ready to create browser windows. diff --git a/src/services/config/caldera.ts b/src/services/config/caldera.ts index 1f54e84..3047c97 100644 --- a/src/services/config/caldera.ts +++ b/src/services/config/caldera.ts @@ -1,5 +1,7 @@ import axios from 'axios' +import { Manifest } from '../../kernel/core/manifest' + /** * Caldera Service */ @@ -8,3 +10,13 @@ export const calderaService = axios.create({ baseURL: 'https://caldera-service-prod.ecosec.on.epicgames.com/caldera/api/v1/launcher', }) + +calderaService.interceptors.request.use((config) => { + const manifest = Manifest.get() + + if (manifest) { + config.headers.setUserAgent(manifest.UserAgent) + } + + return config +}) diff --git a/src/services/config/oauth.ts b/src/services/config/oauth.ts index b2c5e1c..ada86c2 100644 --- a/src/services/config/oauth.ts +++ b/src/services/config/oauth.ts @@ -9,6 +9,8 @@ import { fortniteIOSGameClient, } from '../../config/fortnite/clients' +import { Manifest } from '../../kernel/core/manifest' + /** * OAuth Service */ @@ -39,6 +41,12 @@ axiosRetry(oauthService, { }) oauthService.interceptors.request.use((config) => { + const manifest = Manifest.get() + + if (manifest) { + config.headers.setUserAgent(manifest.UserAgent) + } + if (!config.headers.getAuthorization()) { /** * Load Authorization header with default client on every request diff --git a/src/services/config/public-account.ts b/src/services/config/public-account.ts index 110a503..1818792 100644 --- a/src/services/config/public-account.ts +++ b/src/services/config/public-account.ts @@ -1,5 +1,7 @@ import axios from 'axios' +import { Manifest } from '../../kernel/core/manifest' + /** * Public Account Service */ @@ -8,3 +10,13 @@ export const publicAccountService = axios.create({ baseURL: 'https://account-public-service-prod.ol.epicgames.com/account/api/public/account', }) + +publicAccountService.interceptors.request.use((config) => { + const manifest = Manifest.get() + + if (manifest) { + config.headers.setUserAgent(manifest.UserAgent) + } + + return config +}) diff --git a/src/services/endpoints/oauth.ts b/src/services/endpoints/oauth.ts index 1f9b353..6e55328 100644 --- a/src/services/endpoints/oauth.ts +++ b/src/services/endpoints/oauth.ts @@ -16,11 +16,18 @@ export function getAccessTokenUsingAuthorizationCode(code: string) { }) } -export function getAccessTokenUsingExchangeCode(exchange_code: string) { - return oauthService.post('/token', { - grant_type: 'exchange_code', - exchange_code, - }) +export function getAccessTokenUsingExchangeCode( + exchange_code: string, + config?: AxiosRequestConfig +) { + return oauthService.post( + '/token', + { + grant_type: 'exchange_code', + exchange_code, + }, + config + ) } export function getAccessTokenUsingDeviceAuth(