From 5fd29cf69032f0de9cbb111d17a5e218195a75c8 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Tue, 18 Mar 2025 00:31:00 -0500 Subject: wip: account login progress --- ozone-cli/src/main.rs | 26 +++++++++++++++++++++++--- ozone/src/auth.rs | 43 +++++++++++++++++++++++++++++++++---------- ozone/src/util/progress.rs | 6 +++--- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/ozone-cli/src/main.rs b/ozone-cli/src/main.rs index 05fef49..d4b70a5 100644 --- a/ozone-cli/src/main.rs +++ b/ozone-cli/src/main.rs @@ -377,7 +377,12 @@ async fn main_inner(cli: Cli, multi: MultiProgress) -> Result (), Err(e) => match e.kind() { AuthErrorKind::NotOnXbox => { @@ -405,6 +410,8 @@ async fn main_inner(cli: Cli, multi: MultiProgress) -> Result Result { - msa_acct.log_in_silent(&client).await?; + msa_acct.log_in_silent(&client, &progress as &dyn ProgressIndication).await?; println!("Successfully refreshed account: {}", account); }, _ => { @@ -465,6 +476,8 @@ async fn main_inner(cli: Cli, multi: MultiProgress) -> Result Result (), Err(e) if e.kind() == AuthErrorKind::InteractionRequired => { eprintln!("This account requires interactive authentication: {}", account); @@ -503,6 +521,8 @@ async fn main_inner(cli: Cli, multi: MultiProgress) -> Result) -> bool { @@ -72,7 +73,7 @@ impl MsaAccount { // logs into xbox live using a refresh token // (panics if no refresh token present) - async fn xbl_login_refresh(&mut self, client: &reqwest::Client) -> Result<(), AuthError> { + async fn xbl_login_refresh(&mut self, client: &reqwest::Client, xbl_login: Step<'_, '_>) -> Result<(), AuthError> { debug!("Using refresh token for XBL login"); let oauth_client = create_oauth_client!(self.is_azure_client_id, self.client_id.clone()); let refresh_token = self.refresh_token.as_ref().expect("refresh_access_token called with no refresh token"); @@ -80,7 +81,6 @@ impl MsaAccount { let tokenres: BasicTokenResponse = oauth_client .exchange_refresh_token(refresh_token) .add_scopes(self.scopes_iter()) - .add_extra_param("response_type", "device_code") .request_async(client) .await.map_err(|e| match e { RequestTokenError::ServerResponse(res) @@ -91,6 +91,7 @@ impl MsaAccount { self.refresh_token = tokenres.refresh_token().cloned(); + xbl_login.make_current(); self.xbl_login(client, tokenres.access_token()).await } @@ -124,7 +125,7 @@ impl MsaAccount { // - check if the XBL token is valid/not expired // - if it is expired, try to use refresh token to get a new one // - get rid of auth token if yeah - async fn ensure_xbl(&mut self, client: &reqwest::Client, now: DateTime) -> Result<(), AuthError> { + async fn ensure_xbl<'p>(&mut self, client: &reqwest::Client, now: DateTime, xbl_refresh_step: Step<'p, '_>, xbl_login_step: Step<'p, '_>) -> Result<(), AuthError> { if self.xbl_token.as_ref().is_some_and(|tok| !tok.is_expired(now)) { debug!("XBL token valid. Using it."); return Ok(()) @@ -135,7 +136,8 @@ impl MsaAccount { } debug!("XBL token expired. Trying to refresh it."); - self.xbl_login_refresh(client).await?; + xbl_refresh_step.make_current(); + self.xbl_login_refresh(client, xbl_login_step).await?; self.mc_token = None; @@ -146,12 +148,13 @@ impl MsaAccount { // - if the minecraft services token invalid/expired/missing, do the following // - get minecraftservices xsts token // - use minecraftservices to get mojang token with that xsts token - async fn ensure_mc_token(&mut self, client: &reqwest::Client, now: DateTime) -> Result<(), AuthError> { + async fn ensure_mc_token<'p>(&mut self, client: &reqwest::Client, now: DateTime, xsts_step: Step<'p, '_>, login_step: Step<'p, '_>) -> Result<(), AuthError> { if self.mc_token.as_ref().is_some_and(|tok| !tok.is_expired(now)) { debug!("Mojang token valid. Using it."); return Ok(()) } + xsts_step.make_current(); debug!("Mojang token has expired. Must log in again."); let xbl_token = self.xbl_token.as_ref().expect("ensure_mc_token requires xbl token").value.as_str(); let (user_hash, mc_xsts_tok) = match msa::xsts_request(client, xbl_token, XSTS_RP_MINECRAFT_SERVICES).await? { @@ -163,6 +166,7 @@ impl MsaAccount { msa::XSTSAuthResponse::Error(e) => return Err(e.into()) }; + login_step.make_current(); debug!("Got MinecraftServices XSTS, logging in."); self.mc_token = Some(mcservices::login_with_xbox(client, mc_xsts_tok.as_str(), user_hash.as_str()).await?); @@ -190,18 +194,23 @@ impl MsaAccount { Ok(()) } - async fn load_profile(&mut self, client: &reqwest::Client) -> Result<(), AuthError> { + async fn load_profile(&mut self, client: &reqwest::Client, steps: [Step<'_, '_>; 4]) -> Result<(), AuthError> { + steps[0].make_current(); self.load_xbox_info(client).await?; let mc_token = self.mc_token.as_ref().expect("minecraft token missing").value.as_str(); + steps[1].make_current(); debug!("Checking if you own the game..."); if !mcservices::owns_the_game(client, mc_token).await? { return Err(AuthErrorKind::NotEntitled.into()); } debug!("Getting your profile info..."); + steps[2].make_current(); let player_info = mcservices::get_player_info(client, mc_token).await?; + + steps[3].make_current(); let player_profile = mcservices::get_player_profile(client, player_info.id).await .map_err(|e| AuthError::internal_msg_src("looking up profile", e))?; @@ -211,14 +220,28 @@ impl MsaAccount { Ok(()) } - pub async fn log_in_silent(&mut self, client: &reqwest::Client) -> Result<(), AuthError> { + pub async fn log_in_silent(&mut self, client: &reqwest::Client, progress: &dyn util::progress::ProgressIndication) -> Result<(), AuthError> { + let steps = StepProgress::from(progress); + let step_login = steps.step("Attempt non-interactive login"); + let step_xbl_refresh = steps.step("Refresh Xbox Live token"); + let step_xbl_login = steps.step("Log into Xbox Live"); + let step_mc_xsts = steps.step("Request Minecraft XSTS token"); + let step_mcs_login = steps.step("Log into Minecraft"); + let step_xbl_profile = steps.step("Retrieve your Xbox profile"); + let step_mc_entitlement = steps.step("Check if you own Minecraft"); + let step_mc_profile_1 = steps.step("Look up your Minecraft profile (1)"); + let step_mc_profile_2 = steps.step("Look up your Minecraft profile (2)"); + steps.update_max(); + + step_login.make_current(); + // see it's kind of funny that I call this variable "now" despite the fact that it's // unconditionally 12 hours in the future - figboot let now = Utc::now() + TimeDelta::hours(12); - self.ensure_xbl(client, now).await?; - self.ensure_mc_token(client, now).await?; - self.load_profile(client).await?; + self.ensure_xbl(client, now, step_xbl_refresh, step_xbl_login).await?; + self.ensure_mc_token(client, now, step_mc_xsts, step_mcs_login).await?; + self.load_profile(client, [step_xbl_profile, step_mc_entitlement, step_mc_profile_1, step_mc_profile_2]).await?; Ok(()) } diff --git a/ozone/src/util/progress.rs b/ozone/src/util/progress.rs index 4d2b5e3..ec444c5 100644 --- a/ozone/src/util/progress.rs +++ b/ozone/src/util/progress.rs @@ -31,10 +31,10 @@ pub(crate) struct StepProgress<'ind> { curidx: AtomicUsize } -impl<'ind> Into> for &'ind dyn ProgressIndication { - fn into(self) -> StepProgress<'ind> { +impl<'ind> From<&'ind dyn ProgressIndication> for StepProgress<'ind> { + fn from(ind: &'ind dyn ProgressIndication) -> StepProgress<'ind> { StepProgress { - inner: self, + inner: ind, curidx: AtomicUsize::new(0) } } -- cgit v1.2.3-70-g09d2