Remade auth.rs and adapted main.rs with it.

This commit is contained in:
Marta Borgia Leiva 2024-12-24 18:03:41 +01:00
parent 6bd7d2d1db
commit a98d93ffd1
2 changed files with 482 additions and 324 deletions

View file

@ -1,315 +1,458 @@
use std::thread::AccessError;
use reqwest::blocking::*;
use crate::auth::GameAuthenticationError::{IllegalParameter, IllegalRequest, IllegalResponse};
use log::*;
use serde::Deserialize;
use reqwest::{blocking::*, header::*, StatusCode};
use serde::*;
use std::collections::HashMap;
use std::{error::Error, fmt::Display};
const OAUTH_TOKEN : (&str, &str)= (
const OAUTH_TOKEN: (&str, &str) = (
"3f69e56c7649492c8cc29f1af08a8a12", // Client
"b51ee9cb12234f50a69efa67ef53812e", // Secret
);
/// Describes possible errors that may come from the functions and methods in this module.
#[derive(Debug)]
pub struct DeviceCredentials {
access_token : String,
pub account_id : String,
device_id : String,
pub enum GameAuthenticationError {
IllegalParameter(String),
IllegalRequest(String),
IllegalResponse(String),
RequestFailed,
}
impl Display for GameAuthenticationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl Error for GameAuthenticationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
todo!();
match self {
IllegalParameter(reason) => Some(&IllegalParameter(reason.to_owned())),
IllegalResponse(reason) => Some(&IllegalResponse(reason.to_owned())),
GameAuthenticationError::IllegalRequest(reason) => {
Some(&IllegalRequest(reason.to_owned()))
}
_ => None,
}
}
}
pub enum ClientType {
ANDROID,
GENERIC,
PC,
}
#[derive(Debug)]
pub struct AccessToken(pub String);
impl AccessToken {
pub fn new() -> AccessToken {
AccessToken(String::new())
}
pub fn from(access_token: AccessToken) -> AccessToken {
AccessToken(access_token.0)
}
pub fn from_authcode(
http_client: &reqwest::blocking::Client,
authcode: &str,
persistent_credentials: Option<&mut PersistentCredentials>,
client_type: ClientType,
) -> Result<AccessToken, Box<dyn Error>> {
if authcode.is_empty() {
error!("Authentication code for access token cannot be empty");
Err(Box::new(GameAuthenticationError::IllegalParameter(
"Authentication code for access token cannot be empty".to_string(),
)))?;
}
#[derive(Debug, Deserialize)]
struct ResponseContent {
access_token: String,
account_id: String,
}
const URL: &str =
"https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
/* Request Headers */
let mut request_headers = HeaderMap::new();
request_headers.insert(
"Content-Type",
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
request_headers.insert(
"Authorization",
HeaderValue::from_static(match client_type {
ClientType::ANDROID => "Basic M2Y2OWU1NmM3NjQ5NDkyYzhjYzI5ZjFhZjA4YThhMTI6YjUxZWU5Y2IxMjIzNGY1MGE2OWVmYTY3ZWY1MzgxMmU=",
ClientType::PC => "BASIC MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=",
ClientType::GENERIC => "Basic MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=",
}),
);
/* Request Body */
// "grant_type=authorization_code&code=<authcode>"
let request_body: reqwest::blocking::Body = reqwest::blocking::Body::from(
"grant_type=authorization_code&code=".trim().to_owned() + authcode.trim(),
);
/* Response Fetching */
let response = http_client
.post(URL)
.headers(request_headers)
.body(request_body)
.send()?;
info!("Got HTTP response: {:?}", response);
info!("Got HTTP response headers: {:?}", response.headers());
match response.status() {
StatusCode::OK => {
let response_content: ResponseContent = response.json::<ResponseContent>()?;
// WARN: Side effect here!!!
if persistent_credentials.is_some() {
persistent_credentials.unwrap().account_id = response_content.account_id;
}
return Ok(AccessToken(response_content.access_token));
}
StatusCode::INTERNAL_SERVER_ERROR => {
error!("Internal Server Error when fetching Access Token with Authentication Code");
Err(Box::new(GameAuthenticationError::RequestFailed))?
}
StatusCode::UNAUTHORIZED => {
error!("Unauthorized action when fetching Access Token with Authentication Code");
Err(Box::new(GameAuthenticationError::IllegalRequest(
"Unauthorized action when fetching Access Token with Authentication Code"
.to_string(),
)))?
}
_ => {
error!("Unsupported response when fetching Access Token with Authentication Code");
error!("Got response {:?}", response.text().unwrap_or_default());
Err(Box::new(GameAuthenticationError::IllegalResponse(
"Unsupported response when fetching Access Token with Authentication Code"
.to_string(),
)))?
}
}
}
pub fn from_persistent_credentials(
http_client: &reqwest::blocking::Client,
credentials: &PersistentCredentials,
client_type: ClientType,
) -> Result<AccessToken, Box<dyn Error>> {
// Checking credentials' content
{
if credentials.account_id.is_empty() {
Err(Box::new(IllegalParameter(
"credentials.account_id cannot be empty".to_string(),
)))?;
}
if credentials.device_id.is_empty() {
Err(Box::new(IllegalParameter(
"credentials.device_id cannot be empty".to_string(),
)))?;
}
if credentials.secret.is_empty() {
Err(Box::new(IllegalParameter(
"credentials.secret cannot be empty".to_string(),
)))?
}
}
#[derive(Debug, Deserialize)]
struct ResponseContent {
access_token: String,
}
const URL: &str =
"https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
let mut request_headers: HeaderMap = HeaderMap::new();
request_headers.insert(
"Content-Type",
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
request_headers.insert(
"Authorization",
HeaderValue::from_static(match client_type {
ClientType::ANDROID => "Basic M2Y2OWU1NmM3NjQ5NDkyYzhjYzI5ZjFhZjA4YThhMTI6YjUxZWU5Y2IxMjIzNGY1MGE2OWVmYTY3ZWY1MzgxMmU=",
ClientType::PC => "BASIC MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=",
ClientType::GENERIC => "Basic MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=",
})
);
let request_body: reqwest::blocking::Body = reqwest::blocking::Body::from(
"grant_type=device_auth".to_string()
+ "&account_id=".trim()
+ &credentials.account_id
+ "&device_id=".trim()
+ &credentials.device_id
+ "&secret=".trim()
+ &credentials.secret,
);
let response = http_client
.post(URL)
.headers(request_headers)
.body(request_body)
.send()?;
match response.status() {
StatusCode::OK => {
let response_content: ResponseContent = response.json::<ResponseContent>()?;
return Ok(AccessToken(response_content.access_token));
}
StatusCode::INTERNAL_SERVER_ERROR => {
error!(
"Internal Server Error when fetching Access Token with Persistent Credentials"
);
Err(Box::new(GameAuthenticationError::RequestFailed))?
}
StatusCode::UNAUTHORIZED => {
error!(
"Unauthorized action when fetching Access Token with Persistent Credentials"
);
Err(Box::new(GameAuthenticationError::IllegalRequest(
"Unauthorized action when fetching Access Token with Persistent Credentials"
.to_string(),
)))?
}
_ => {
error!(
"Unsupported response when fetching Access Token with Persistent Credentials"
);
error!("Got response:\n {:?}", response.text().unwrap_or_default());
Err(Box::new(GameAuthenticationError::IllegalResponse(
"Unsupported response when fetching Access Token with Persistent Credentials"
.to_string(),
)))?
}
}
}
pub fn from_exchange_code(
http_client: &Client,
exchange_code: &ExchangeCode,
) -> Result<AccessToken, Box<dyn Error>> {
if exchange_code.0.is_empty() {
Err(Box::new(GameAuthenticationError::IllegalParameter(
"exchange_code cannot be empty".to_owned(),
)))?
}
#[derive(Debug, Deserialize)]
struct ResponseContent {
access_token: String,
}
const URL: &str =
"https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
let mut request_headers: HeaderMap = HeaderMap::new();
request_headers.insert(
"Content-Type",
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
request_headers.insert(
"Authorization",
HeaderValue::from_static("Basic MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y="),
);
let request_body: reqwest::blocking::Body = reqwest::blocking::Body::from(
"grant_type=exchange_code".to_string() + "&exchange_code=" + &exchange_code.0,
);
let response = http_client
.post(URL)
.headers(request_headers)
.body(request_body)
.send()?;
match response.status() {
StatusCode::OK => {
let response_content: ResponseContent = response.json::<ResponseContent>()?;
Ok(AccessToken(response_content.access_token))
}
StatusCode::INTERNAL_SERVER_ERROR => {
error!("Internal Server Error when fetching Access Token with Exchange Code");
Err(Box::new(GameAuthenticationError::RequestFailed))?
}
StatusCode::UNAUTHORIZED => {
error!("Unauthorized action when fetching Access Token with Exchange Code");
Err(Box::new(GameAuthenticationError::IllegalRequest(
"Unauthorized action when fetching Access Token with Exchange Code".to_string(),
)))?
}
_ => {
error!("Unsupported response when fetching Access Token with Exchange Code");
error!("Got response {:?}", response.text().unwrap_or_default());
Err(Box::new(GameAuthenticationError::IllegalResponse(
"Unsupported response when fetching Access Token with Exchange Code"
.to_string(),
)))?
}
}
}
}
/// Contains the account info that can be kept on the device to login in the game
#[derive(Debug)]
pub struct PersistentCredentials {
pub account_id: String,
device_id: String,
secret: String,
}
impl DeviceCredentials {
pub fn new() -> DeviceCredentials {
DeviceCredentials{
device_id: String::new(),
impl PersistentCredentials {
/// Creates a new empty instance of PersistentCredentials.
pub fn new() -> PersistentCredentials {
PersistentCredentials {
account_id: String::new(),
access_token: String::new(),
device_id: String::new(),
secret: String::new(),
}
}
pub fn get_access_token_and_account_id(&mut self, http_client: &Client, authcode: &str) {
if authcode.is_empty() {
error!("Authentication Code cannot be empty");
return;
}
#[derive(Debug, Deserialize)]
struct ResponseStruct {
access_token: String,
expires_in: u16,
expires_at: String,
token_type: String,
refresh_token: String,
refresh_expires: u16,
refresh_expires_at: String,
account_id: String,
client_id: String,
internal_client: bool,
client_service: String,
scope: Vec<String>,
displayName: String,
app: String,
in_app_id: String,
product_id: String,
application_id: String,
acr: String,
auth_time: String
}
let url = "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
let mut req_body: String = String::from("grant_type=authorization_code&code=").trim().to_string();
req_body.push_str(authcode.trim());
let response = Client::post(&http_client, url)
.body(req_body)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization",
"Basic M2Y2OWU1NmM3NjQ5NDkyYzhjYzI5ZjFhZjA4YThhMTI6YjUxZWU5Y2IxMjIzNGY1MGE2OWVmYTY3ZWY1MzgxMmU=")
.send();
match response {
Ok(response_data) => {
match response_data.json::<ResponseStruct>().ok() {
Some(response) => {
self.access_token = response.access_token;
self.account_id = response.account_id;
},
None => {
error!("Failed to get access_token!");
}
}
},
_ => { error!("Failed to get access token!"); },
/// Creates a new instance of PersistentCredentials with the given String parameters.
pub fn from(account_id: String, device_id: String, secret: String) -> PersistentCredentials {
PersistentCredentials {
account_id,
device_id,
secret,
}
}
pub fn get_device_auth_and_secret(&mut self, http_client: &Client) {
if self.access_token.is_empty() || self.account_id.is_empty() {
error!("Device access token cannot be empty!");
return;
/// Fetches the Persistent credentials
pub fn fetch(
http_client: &reqwest::blocking::Client,
authcode: &str,
) -> Result<PersistentCredentials, Box<dyn Error>> {
if authcode.is_empty() {
Err(GameAuthenticationError::IllegalParameter(
"Authorization code cannot be empty".to_string(),
))?
}
let mut url : String = String::from("https://account-public-service-prod.ol.epicgames.com/account/api/public/account/");
url.push_str(self.account_id.as_str());
url.push_str("/deviceAuth");
// PersistentCredentials to be returned by this function.
let mut resulting_credentials = PersistentCredentials::new();
/*
let it : String = String::new;
it.starts_with("one thing");
// Just discovered String.starts_with() KEKW
*/
// Getting access token and account id
info!("Getting Access Token");
let access_token: AccessToken = AccessToken::from_authcode(
http_client,
&authcode,
Some(&mut resulting_credentials),
ClientType::ANDROID,
)?;
#[derive(Debug, Deserialize)]
struct CreatedObject {
location: String,
ipAddress: String,
dateTime: String,
}
// Getting device_id and secret
{
let url = format!("https://account-public-service-prod.ol.epicgames.com/account/api/public/account/{}/deviceAuth", resulting_credentials.account_id);
#[derive(Debug, Deserialize)]
struct ResponseStruct {
deviceId: String,
accountId: String,
secret: String,
userAgent: String,
created: CreatedObject,
}
let mut headers = HeaderMap::new();
headers.insert(
"Authorization",
format!("Bearer {}", access_token.0).parse().unwrap(),
);
let mut bearer_header = String::from("Bearer ");
bearer_header.push_str(self.access_token.as_str());
#[derive(Debug, Deserialize)]
struct ResponseContent {
deviceId: String,
accountId: String,
secret: String,
}
let response = Client::post(&http_client, url)
.header("Authorization", bearer_header.as_str())
.send();
let response = http_client.post(&url).headers(headers).send()?;
match response {
Ok(response_data) => {
match response_data.json::<ResponseStruct>().ok() {
Some(response_json) => {
self.device_id = response_json.deviceId;
self.secret = response_json.secret;
}
None => {
error!("Failed to parse device_id!");
},
match response.status() {
StatusCode::OK => {
let response_content: ResponseContent = response.json::<ResponseContent>()?;
resulting_credentials.device_id = response_content.deviceId;
resulting_credentials.account_id = response_content.accountId;
resulting_credentials.secret = response_content.secret;
Ok(resulting_credentials)
}
},
_ => { error!("Failed to get device_id!"); },
StatusCode::INTERNAL_SERVER_ERROR => {
error!("Internal Server Error when fetching device_id and secret with Access Token");
Err(Box::new(GameAuthenticationError::RequestFailed))?
}
StatusCode::UNAUTHORIZED => {
error!(
"Unauthorized action when fetching device_id and secret with Access Token"
);
Err(Box::new(GameAuthenticationError::IllegalRequest(
"Unauthorized action when fetching device_id and secret with Access Token"
.to_string(),
)))?
}
_ => {
error!(
"Unsupported response when fetching device_id and secret with Access Token"
);
error!("Got response {:?}", response.text().unwrap_or_default());
Err(Box::new(GameAuthenticationError::IllegalResponse(
"Unsupported response when fetching device_id and secret with Access Token"
.to_string(),
)))?
}
}
}
}
}
#[derive(Debug)]
pub struct TemporaryCredentials {
access_token : String,
pub exchange_code : String,
}
impl TemporaryCredentials {
pub fn new() -> TemporaryCredentials {
TemporaryCredentials {
access_token : String::new(),
exchange_code : String::new(),
}
pub struct ExchangeCode(pub String);
impl ExchangeCode {
pub fn new() -> ExchangeCode {
ExchangeCode(String::new())
}
pub fn get_access_token_from_device_auth(&mut self, http_client: &Client, device_credentials: &DeviceCredentials) /*Android*/ {
pub fn from_persistent_credentials(
http_client: &reqwest::blocking::Client,
access_token: &AccessToken,
client_type: ClientType,
) -> Result<ExchangeCode, GameAuthenticationError> {
#[derive(Debug, Deserialize)]
struct ResponseStruct {
access_token: String,
expires_in: u16,
expires_at: String,
token_type: String,
refresh_token: String,
refresh_expires: u16,
refresh_expires_at: String,
account_id: String,
client_id: String,
internal_client: bool,
client_service: String,
displayName: String,
app: String,
in_app_id: String,
product_id: String,
application_id: String,
acr: String,
auth_time: String
}
let mut request_body = String::new();
{
// Grant type
request_body.push_str("grant_type=device_auth"
.trim());
// AccountID
request_body.push_str("&account_id=");
request_body.push_str(device_credentials.account_id
.as_str()
.trim());
// DeviceID
request_body.push_str("&device_id=");
request_body.push_str(device_credentials.device_id
.as_str()
.trim());
// Secret
request_body.push_str("&secret=");
request_body.push_str(device_credentials.secret
.as_str()
.trim());
request_body = request_body.trim().to_string();
}
let url : &str = "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
let response = Client::post(&http_client, url)
.body(request_body)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization",
"Basic M2Y2OWU1NmM3NjQ5NDkyYzhjYzI5ZjFhZjA4YThhMTI6YjUxZWU5Y2IxMjIzNGY1MGE2OWVmYTY3ZWY1MzgxMmU=")
.send();
match response {
Ok(response_data) => {
match response_data.json::<ResponseStruct>().ok() {
Some(response) => {
self.access_token = response.access_token;
},
None => {
error!("Failed to parse access_token!");
}
}
},
_ => { error!("Failed to get access token!"); },
}
}
pub fn get_access_token_from_exchange_code(&mut self, http_client: &Client, temporary_credentials: &TemporaryCredentials) /*Generic*/ {
#[derive(Debug, Deserialize)]
struct ResponseStruct {
access_token: String,
/*expires_in: u16,
expires_at: String,
token_type: String,
refresh_token: String,
refresh_expires: u16,
refresh_expires_at: String,
account_id: String,
client_id: String,
internal_client: bool,
client_service: String,
displayName: String,
app: String,
in_app_id: String,
acr: String,
auth_time: String*/
}
let mut request_body = String::new();
request_body.push_str("grant_type=exchange_code&exchange_code=".trim());
request_body.push_str(temporary_credentials.exchange_code.as_str().trim());
let url : &str = "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token";
let response = http_client.post(url)
.body(request_body)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization",
"Basic MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=")
.send();
match response {
Ok(response_data) => {
match response_data.json::<ResponseStruct>().ok() {
Some(response) => {
self.access_token = response.access_token;
},
None => {
error!("Failed to parse access_token!");
}
}
},
_ => { error!("Failed to get access token!"); },
}
}
pub fn get_exchange_code(&mut self, http_client: &Client, client_id: &str) {
#[derive(Debug,Deserialize)]
struct ResponseStruct {
expiresInSeconds: u16,
code: String,
creatingClientId: String,
}
let mut bearer_header = String::from("Bearer ");
bearer_header.push_str(self.access_token.as_str());
let mut query_content : String = String::from("?consumingClientId=").trim().to_string();
query_content.push_str(client_id.trim());
let url: &str = "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/exchange";
let response = Client::get(&http_client, url)
//.query(query_content.as_str())
.query(&[("consumingClientId",client_id.trim())])
.header("Authorization", bearer_header.as_str())
.send();
match response {
Ok(response_data) => {
match response_data.json::<ResponseStruct>().ok() {
Some(response_json) => {
self.exchange_code = response_json.code;
}
None => {
error!("Failed to parse Exchange code!");
},
}
let mut bearer = HeaderMap::new();
bearer.insert(
"Authorization",
HeaderValue::from_str(&format!("Bearer {}", access_token.0)).unwrap(),
);
let query_content = [(
"consumingClientId",
match client_type {
ClientType::ANDROID => "34a02cf8f4414e29b15921876da36f9a",
ClientType::PC | ClientType::GENERIC => "ec684b8c687f479fadea3cb2ad83f5c6",
},
_ => { error!("Failed to get Exchange code!"); },
)];
let url = "https://account-public-service-prod.ol.epicgames.com/account/api/oauth/exchange";
let response = http_client
.get(url)
.query(&query_content)
.headers(bearer)
.send();
match response {
Ok(response_data) => match response_data.json::<ResponseStruct>() {
Ok(response_json) => {
return Ok(ExchangeCode(response_json.code));
}
_ => Err(GameAuthenticationError::IllegalResponse(
"Could not parse exchange code".to_string(),
))?,
},
_ => Err(GameAuthenticationError::RequestFailed)?,
}
}
}

View file

@ -1,11 +1,10 @@
mod auth;
use std::process::exit;
use log::*;
use colog;
use log::*;
use std::env::args;
use std::io::Write;
use std::process::Command;
use std::process::{exit, Command};
use crate::auth::*;
@ -13,7 +12,7 @@ fn main() {
/* Initial initializations */
{
colog::init();
// TODO: Set log level to display nothing
// TODO: Set log level to display nothing by default
}
/* Command line arg fetching and handling */
@ -22,8 +21,7 @@ fn main() {
/* Help page */
if command_line_arguments.iter().any(|a| a == "--help") {
// TODO: Complete help message
let help_page : &str =
"
let help_page: &str = "
=== godo-launcher help page===\n
`--verbose`, `-v`, `-vv`\t| Enables debug logging.
`--debug`, `-d`\t\t\t| Enables verbose logging.
@ -31,11 +29,14 @@ fn main() {
";
println!("{help_page}");
std::process::exit(0);
exit(0);
}
/* LogLevel modifiers */
if command_line_arguments.iter().any(|a| a == "--verbose" || a == "-v" || a == "-vv") {
if command_line_arguments
.iter()
.any(|a| a == "--verbose" || a == "-v" || a == "-vv")
{
// TODO: Change Log Level to info and warn
}
@ -43,56 +44,87 @@ fn main() {
warn!("Debug info enabled!");
// TODO: Change Log Level to debug
}
}
/* Authentication Process */
//1. get access token 1 from authcode
//2. get persistent credentials from access token 1
//3. get access token 2 from persistent credentials
//4. get android exchange code from access token and persistent credentials
let http_client_builder = reqwest::blocking::ClientBuilder::new()
.https_only(true)
.user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),));
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION"),
));
let http_client = http_client_builder.build()
let http_client = http_client_builder
.build()
.unwrap_or(reqwest::blocking::Client::new());
// Authentication code from user input
print!("Insert AuthCode > ");
info!("Insert Authentication Code: ");
std::io::stdout().flush().unwrap();
// Getting temporal info to create persistent login info
let mut auth_code: String = "".to_string();
let _ = std::io::stdin().read_line(&mut auth_code).unwrap();
let mut persistent_credentials: DeviceCredentials = DeviceCredentials::new();
persistent_credentials.get_access_token_and_account_id(&http_client, &auth_code);
persistent_credentials.get_device_auth_and_secret(&http_client);
dbg!(&persistent_credentials);
/* Android temporary credentials */
let mut android_credentials: TemporaryCredentials = TemporaryCredentials::new();
android_credentials.get_access_token_from_device_auth(&http_client, &persistent_credentials);
android_credentials.get_exchange_code(&http_client, "34a02cf8f4414e29b15921876da36f9a");
dbg!(&android_credentials);
info!("Generating persistent credentials");
let persistent_credentials = PersistentCredentials::fetch(&http_client, &auth_code).unwrap();
info!("New persistent credentials: {:#?}", &persistent_credentials);
/* Generic temporary credentials */
let mut generic_credentials : TemporaryCredentials = TemporaryCredentials::new();
generic_credentials.get_access_token_from_exchange_code(&http_client, &android_credentials);
generic_credentials.get_exchange_code(&http_client, "ec684b8c687f479fadea3cb2ad83f5c6");
dbg!(&generic_credentials);
// Generating Android access token and exchange code from persistent_credentials
info!("Generating Android exchange code from persistent credentials");
let android_token = AccessToken::from_persistent_credentials(
&http_client,
&persistent_credentials,
ClientType::ANDROID,
)
.unwrap();
info!("Android Access Token: {:#?}", &android_token);
let android_exchange_code = ExchangeCode::from_persistent_credentials(
&http_client,
&android_token,
ClientType::ANDROID,
)
.unwrap();
info!("Android Exchange Code: {:#?}", &android_exchange_code);
// Generating generic access_token and exchange code from android exchange code
info!("Generating generic access token");
let generic_access_token =
AccessToken::from_exchange_code(&http_client, &android_exchange_code).unwrap();
info!("Generic Access Token: {:#?}", &generic_access_token);
info!("Generating generic exchange code");
let generic_exchange_code = ExchangeCode::from_persistent_credentials(
&http_client,
&generic_access_token,
ClientType::GENERIC,
)
.unwrap();
info!("Generated exchange code: {:#?}", &generic_exchange_code);
/* Game Launching*/
info!("Starting game...");
let mut auth_password_argument = String::from("-AUTH_PASSWORD=");
auth_password_argument.push_str(generic_credentials.exchange_code.as_str());
auth_password_argument.push_str(generic_exchange_code.0.as_str());
let mut uid_argument = String::from("-epicuserid=");
uid_argument.push_str(persistent_credentials.account_id.as_str());
let command = Command::new("cmd")
.arg("/C") // '/C' executes the command and terminates the command shell
.arg("/C") // '/C' executes the command and terminates the command shell
.arg("start")
.arg("/d")
.arg("D:\\Games\\Epic Games\\Fortnite\\FortniteGame\\Binaries\\Win64") // Path to the directory
.arg("D:\\Games\\Epic Games\\Fortnite\\FortniteGame\\Binaries\\Win64") // Path to the directory
.arg("FortniteLauncher.exe") // The executable
.arg("-AUTH_LOGIN=unused")
.arg(&auth_password_argument)
@ -104,32 +136,15 @@ fn main() {
.arg(&uid_argument)
.arg("-epicsandboxid=fn")
.spawn();
/*
"D:\Games\Epic Games\Fortnite\FortniteGame\Binaries\Win64/FortniteClient-Win64-Shipping_EAC_EOS.exe"
-obfuscationid=N8Kw52kUZsQq50886Eit-gzJOBar1g
-AUTH_LOGIN=unused -AUTH_PASSWORD=91ec3f72c2d94c8598082d58fc007a02
-AUTH_TYPE=exchangecode
-epicapp=Fortnite
-epicenv=Prod
-EpicPortal
-epicusername=Generic_Boi69
-epicuserid=bff1ee7d635140ed945f69a0595526b2
-epiclocale=en
-epicsandboxid=fn
-named_pipe=bff1ee7d635140ed945f69a0595526b2\Fortnite
-fromfl=eaceos
-caldera=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50X2lkIjoiYmZmMWVlN2Q2MzUxNDBlZDk0NWY2OWEwNTk1NTI2YjIiLCJnZW5lcmF0ZWQiOjE3MzQ1NDMwNDYsImNhbGRlcmFHdWlkIjoiOTUyZmU2OTktZjJjMy00YjZlLTk2NzctOWRlMDMyOTcyZjkxIiwiYWNQcm92aWRlciI6IkVhc3lBbnRpQ2hlYXRFT1MiLCJub3RlcyI6IiIsInByZSI6dHJ1ZSwicGFlIjpmYWxzZSwiZmFsbGJhY2siOmZhbHNlfQ.SaiRyK-FFbzCI3TVM8RRFS0UyCu5VUsTlIMeNKPZHYcKUORdE7fZJlo0DC4zoZsPfmLNEzZxCLb_sJVPiy-m7A
*/
match command {
Ok(mut child) => {
// Optionally, you can wait for the process to complete
let status = child.wait().expect("Failed to wait on child");
println!("Command executed with status: {}", status);
info!("Command executed with status: {}", status);
}
Err(e) => {
eprintln!("Error executing command: {}", e);
error!("Error executing command: {}", e);
exit(1);
}
}