Skip to content

Commit

Permalink
feat: OAuth and Donation Perks (#309)
Browse files Browse the repository at this point in the history
* OAuth Authentication
* Donation Perks (Skip advertisement)

---------

Co-authored-by: Senk Ju <18741573+SenkJu@users.noreply.github.com>
  • Loading branch information
1zun4 and SenkJu authored Jul 1, 2024
1 parent 0e06583 commit 51094e2
Show file tree
Hide file tree
Showing 19 changed files with 801 additions and 426 deletions.
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
"tauri": "tauri"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0",
"@tauri-apps/cli": "^1.5",
"svelte": "^4.2",
"vite": "^5.0",
"@sveltejs/vite-plugin-svelte": "^3.0"
"vite": "^5.0"
},
"dependencies": {
"@tauri-apps/api": "^1.5",
"jwt-decode": "^4.0.0",
"nouislider": "^15.7"
}
}
33 changes: 33 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ toml = "0.7.2"
azalea-auth = { git = "https://github.com/CCBlueX/azalea.git", branch = "custom_auth", package = "azalea-auth" }

md5 = "0.7.0"
oauth2 = "5.0.0-alpha.4"

[features]
# by default Tauri runs in production mode
Expand Down
41 changes: 39 additions & 2 deletions src-tauri/src/app/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use serde::de::DeserializeOwned;

use crate::auth::ClientAccount;
use crate::minecraft::java::JavaDistribution;
use crate::HTTP_CLIENT;
use crate::utils::get_maven_artifact_path;

/// API endpoint url
pub const LAUNCHER_API: &str = "https://api.liquidbounce.net";
pub const LAUNCHER_API_VERSION: &str = "api/v1";
pub const API_V1: &str = "api/v1";
pub const API_V3: &str = "api/v3";

pub const CONTENT_DELIVERY: &str = "https://cloud.liquidbounce.net";
pub const CONTENT_FOLDER: &str = "LiquidLauncher";
Expand Down Expand Up @@ -122,9 +124,28 @@ impl ApiEndpoints {
Self::request_from_endpoint(&format!("version/changelog/{}", build_id)).await
}

/// Resolve direct download link from skip file pid
pub async fn user(client_account: &ClientAccount) -> Result<UserInformation> {
Self::request_with_client_account("oauth/user", client_account).await
}

/// Resolve direct download link from skip file pid
pub async fn resolve_skip_file(client_account: &ClientAccount, pid: &str) -> Result<SkipFileResolve> {
Self::request_with_client_account(&format!("file/resolve/{}", pid), client_account).await
}

/// Request JSON formatted data from launcher API
pub async fn request_from_endpoint<T: DeserializeOwned>(endpoint: &str) -> Result<T> {
Ok(HTTP_CLIENT.get(format!("{}/{}/{}", LAUNCHER_API, LAUNCHER_API_VERSION, endpoint))
Ok(HTTP_CLIENT.get(format!("{}/{}/{}", LAUNCHER_API, API_V1, endpoint))
.send().await?
.error_for_status()?
.json::<T>()
.await?
)
}

pub async fn request_with_client_account<T: DeserializeOwned>(endpoint: &str, client_account: &ClientAccount) -> Result<T> {
Ok(client_account.authenticate_request(HTTP_CLIENT.get(format!("{}/{}/{}", LAUNCHER_API, API_V3, endpoint)))?
.send().await?
.error_for_status()?
.json::<T>()
Expand Down Expand Up @@ -258,3 +279,19 @@ pub enum LoaderSubsystem {
#[serde(rename = "forge")]
Forge { manifest: String, mod_directory: String },
}

#[derive(Deserialize, Serialize, Debug)]
pub struct SkipFileResolve {
pub error: bool,
pub msg: String,
pub target_pid: Option<String>,
pub download_url: Option<String>,
pub direct_url: Option<String>
}

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct UserInformation {
#[serde(rename = "userId", alias = "user_id")]
pub user_id: String,
pub premium: bool,
}
8 changes: 7 additions & 1 deletion src-tauri/src/app/app_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::{path::Path, collections::HashMap};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use tokio::fs;
use crate::minecraft::auth::MinecraftAccount;
use crate::{auth::ClientAccount, minecraft::auth::MinecraftAccount};

fn default_concurrent_downloads() -> i32 {
10
Expand All @@ -44,6 +44,10 @@ pub(crate) struct LauncherOptions {
pub selected_branch: Option<String>,
#[serde(rename = "selectedBuild")]
pub selected_build: Option<i32>,
#[serde(rename = "clientAccount")]
pub client_account: Option<ClientAccount>,
#[serde(rename = "skipAdvertisement", default)]
pub skip_advertisement: bool,
#[serde(rename = "currentAccount")]
pub current_account: Option<MinecraftAccount>,
#[serde(rename = "branchOptions", default)]
Expand Down Expand Up @@ -86,8 +90,10 @@ impl Default for LauncherOptions {
custom_java_path: String::new(),
selected_branch: None,
selected_build: None,
client_account: None,
current_account: None,
branch_options: HashMap::new(),
skip_advertisement: false,
concurrent_downloads: 10
}
}
Expand Down
47 changes: 44 additions & 3 deletions src-tauri/src/app/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use tracing::{error, info, debug};
use tauri::{Manager, Window};
use uuid::Uuid;

use crate::{LAUNCHER_DIRECTORY, minecraft::{launcher::{LauncherData, LaunchingParameter}, prelauncher, progress::ProgressUpdate, auth::{MinecraftAccount, self}}, HTTP_CLIENT, LAUNCHER_VERSION};
use crate::{auth::{ClientAccountAuthenticator, ClientAccount}, minecraft::{auth::{self, MinecraftAccount}, launcher::{LauncherData, LaunchingParameter}, prelauncher, progress::ProgressUpdate}, HTTP_CLIENT, LAUNCHER_DIRECTORY, LAUNCHER_VERSION};
use crate::app::api::{Branches, Changelog, ContentDelivery, News};
use crate::utils::percentage_of_total_memory;

Expand Down Expand Up @@ -138,6 +138,31 @@ async fn login_microsoft(window: tauri::Window) -> Result<MinecraftAccount, Stri
Ok(account)
}

#[tauri::command]
async fn client_account_authenticate(window: tauri::Window) -> Result<ClientAccount, String> {
let mut account = ClientAccountAuthenticator::start_auth(|uri| {
// Open the browser with the auth URL
let _ = window.emit("auth_url", uri);
}).await.map_err(|e| format!("{}", e))?;

// Fetch user information
account.update_info().await
.map_err(|e| format!("unable to fetch user information: {:?}", e))?;

Ok(account)
}

#[tauri::command]
async fn client_account_update(account: ClientAccount) -> Result<ClientAccount, String> {
let mut account = account.renew().await
.map_err(|e| format!("unable to update access token: {:?}", e))?;

// Fetch user information
account.update_info().await
.map_err(|e| format!("unable to fetch user information: {:?}", e))?;
Ok(account)
}

#[tauri::command]
async fn get_custom_mods(branch: &str, mc_version: &str) -> Result<Vec<LoaderMod>, String> {
let data = LAUNCHER_DIRECTORY.data_dir();
Expand Down Expand Up @@ -248,15 +273,27 @@ fn handle_log(window: &ShareableWindow, msg: &str) -> anyhow::Result<()> {
}

#[tauri::command]
async fn run_client(build_id: u32, account_data: MinecraftAccount, options: LauncherOptions, mods: Vec<LoaderMod>, window: Window, app_state: tauri::State<'_, AppState>) -> Result<(), String> {
async fn run_client(
build_id: u32,
options: LauncherOptions,
mods: Vec<LoaderMod>,
window: Window,
app_state: tauri::State<'_, AppState>
) -> Result<(), String> {
// A shared mutex for the window object.
let shareable_window: ShareableWindow = Arc::new(Mutex::new(window));

let (account_name, uuid, token, user_type) = match account_data {
let minecraft_account = options.current_account.ok_or("no account selected")?;
let (account_name, uuid, token, user_type) = match minecraft_account {
MinecraftAccount::MsaAccount { msa: _, xbl: _, mca, profile, .. } => (profile.name, profile.id.to_string(), mca.data.access_token, "msa".to_string()),
MinecraftAccount::LegacyMsaAccount { name, uuid, token, .. } => (name, uuid.to_string(), token, "msa".to_string()),
MinecraftAccount::OfflineAccount { name, id, .. } => (name, id.to_string(), "-".to_string(), "legacy".to_string())
};

let client_account = options.client_account;
let skip_advertisement = options.skip_advertisement && client_account.as_ref().is_some_and(|x|
x.get_user_information().is_some_and(|u| u.premium)
);

// Random XUID
let xuid = Uuid::new_v4().to_string();
Expand All @@ -273,6 +310,8 @@ async fn run_client(build_id: u32, account_data: MinecraftAccount, options: Laun
user_type,
keep_launcher_open: options.keep_launcher_open,
concurrent_downloads: options.concurrent_downloads,
client_account,
skip_advertisement: skip_advertisement
};

let runner_instance = &app_state.runner_instance;
Expand Down Expand Up @@ -459,6 +498,8 @@ pub fn gui_main() {
run_client,
login_offline,
login_microsoft,
client_account_authenticate,
client_account_update,
logout,
refresh,
fetch_news,
Expand Down
Loading

0 comments on commit 51094e2

Please sign in to comment.