diff --git a/Cargo.toml b/Cargo.toml index a527951..82d18c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] colog = "1.3.0" log = "0.4.22" -reqwest = { version = "0.12", features = ["json"] } -tokio = { version = "1", features = ["full"] } +reqwest = { version = "0.12", features = ["blocking", "json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..88af090 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,193 @@ +use reqwest::blocking::*; +use log::*; +use serde::Deserialize; + +const OAUTH_TOKEN : (&str, &str)= ( + "3f69e56c7649492c8cc29f1af08a8a12", // Client + "b51ee9cb12234f50a69efa67ef53812e", // Secret +); + +#[derive(Debug)] +pub struct DeviceCredentials { + access_token : String, + account_id : String, + device_id : String, +} +impl DeviceCredentials { + pub fn new() -> DeviceCredentials { + DeviceCredentials{ + device_id: String::new(), + account_id: String::new(), + access_token: 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, + 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::().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!"); }, + } + } + + pub fn get_device_auth(&mut self, http_client: &Client) { + + if self.access_token.is_empty() || self.account_id.is_empty() { + error!("Device access token cannot be empty!"); + return; + } + + 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"); + + /* + let it : String = String::new; + it.starts_with("one thing"); + // Just discovered String.starts_with() KEKW + */ + + #[derive(Debug, Deserialize)] + struct CreatedObject { + location: String, + ipAddress: String, + dateTime: String, + } + + #[derive(Debug, Deserialize)] + struct ResponseStruct { + deviceId: String, + accountId: String, + secret: String, + userAgent: String, + created: CreatedObject, + } + + let mut bearer_header = String::from("Bearer "); + bearer_header.push_str(self.access_token.as_str()); + + let response = Client::post(&http_client, url) + .header("Authorization", bearer_header.as_str()) + .send(); + + match response { + Ok(response_data) => { + match response_data.json::().ok() { + Some(response_json) => { + self.device_id = response_json.deviceId; + } + None => { + error!("Failed to parse device_id!"); + }, + } + }, + _ => { error!("Failed to get device_id!"); }, + } + } + pub fn get_device_auth_from(&mut self, access_token: &str, account_id: &str) { + + if access_token.is_empty() || account_id.is_empty() { + error!("Device access token cannot be empty!"); + return; + } + + let mut url : String = String::from("https://account-public-service-prod.ol.epicgames.com/account/api/public/account/"); + url.push_str(account_id.as_str()); + url.push_str("/deviceAuth"); + + /* + let it : String = String::new; + it.starts_with("one thing"); + // Just discovered String.starts_with() KEKW + */ + + #[derive(Debug, Deserialize)] + struct CreatedObject { + location: String, + ipAddress: String, + dateTime: String, + } + + #[derive(Debug, Deserialize)] + struct ResponseStruct { + deviceId: String, + accountId: String, + secret: String, + userAgent: String, + created: CreatedObject, + } + + let mut bearer_header = String::from("Bearer "); + bearer_header.push_str(access_token.as_str()); + + let response = Client::post(&http_client, url) + .header("Authorization", bearer_header.as_str()) + .send(); + + match response { + Ok(response_data) => { + match response_data.json::().ok() { + Some(response_json) => { + self.device_id = response_json.deviceId; + } + None => { + error!("Failed to parse device_id!"); + }, + } + }, + _ => { error!("Failed to get device_id!"); }, + } + } + + pub fn get_exchange_code() { + todo!(); + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..cf998cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,68 @@ +mod auth; + +use log::*; +use colog; +use std::env::args; +use std::io::Write; +use crate::auth::DeviceCredentials; + fn main() { - println!("Hello, world!"); + /* Initial initializations */ + { + colog::init(); + // TODO: Set log level to display nothing + } + + /* Command line arg fetching and handling */ + let command_line_arguments: Vec = args().collect(); + { + /* Help page */ + if command_line_arguments.iter().any(|a| a == "--help") { + // TODO: Complete help message + let help_page : &str = +" +=== godo-launcher help page===\n +`--verbose`, `-v`, `-vv`\t| Enables debug logging. +`--debug`, `-d`\t\t\t| Enables verbose logging. +`--help`, `-h`\t\t\t| Prints this page. +"; + + println!("{help_page}"); + std::process::exit(0); + } + + /* LogLevel modifiers */ + if command_line_arguments.iter().any(|a| a == "--verbose" || a == "-v" || a == "-vv") { + // TODO: Change Log Level to info and warn + } + + if command_line_arguments.iter().any(|a| a == "--debug") { + warn!("Debug info enabled!"); + // TODO: Change Log Level to debug + } + + } + + /* Authentication Process */ + + let http_client_builder = reqwest::blocking::ClientBuilder::new() + .https_only(true) + .user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),)); + + let http_client = http_client_builder.build().unwrap_or(reqwest::blocking::Client::new()); + + // Authentication code from user input + print!("Insert AuthCode > "); + std::io::stdout().flush().unwrap(); + + let mut auth_code: String = "".to_string(); + let _ = std::io::stdin().read_line(&mut auth_code).unwrap(); + + let mut credentials : DeviceCredentials = DeviceCredentials::new(); + credentials.get_access_token_and_account_id(&http_client, &auth_code); + credentials.get_device_auth(&http_client); + dbg!(&credentials); + + /* Game Launching*/ + }