Skip to content

Commit

Permalink
fix: generate exchange code with launcherAppClient2 (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ciensprog authored Apr 23, 2024
2 parents 762461e + c966e90 commit 40edbf9
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 37 deletions.
116 changes: 86 additions & 30 deletions src/kernel/core/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
50 changes: 50 additions & 0 deletions src/kernel/core/manifest.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
9 changes: 7 additions & 2 deletions src/kernel/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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({
Expand All @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions src/services/config/caldera.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios'

import { Manifest } from '../../kernel/core/manifest'

/**
* Caldera Service
*/
Expand All @@ -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
})
8 changes: 8 additions & 0 deletions src/services/config/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
fortniteIOSGameClient,
} from '../../config/fortnite/clients'

import { Manifest } from '../../kernel/core/manifest'

/**
* OAuth Service
*/
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/services/config/public-account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios'

import { Manifest } from '../../kernel/core/manifest'

/**
* Public Account Service
*/
Expand All @@ -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
})
17 changes: 12 additions & 5 deletions src/services/endpoints/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ export function getAccessTokenUsingAuthorizationCode(code: string) {
})
}

export function getAccessTokenUsingExchangeCode(exchange_code: string) {
return oauthService.post<ExchangeCodeResponse>('/token', {
grant_type: 'exchange_code',
exchange_code,
})
export function getAccessTokenUsingExchangeCode(
exchange_code: string,
config?: AxiosRequestConfig
) {
return oauthService.post<ExchangeCodeResponse>(
'/token',
{
grant_type: 'exchange_code',
exchange_code,
},
config
)
}

export function getAccessTokenUsingDeviceAuth(
Expand Down

0 comments on commit 40edbf9

Please sign in to comment.