Skip to content

Commit

Permalink
Get rid of onboarding + New Launcher Auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometrically committed Jan 11, 2024
1 parent ef8b525 commit c10477a
Show file tree
Hide file tree
Showing 31 changed files with 182 additions and 3,697 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion theseus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ flate2 = "1.0.27"
tempfile = "3.5.0"
urlencoding = "2.1.3"


chrono = { version = "0.4.19", features = ["serde"] }
daedalus = { version = "0.1.25" }
dirs = "5.0.1"
Expand All @@ -50,6 +49,7 @@ reqwest = { version = "0.11", features = ["json", "stream"] }
tokio = { version = "1", features = ["full"] }
tokio-stream = { version = "0.1", features = ["fs"] }
async-recursion = "1.0.4"
tiny_http = "0.12.0"

notify = { version = "5.1.0", default-features = false }
notify-debouncer-mini = { version = "0.2.1", default-features = false }
Expand Down
4 changes: 2 additions & 2 deletions theseus/src/api/auth.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Authentication flow interface
use crate::{
hydra::{self, init::DeviceLoginSuccess},
hydra::{self, init::AuthInit},
launcher::auth as inner,
State,
};
Expand All @@ -14,7 +14,7 @@ pub use inner::Credentials;
/// This can be used in conjunction with 'authenticate_await_complete_flow'
/// to call authenticate and call the flow from the frontend.
/// Visit the URL in a browser, then call and await 'authenticate_await_complete_flow'.
pub async fn authenticate_begin_flow() -> crate::Result<DeviceLoginSuccess> {
pub async fn authenticate_begin_flow() -> crate::Result<AuthInit> {
let url = AuthTask::begin_auth().await?;
Ok(url)
}
Expand Down
4 changes: 2 additions & 2 deletions theseus/src/api/hydra/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ pub struct SuccessfulLogin {
pub expires_after: i64,
}

pub async fn wait_finish(device_code: String) -> crate::Result<Credentials> {
pub async fn wait_finish() -> crate::Result<Credentials> {
// Loop, polling for response from Microsoft
let oauth = poll_response::poll_response(device_code).await?;
let oauth = poll_response::poll_response().await?;

// Get xbl token from oauth token
let xbl_token = xbl_signin::login_xbl(&oauth.access_token).await?;
Expand Down
48 changes: 11 additions & 37 deletions theseus/src/api/hydra/init.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,21 @@
//! Login route for Hydra, redirects to the Microsoft login page before going to the redirect route
use serde::{Deserialize, Serialize};

use crate::{hydra::MicrosoftError, util::fetch::REQWEST_CLIENT};
use super::{MICROSOFT_CLIENT_ID, REDIRECT_URL, REQUESTED_SCOPES};

use super::{stages::auth_retry, MICROSOFT_CLIENT_ID};

#[derive(Serialize, Deserialize, Debug)]
pub struct DeviceLoginSuccess {
pub device_code: String,
pub user_code: String,
#[derive(Serialize, Deserialize)]
pub struct AuthInit {
pub verification_uri: String,
pub expires_in: u64,
pub interval: u64,
pub message: String,
}

pub async fn init() -> crate::Result<DeviceLoginSuccess> {
// Get the initial URL
// Get device code
// Define the parameters

// urlencoding::encode("XboxLive.signin offline_access"));
let resp = auth_retry(|| REQWEST_CLIENT.get("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode")
.header("Content-Length", "0")
.query(&[
("client_id", MICROSOFT_CLIENT_ID),
(
"scope",
"XboxLive.signin XboxLive.offline_access profile openid email",
),
])
.send()
).await?;
pub async fn init() -> AuthInit {
let url = format!(
"https://login.live.com/oauth20_authorize.srf?client_id={MICROSOFT_CLIENT_ID}&response_type=code&redirect_uri={}&scope={}&prompt=select_account&cobrandid=8058f65d-ce06-4c30-9559-473c9275a65d",
urlencoding::encode(REDIRECT_URL),
urlencoding::encode(REQUESTED_SCOPES),
);

match resp.status() {
reqwest::StatusCode::OK => Ok(resp.json().await?),
_ => {
let microsoft_error = resp.json::<MicrosoftError>().await?;
Err(crate::ErrorKind::HydraError(format!(
"Error from Microsoft: {:?}",
microsoft_error.error_description
))
.into())
}
AuthInit {
verification_uri: url,
}
}
6 changes: 6 additions & 0 deletions theseus/src/api/hydra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ pub(crate) mod stages;
use serde::Deserialize;

const MICROSOFT_CLIENT_ID: &str = "c4502edb-87c6-40cb-b595-64a280cf8906";
// TODO: figure out what to do with this
const MICROSOFT_CLIENT_SECRET: &str = "TODOFIGUREOUT";

const REDIRECT_URL: &str = "http://localhost:20123/theseus/callback";
const REQUESTED_SCOPES: &str =
"XboxLive.signin XboxLive.offline_access profile openid email";

#[derive(Deserialize)]
pub struct MicrosoftError {
Expand Down
110 changes: 76 additions & 34 deletions theseus/src/api/hydra/stages/poll_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ use std::collections::HashMap;

use reqwest::StatusCode;
use serde::Deserialize;
use tiny_http::Response;

use crate::{
hydra::{MicrosoftError, MICROSOFT_CLIENT_ID},
hydra::{
MicrosoftError, MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET,
REDIRECT_URL, REQUESTED_SCOPES,
},
util::fetch::REQWEST_CLIENT,
};

Expand All @@ -20,26 +24,71 @@ pub struct OauthSuccess {
}

#[tracing::instrument]
pub async fn poll_response(device_code: String) -> crate::Result<OauthSuccess> {
let mut params = HashMap::new();
params.insert("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
params.insert("client_id", MICROSOFT_CLIENT_ID);
params.insert("device_code", &device_code);
params.insert(
"scope",
"XboxLive.signin XboxLive.offline_access profile openid email",
);

// Poll the URL in a loop until we are successful.
// On an authorization_pending response, wait 5 seconds and try again.
pub async fn poll_response() -> crate::Result<OauthSuccess> {
let state = crate::state::State::get().await?;
let server = state.auth_flow.http_server.read().await;

loop {
// blocks until the next request is received
let request = match server
.as_ref()
.ok_or_else(|| {
crate::ErrorKind::HydraError(
"Could not aqquire HTTP server".to_string(),
)
})?
.recv()
{
Ok(rq) => rq,
Err(e) => {
tracing::warn!("server request error: {}", e);
continue;
}
};

let url = match url::Url::parse(&format!(
"http://localhost:20123{}",
request.url()
)) {
Ok(val) => val,
Err(err) => {
tracing::warn!("error parsing uri: {err}");
continue;
}
};

if url.path() != "/theseus/callback" {
tracing::warn!("wrong URI path: {}", url.path());

continue;
}

let query = url
.query_pairs()
.collect::<HashMap<std::borrow::Cow<str>, std::borrow::Cow<str>>>();

let code = match query.get("code") {
Some(val) => val,
None => {
tracing::warn!("missing response code");
continue;
}
};

let mut map = HashMap::new();
map.insert("client_id", MICROSOFT_CLIENT_ID);
map.insert("client_secret", MICROSOFT_CLIENT_SECRET);
map.insert("code", &**code);
map.insert("grant_type", "authorization_code");
map.insert("redirect_uri", REDIRECT_URL);
map.insert("scope", REQUESTED_SCOPES);

let resp = auth_retry(|| {
REQWEST_CLIENT
.post(
"https://login.microsoftonline.com/consumers/oauth2/v2.0/token",
)
.form(&params)
.send()
.post("https://login.live.com/oauth20_token.srf")
.header(reqwest::header::ACCEPT, "application/json")
.form(&map)
.send()
})
.await?;

Expand All @@ -52,9 +101,18 @@ pub async fn poll_response(device_code: String) -> crate::Result<OauthSuccess> {
err
))
})?;

let response = Response::from_string("Microsoft login succeeded. Loading your Minecraft account...");
request.respond(response).ok();

return Ok(oauth);
}
_ => {
let response = Response::from_string(
"Authentication failed. Please try again.",
);
request.respond(response).ok();

let failure =
resp.json::<MicrosoftError>().await.map_err(|err| {
crate::ErrorKind::HydraError(format!(
Expand All @@ -63,28 +121,12 @@ pub async fn poll_response(device_code: String) -> crate::Result<OauthSuccess> {
))
})?;
match failure.error.as_str() {
"authorization_pending" => {
tokio::time::sleep(std::time::Duration::from_secs(2))
.await;
}
"authorization_declined" => {
return Err(crate::ErrorKind::HydraError(
"Authorization declined".to_string(),
)
.as_error());
}
"expired_token" => {
return Err(crate::ErrorKind::HydraError(
"Device code expired".to_string(),
)
.as_error());
}
"bad_verification_code" => {
return Err(crate::ErrorKind::HydraError(
"Invalid device code".to_string(),
)
.as_error());
}
_ => {
return Err(crate::ErrorKind::HydraError(format!(
"Unknown error: {}",
Expand Down
3 changes: 1 addition & 2 deletions theseus/src/launcher/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ pub fn get_jvm_arguments(
parsed_arguments.push(arg);
}
}
parsed_arguments.push("-Dorg.lwjgl.util.Debug=true".to_string());

Ok(parsed_arguments)
}
Expand Down Expand Up @@ -275,7 +274,7 @@ fn parse_minecraft_argument(
.replace("${auth_xuid}", "0")
.replace("${auth_uuid}", &uuid.simple().to_string())
.replace("${uuid}", &uuid.simple().to_string())
.replace("${clientid}", "c4502edb-87c6-40cb-b595-64a280cf8906")
.replace("${clientid}", "c4502edb-c6-40cb-b595-64a280cf8906")
.replace("${user_properties}", "{}")
.replace("${user_type}", "msa")
.replace("${version_name}", version)
Expand Down
Loading

0 comments on commit c10477a

Please sign in to comment.