From 4cef62e8e4aa04e44048eb67e5091c12a73d2a09 Mon Sep 17 00:00:00 2001 From: mat <27899617+mat-1@users.noreply.github.com> Date: Sun, 16 Oct 2022 22:54:54 -0500 Subject: Microsoft Authentication (#29) * a * try to do more work on auth signing (untested) * well auth works when i remove the d= so * auth stuff * sessionserver stuff * add auth in azalea-protocol/client * caching* refreshing microsoft auth tokens isn't implemented yet, also i haven't tested it * how did i not notice that i had the code duplicated * fix cache * add refreshing msa token * replace some printlns with log::trace * auth works! * Update main.rs * fix clippy warnings --- azalea-client/src/account.rs | 27 ++++++++++++++++++++++++++- azalea-client/src/client.rs | 18 +++++++++++++++--- azalea-client/src/get_mc_dir.rs | 34 ++++++++++++++++++++++++++++++++++ azalea-client/src/lib.rs | 1 + 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 azalea-client/src/get_mc_dir.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index c554908f..94e84ab8 100644 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -1,20 +1,45 @@ //! Connect to Minecraft servers. -use crate::{client::JoinError, Client, Event}; +use crate::{client::JoinError, get_mc_dir, Client, Event}; use azalea_protocol::ServerAddress; use tokio::sync::mpsc::UnboundedReceiver; +use uuid::Uuid; /// Something that can join Minecraft servers. pub struct Account { pub username: String, + /// The access token for authentication. You can obtain one of these + /// manually from azalea-auth. + pub access_token: Option, + /// Only required for online-mode accounts. + pub uuid: Option, } impl Account { pub fn offline(username: &str) -> Self { Self { username: username.to_string(), + access_token: None, + uuid: None, } } + pub async fn microsoft(email: &str) -> Result { + let minecraft_dir = get_mc_dir::minecraft_dir().unwrap(); + let auth_result = azalea_auth::auth( + email, + azalea_auth::AuthOpts { + cache_file: Some(minecraft_dir.join("azalea-auth.json")), + ..Default::default() + }, + ) + .await?; + Ok(Self { + username: auth_result.profile.name, + access_token: Some(auth_result.access_token), + uuid: Some(Uuid::parse_str(&auth_result.profile.id).expect("Invalid UUID")), + }) + } + /// Joins the Minecraft server on the given address using this account. pub async fn join( &self, diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 09f68c4a..25c68c5d 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -38,7 +38,6 @@ use std::{ }; use thiserror::Error; use tokio::{ - io::AsyncWriteExt, sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, task::JoinHandle, time::{self}, @@ -105,6 +104,8 @@ pub enum JoinError { ReadPacket(#[from] azalea_protocol::read::ReadPacketError), #[error("{0}")] Io(#[from] io::Error), + #[error("{0}")] + SessionServer(#[from] azalea_auth::sessionserver::SessionServerError), } #[derive(Error, Debug)] @@ -159,7 +160,17 @@ impl Client { debug!("Got encryption request"); let e = azalea_crypto::encrypt(&p.public_key, &p.nonce).unwrap(); - // TODO: authenticate with the server here (authenticateServer) + if let Some(access_token) = &account.access_token { + conn.authenticate( + access_token, + &account + .uuid + .expect("Uuid must be present if access token is present."), + e.secret_key, + p, + ) + .await?; + } conn.write( ServerboundKeyPacket { @@ -171,6 +182,7 @@ impl Client { .get(), ) .await?; + conn.set_encryption_key(e.secret_key); } ClientboundLoginPacket::LoginCompression(p) => { @@ -237,7 +249,7 @@ impl Client { /// Disconnect from the server, ending all tasks. pub async fn shutdown(self) -> Result<(), std::io::Error> { - self.write_conn.lock().await.write_stream.shutdown().await?; + self.write_conn.lock().await.shutdown().await?; let tasks = self.tasks.lock(); for task in tasks.iter() { task.abort(); diff --git a/azalea-client/src/get_mc_dir.rs b/azalea-client/src/get_mc_dir.rs new file mode 100644 index 00000000..abc5b3c8 --- /dev/null +++ b/azalea-client/src/get_mc_dir.rs @@ -0,0 +1,34 @@ +//! Find out where the user's .minecraft directory is. +//! +//! Used for the auth cache. + +use std::path::PathBuf; + +/// Return the location of the user's .minecraft directory. +/// +/// Windows: `%appdata%\.minecraft`\ +/// Mac: `$HOME/Library/Application Support/minecraft`\ +/// Linux: `$HOME/.minecraft` +/// +/// Anywhere else it'll return None. +pub fn minecraft_dir() -> Option { + #[cfg(target_os = "windows")] + { + let appdata = std::env::var("APPDATA").ok()?; + Some(PathBuf::from(appdata).join(".minecraft")) + } + #[cfg(target_os = "macos")] + { + let home = std::env::var("HOME").ok()?; + Some(PathBuf::from(home).join("Library/Application Support/minecraft")) + } + #[cfg(target_os = "linux")] + { + let home = std::env::var("HOME").ok()?; + Some(PathBuf::from(home).join(".minecraft")) + } + #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))] + { + None + } +} diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index a918ca32..c30ca103 100755 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -2,6 +2,7 @@ mod account; mod client; +mod get_mc_dir; mod movement; pub mod ping; mod player; -- cgit v1.2.3