🔨 chore: explain mc auth flow
This commit is contained in:
parent
4539cb5388
commit
a73f4b0397
8 changed files with 27 additions and 135 deletions
|
|
@ -26,8 +26,8 @@ All rights reserved. Copyright (C) 2022~2024 Polyfrost
|
|||
|
||||
This includes, but may not be limited to, the following files:
|
||||
|
||||
* [`apps/desktop/.icons/*`](./apps/desktop/.icons/)
|
||||
* [`apps/desktop/src/assets/licensed/*`](./apps/desktop/src/assets/licensed/)
|
||||
* [`packages/distribution/icons/*`](./packages/distribution/icons/)
|
||||
* [`apps/frontend/src/assets/licensed/*`](./apps/frontend/src/assets/licensed/)
|
||||
|
||||
## Additional Libraries
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ fn main() {
|
|||
"onelauncher",
|
||||
tauri_build::InlinedPlugin::new().commands(&[
|
||||
// User
|
||||
"msa_auth",
|
||||
"begin_msa",
|
||||
"finish_msa",
|
||||
"get_users",
|
||||
"get_user",
|
||||
"remove_user",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
description = "Default permissions for the plugin"
|
||||
permissions = [
|
||||
# User
|
||||
"allow-msa-auth",
|
||||
"allow-begin-msa",
|
||||
"allow-finish-msa",
|
||||
"allow-get-users",
|
||||
"allow-get-user",
|
||||
"allow-remove-user",
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@ use interpulse::api::minecraft::Version;
|
|||
use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION};
|
||||
use onelauncher::data::{Loader, ManagedPackage, MinecraftCredentials, PackageData, Settings};
|
||||
use onelauncher::package::content;
|
||||
use onelauncher::store::{Cluster, ClusterPath};
|
||||
use onelauncher::store::{Cluster, ClusterPath, MinecraftLogin};
|
||||
use onelauncher::{cluster, minecraft, processor, settings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[macro_export]
|
||||
|
|
@ -23,7 +21,8 @@ macro_rules! collect_commands {
|
|||
)
|
||||
.commands(tauri_specta::collect_commands![
|
||||
// User
|
||||
msa_auth,
|
||||
begin_msa,
|
||||
finish_msa,
|
||||
get_users,
|
||||
get_user,
|
||||
remove_user,
|
||||
|
|
@ -215,13 +214,14 @@ pub async fn get_user(uuid: Uuid) -> Result<MinecraftCredentials, String> {
|
|||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn msa_auth(handle: AppHandle) -> Result<MinecraftCredentials, String> {
|
||||
let login = minecraft::begin().await?;
|
||||
handle.shell().open(login.redirect_uri.clone(), None).map_err(|err| err.to_string())?;
|
||||
pub async fn begin_msa() -> Result<MinecraftLogin, String> {
|
||||
Ok(minecraft::begin().await?)
|
||||
}
|
||||
|
||||
Err("not implemented".to_string())
|
||||
|
||||
// Ok(minecraft::finish(code.as_str(), login).await?)
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn finish_msa(code: String, login: MinecraftLogin) -> Result<MinecraftCredentials, String> {
|
||||
Ok(minecraft::finish(code.as_str(), login).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
|
|
|
|||
|
|
@ -17,13 +17,21 @@ async fn main() -> onelauncher::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// pauline's epic gamer minecraft auth flow
|
||||
/// opens a browser tab in which the user sees xbox login and logs in with microsoft and gets redirected
|
||||
/// to a new url. this new url contains the ?code=.... parameter that we need to finish authentication.
|
||||
/// in this testing environment we can just copy the final url from our browser and into the testing console
|
||||
/// and it will parse the url and find the code and complete the authenticaiton process.
|
||||
/// in production/frontend, it works the same way but we get the url and code parameter automatically.
|
||||
pub async fn authenticate_mc() -> onelauncher::Result<MinecraftCredentials> {
|
||||
// begins login flow, opens browser
|
||||
println!("a browser will open, follow login flow");
|
||||
let login = minecraft::begin().await?;
|
||||
|
||||
println!("url is {}", login.redirect_uri.as_str());
|
||||
webbrowser::open(login.redirect_uri.as_str())?;
|
||||
|
||||
// after user is done with auth, paste the final code into console below this line
|
||||
println!("enter flow url: ");
|
||||
let mut input = String::new();
|
||||
std::io::stdin()
|
||||
|
|
@ -32,6 +40,7 @@ pub async fn authenticate_mc() -> onelauncher::Result<MinecraftCredentials> {
|
|||
|
||||
println!("{}", input.trim());
|
||||
|
||||
// parses the url code query param using the same logic we should use in frontend
|
||||
let parsed = url::Url::parse(input.trim()).expect("idk");
|
||||
let code = if let Some((_, code)) = parsed.query_pairs().find(|x| x.0 == "code") {
|
||||
let code = code.clone();
|
||||
|
|
@ -41,6 +50,7 @@ pub async fn authenticate_mc() -> onelauncher::Result<MinecraftCredentials> {
|
|||
};
|
||||
let creds = minecraft::finish(code.as_str(), login).await?;
|
||||
|
||||
// mc auth flow is complete and added to state
|
||||
println!("logged in {}", creds.username);
|
||||
Ok(creds)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ pub const DUMMY_REPLACE_NEWLINE: &str = "\n";
|
|||
pub const DISCORD_RPC_CLIENT_ID: &str = "1234567890000000"; // todo
|
||||
/// Our Microsoft client ID.
|
||||
pub const MICROSOFT_CLIENT_ID: &str = "9eac3a4e-8cdd-43ef-863e-49cd601b1f03";
|
||||
/// The port to run the localhost server on.
|
||||
pub const MSA_SERVER_PORT: u16 = 13523;
|
||||
/// Mojang/Microsoft client ID.
|
||||
pub const MINECRAFT_CLIENT_ID: &str = "00000000402b5328";
|
||||
/// Mojang/Microsoft login redirect URI.
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OneLauncher</title>
|
||||
<style>
|
||||
* {
|
||||
font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Ubuntu, Cantarell, sans-serif;
|
||||
}
|
||||
body {
|
||||
opacity: 0;
|
||||
animation: fadeIn 1s forwards 0.3s;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80vh;
|
||||
font-size: 24px;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>%TITLE%</h1>
|
||||
<p>%CONTENT%</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -310,89 +310,6 @@ impl MinecraftState {
|
|||
}
|
||||
}
|
||||
|
||||
// pub struct LocalServer {
|
||||
// listener: TcpListener,
|
||||
// sender: tokio::sync::mpsc::Sender<bool>,
|
||||
// receiver: tokio::sync::mpsc::Receiver<bool>,
|
||||
// }
|
||||
|
||||
// impl LocalServer {
|
||||
// pub async fn new() -> crate::Result<Self> {
|
||||
// let listener = TcpListener::bind(format!("127.0.0.1:{}", constants::MSA_SERVER_PORT)).await?;
|
||||
// let (sender, receiver) = tokio::sync::mpsc::channel::<bool>(1);
|
||||
|
||||
// Ok(Self {
|
||||
// listener,
|
||||
// sender,
|
||||
// receiver,
|
||||
// })
|
||||
// }
|
||||
|
||||
// pub async fn wait_for_code(&self) -> crate::Result<String> {
|
||||
|
||||
// }
|
||||
|
||||
// async fn handle_request(&self, stream: &mut TcpStream) -> crate::Result<String> {
|
||||
// use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
// let code: Option<String>;
|
||||
|
||||
// let code_result = {
|
||||
// let mut buf = vec![0; 1024];
|
||||
// let size = stream.read(&mut buf).await?;
|
||||
// if size == 0 {
|
||||
// return Err(MinecraftAuthError::MSACodeFetchError.into());
|
||||
// }
|
||||
|
||||
// let request = String::from_utf8_lossy(&buf[..size]);
|
||||
// code = Some(
|
||||
// request
|
||||
// .split(' ')
|
||||
// .find(|x| x.starts_with("code="))
|
||||
// .ok_or(anyhow::anyhow!("No code found"))?
|
||||
// .split('=')
|
||||
// .nth(1)
|
||||
// .ok_or(anyhow::anyhow!("No code found"))?
|
||||
// .to_string()
|
||||
// );
|
||||
|
||||
// Ok::<(), String>(())
|
||||
// };
|
||||
|
||||
// let errored = code_result.is_err();
|
||||
// let html = include_str!("./auth_server.html");
|
||||
// let result = match code_result {
|
||||
// Ok(()) => {
|
||||
// html
|
||||
// .replace("%TITLE%", "Successfully Authenticated")
|
||||
// .replace("%CONTENT%", "You can now close this tab.")
|
||||
// }
|
||||
// Err(e) => {
|
||||
// html
|
||||
// .replace("%TITLE%", "Error")
|
||||
// .replace("%CONTENT%", &format!("An error has occurred: {}", e.as_str()))
|
||||
// }
|
||||
// };
|
||||
|
||||
// let response = format!(
|
||||
// "HTTP/1.1 {}\r\nContent-Length: {}\r\n\r\n{}",
|
||||
// if errored { "400 Bad Request" } else { "200 OK" },
|
||||
// result.len(),
|
||||
// result
|
||||
// );
|
||||
|
||||
// stream.write_all(response.as_bytes()).await?;
|
||||
|
||||
// if errored {
|
||||
// return Err(MinecraftAuthError::MSACodeFetchError.into());
|
||||
// }
|
||||
|
||||
// match code {
|
||||
// Some(code) => Ok(code),
|
||||
// None => Err(MinecraftAuthError::MSACodeFetchError.into()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A [`reqwest::Request`] with a [`DateTime<Utc>`] attached to it.
|
||||
struct RequestWithDate<T> {
|
||||
pub date: DateTime<Utc>,
|
||||
|
|
@ -1117,7 +1034,5 @@ pub enum MinecraftAuthError {
|
|||
#[error("failed to read user hash")]
|
||||
HashError,
|
||||
#[error("failed to read user xbox session ID")]
|
||||
SessionIdError,
|
||||
#[error("failed to read user MSA code")]
|
||||
MSACodeFetchError,
|
||||
SessionIdError
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue