From cdeee17c2be5b8b9a333b977b3e2d373b94dfe0a Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Fri, 31 Jan 2025 02:32:19 -0600 Subject: do clippy stuff and change line endings --- src/auth/mcservices.rs | 184 ++++++++++++++--------------- src/auth/msa.rs | 7 +- src/auth/types.rs | 256 ++++++++++++++++++++--------------------- src/auth/types/property_map.rs | 100 ++++++++-------- 4 files changed, 273 insertions(+), 274 deletions(-) (limited to 'src/auth') diff --git a/src/auth/mcservices.rs b/src/auth/mcservices.rs index 4305363..45ef795 100644 --- a/src/auth/mcservices.rs +++ b/src/auth/mcservices.rs @@ -1,92 +1,92 @@ -use std::time::{Duration, SystemTime}; -use chrono::{DateTime, Utc}; -use reqwest::{IntoUrl, Method, RequestBuilder}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use super::{AuthError, MinecraftPlayerInfo, PlayerProfile}; -use super::types::Token; - -const MINECRAFT_LOGIN: &str = "https://api.minecraftservices.com/authentication/login_with_xbox"; -const MINECRAFT_ENTITLEMENTS: &str = "https://api.minecraftservices.com/entitlements"; -const MINECRAFT_PROFILE: &str = "https://api.minecraftservices.com/minecraft/profile"; - -const MINECRAFT_SESSION_PROFILE: &str = "https://sessionserver.mojang.com/session/minecraft/profile/"; - -fn build_authenticated(client: &reqwest::Client, url: impl IntoUrl, method: Method, mc_token: &str) -> RequestBuilder { - super::build_json_request(client, url, method) - .bearer_auth(mc_token) -} - -#[derive(Serialize, Debug)] -#[serde(rename_all = "camelCase")] -struct MinecraftXboxLoginRequest<'a> { - identity_token: &'a str, - ensure_legacy_enabled: bool -} - -#[derive(Deserialize, Debug)] -struct MinecraftXboxLoginResponse { - access_token: String, - expires_in: u64 -} - -pub async fn login_with_xbox(client: &reqwest::Client, xsts_token: &str, user_hash: &str) -> Result { - let tok = format!("XBL3.0 x={user_hash};{xsts_token}"); - let req = MinecraftXboxLoginRequest { - identity_token: tok.as_str(), - ensure_legacy_enabled: true - }; - - let res: MinecraftXboxLoginResponse = super::build_json_request(client, MINECRAFT_LOGIN, Method::POST) - .json(&req).send().await - .and_then(|r| r.error_for_status()) - .map_err(|e| AuthError::Request { what: "minecraft xbox login", error: e })? - .json().await - .map_err(|e| AuthError::Request { what: "minecraft xbox login (decode)", error: e })?; - - let now: DateTime = SystemTime::now().into(); - - Ok(Token { - value: res.access_token, - expire: Some(now + Duration::from_secs(res.expires_in)) - }) -} - -#[derive(Deserialize, Debug)] -struct EntitlementItem { - name: String - // we don't care about the signature -} - -#[derive(Deserialize, Debug)] -struct EntitlementResponse { - #[serde(default)] - items: Vec -} - -pub async fn owns_the_game(client: &reqwest::Client, token: &str) -> Result { - let res: EntitlementResponse = build_authenticated(client, MINECRAFT_ENTITLEMENTS, Method::GET, token) - .send().await - .and_then(|r| r.error_for_status()) - .map_err(|e| AuthError::Request { what: "entitlements", error: e })? - .json().await - .map_err(|e| AuthError::Request { what: "entitlements (receive)", error: e})?; - - Ok(res.items.iter().filter(|i| i.name == "game_minecraft" || i.name == "product_minecraft").next().is_some()) -} - -pub async fn get_player_info(client: &reqwest::Client, token: &str) -> Result { - build_authenticated(client, MINECRAFT_PROFILE, Method::GET, token) - .send().await - .and_then(|r| r.error_for_status()) - .map_err(|e| AuthError::Request { what: "player info", error: e })? - .json().await - .map_err(|e| AuthError::Request { what: "player info (receive)", error: e }) -} - -pub async fn get_player_profile(client: &reqwest::Client, uuid: Uuid) -> Result { - super::build_json_request(client, format!("{}{}?unsigned=false", MINECRAFT_SESSION_PROFILE, uuid.as_simple()), Method::GET) - .send().await - .and_then(|r| r.error_for_status())? - .json().await -} +use std::time::{Duration, SystemTime}; +use chrono::{DateTime, Utc}; +use reqwest::{IntoUrl, Method, RequestBuilder}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; +use super::{AuthError, MinecraftPlayerInfo, PlayerProfile}; +use super::types::Token; + +const MINECRAFT_LOGIN: &str = "https://api.minecraftservices.com/authentication/login_with_xbox"; +const MINECRAFT_ENTITLEMENTS: &str = "https://api.minecraftservices.com/entitlements"; +const MINECRAFT_PROFILE: &str = "https://api.minecraftservices.com/minecraft/profile"; + +const MINECRAFT_SESSION_PROFILE: &str = "https://sessionserver.mojang.com/session/minecraft/profile/"; + +fn build_authenticated(client: &reqwest::Client, url: impl IntoUrl, method: Method, mc_token: &str) -> RequestBuilder { + super::build_json_request(client, url, method) + .bearer_auth(mc_token) +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +struct MinecraftXboxLoginRequest<'a> { + identity_token: &'a str, + ensure_legacy_enabled: bool +} + +#[derive(Deserialize, Debug)] +struct MinecraftXboxLoginResponse { + access_token: String, + expires_in: u64 +} + +pub async fn login_with_xbox(client: &reqwest::Client, xsts_token: &str, user_hash: &str) -> Result { + let tok = format!("XBL3.0 x={user_hash};{xsts_token}"); + let req = MinecraftXboxLoginRequest { + identity_token: tok.as_str(), + ensure_legacy_enabled: true + }; + + let res: MinecraftXboxLoginResponse = super::build_json_request(client, MINECRAFT_LOGIN, Method::POST) + .json(&req).send().await + .and_then(|r| r.error_for_status()) + .map_err(|e| AuthError::Request { what: "minecraft xbox login", error: e })? + .json().await + .map_err(|e| AuthError::Request { what: "minecraft xbox login (decode)", error: e })?; + + let now: DateTime = SystemTime::now().into(); + + Ok(Token { + value: res.access_token, + expire: Some(now + Duration::from_secs(res.expires_in)) + }) +} + +#[derive(Deserialize, Debug)] +struct EntitlementItem { + name: String + // we don't care about the signature +} + +#[derive(Deserialize, Debug)] +struct EntitlementResponse { + #[serde(default)] + items: Vec +} + +pub async fn owns_the_game(client: &reqwest::Client, token: &str) -> Result { + let res: EntitlementResponse = build_authenticated(client, MINECRAFT_ENTITLEMENTS, Method::GET, token) + .send().await + .and_then(|r| r.error_for_status()) + .map_err(|e| AuthError::Request { what: "entitlements", error: e })? + .json().await + .map_err(|e| AuthError::Request { what: "entitlements (receive)", error: e})?; + + Ok(res.items.iter().any(|i| i.name == "game_minecraft" || i.name == "product_minecraft")) +} + +pub async fn get_player_info(client: &reqwest::Client, token: &str) -> Result { + build_authenticated(client, MINECRAFT_PROFILE, Method::GET, token) + .send().await + .and_then(|r| r.error_for_status()) + .map_err(|e| AuthError::Request { what: "player info", error: e })? + .json().await + .map_err(|e| AuthError::Request { what: "player info (receive)", error: e }) +} + +pub async fn get_player_profile(client: &reqwest::Client, uuid: Uuid) -> Result { + super::build_json_request(client, format!("{}{}?unsigned=false", MINECRAFT_SESSION_PROFILE, uuid.as_simple()), Method::GET) + .send().await + .and_then(|r| r.error_for_status())? + .json().await +} diff --git a/src/auth/msa.rs b/src/auth/msa.rs index 47e088b..404329b 100644 --- a/src/auth/msa.rs +++ b/src/auth/msa.rs @@ -112,8 +112,7 @@ impl XSTSAuthSuccessResponse { } fn get_display_claim(&self, name: &str) -> Option<&str> { - self.display_claims.xui.iter().filter(|m| m.contains_key(name)) - .next().map_or(None, |f| f.get(name).map(|s| s.as_str())) + self.display_claims.xui.iter().find(|m| m.contains_key(name)).and_then(|f| f.get(name).map(|s| s.as_str())) } pub(super) fn get_user_hash(&self) -> Option<&str> { @@ -121,8 +120,7 @@ impl XSTSAuthSuccessResponse { } pub(super) fn get_xuid(&self) -> Option { - self.get_display_claim("xid") - .map_or(None, |s| Some(Uuid::from_u64_pair(0, s.parse().ok()?))) + self.get_display_claim("xid").and_then(|s| s.parse().ok()).map(|n| Uuid::from_u64_pair(0, n)) } pub(super) fn get_gamertag(&self) -> Option<&str> { @@ -130,6 +128,7 @@ impl XSTSAuthSuccessResponse { } } +#[allow(clippy::from_over_into)] impl Into for XSTSAuthErrorResponse { fn into(self) -> AuthError { AuthError::AuthXError { diff --git a/src/auth/types.rs b/src/auth/types.rs index 348bdf8..79b89c9 100644 --- a/src/auth/types.rs +++ b/src/auth/types.rs @@ -1,128 +1,128 @@ -pub mod property_map; -pub use property_map::PropertyMap; - -use std::fmt::{Debug, Formatter}; -use chrono::{DateTime, Utc}; -use multimap::MultiMap; -use oauth2::RefreshToken; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Property { - pub name: String, - pub value: String, - pub signature: Option -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct PlayerProfile { - #[serde(with = "uuid::serde::simple")] - pub id: Uuid, - pub name: String, - - #[serde(default, skip_serializing_if = "MultiMap::is_empty", with = "property_map")] - pub properties: MultiMap -} - -#[derive(Serialize, Deserialize)] -pub(super) struct Token { - pub value: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub expire: Option> -} - -struct RedactedValue; -impl Debug for RedactedValue { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("[redacted]") - } -} - -impl Debug for Token { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Token") - .field("value", &RedactedValue) - .field("expire", &self.expire) - .finish() - } -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum SkinState { - Active, - Inactive -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum SkinVariant { - Classic, - Slim -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SkinInfo { - pub id: Uuid, - pub state: SkinState, - pub url: String, - pub texture_key: Option, - pub variant: Option, - pub alias: Option -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MinecraftPlayerInfo { - #[serde(with = "uuid::serde::simple")] - pub id: Uuid, - pub name: String, - - #[serde(default)] - pub skins: Vec, - #[serde(default)] - pub capes: Vec, - - #[serde(default)] - pub demo: bool, - - #[serde(default)] - pub legacy: bool, - - // todo: profile actions (idk the format) -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct MsaUser { - #[serde(skip_serializing_if = "Option::is_none")] - pub player_profile: Option, - pub xuid: Option, - pub gamertag: Option, - - #[serde(skip)] // this information is transient - pub player_info: Option, - - pub(super) client_id: oauth2::ClientId, - - #[serde(default, skip_serializing_if = "std::ops::Not::not")] - pub(super) is_azure_client_id: bool, - - pub(super) mc_token: Option, - pub(super) xbl_token: Option, - pub(super) refresh_token: Option -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum User { - Dummy(PlayerProfile), - MSA(MsaUser) -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct AuthenticationDatabase { - pub users: Vec -} +pub mod property_map; +pub use property_map::PropertyMap; + +use std::fmt::{Debug, Formatter}; +use chrono::{DateTime, Utc}; +use multimap::MultiMap; +use oauth2::RefreshToken; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Property { + pub name: String, + pub value: String, + pub signature: Option +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PlayerProfile { + #[serde(with = "uuid::serde::simple")] + pub id: Uuid, + pub name: String, + + #[serde(default, skip_serializing_if = "MultiMap::is_empty", with = "property_map")] + pub properties: PropertyMap +} + +#[derive(Serialize, Deserialize)] +pub(super) struct Token { + pub value: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub expire: Option> +} + +struct RedactedValue; +impl Debug for RedactedValue { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("[redacted]") + } +} + +impl Debug for Token { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Token") + .field("value", &RedactedValue) + .field("expire", &self.expire) + .finish() + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum SkinState { + Active, + Inactive +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum SkinVariant { + Classic, + Slim +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SkinInfo { + pub id: Uuid, + pub state: SkinState, + pub url: String, + pub texture_key: Option, + pub variant: Option, + pub alias: Option +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MinecraftPlayerInfo { + #[serde(with = "uuid::serde::simple")] + pub id: Uuid, + pub name: String, + + #[serde(default)] + pub skins: Vec, + #[serde(default)] + pub capes: Vec, + + #[serde(default)] + pub demo: bool, + + #[serde(default)] + pub legacy: bool, + + // todo: profile actions (idk the format) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct MsaUser { + #[serde(skip_serializing_if = "Option::is_none")] + pub player_profile: Option, + pub xuid: Option, + pub gamertag: Option, + + #[serde(skip)] // this information is transient + pub player_info: Option, + + pub(super) client_id: oauth2::ClientId, + + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub(super) is_azure_client_id: bool, + + pub(super) mc_token: Option, + pub(super) xbl_token: Option, + pub(super) refresh_token: Option +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum User { + Dummy(PlayerProfile), + MSA(Box) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthenticationDatabase { + pub users: Vec +} diff --git a/src/auth/types/property_map.rs b/src/auth/types/property_map.rs index ff67416..964b06f 100644 --- a/src/auth/types/property_map.rs +++ b/src/auth/types/property_map.rs @@ -1,50 +1,50 @@ -use std::fmt::Formatter; -use multimap::MultiMap; -use serde::de::{SeqAccess, Visitor}; -use serde::{Deserializer, Serializer}; -use serde::ser::SerializeSeq; -use crate::auth::Property; - -pub type PropertyMap = MultiMap; - -pub fn serialize(value: &PropertyMap, serializer: S) -> Result -where - S: Serializer -{ - let mut seq = serializer.serialize_seq(Some(value.keys().len()))?; - - for (_, prop) in value.flat_iter() { - seq.serialize_element(prop)?; - } - - seq.end() -} - -pub fn deserialize<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de> -{ - struct PropertyMapVisitor; - - impl<'de> Visitor<'de> for PropertyMapVisitor { - type Value = PropertyMap; - - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str("a property map") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut map = MultiMap::new() as PropertyMap; - while let Some(prop) = seq.next_element::()? { - map.insert(prop.name.clone(), prop); - } - - Ok(map) - } - } - - deserializer.deserialize_seq(PropertyMapVisitor) -} +use std::fmt::Formatter; +use multimap::MultiMap; +use serde::de::{SeqAccess, Visitor}; +use serde::{Deserializer, Serializer}; +use serde::ser::SerializeSeq; +use crate::auth::Property; + +pub type PropertyMap = MultiMap; + +pub fn serialize(value: &PropertyMap, serializer: S) -> Result +where + S: Serializer +{ + let mut seq = serializer.serialize_seq(Some(value.keys().len()))?; + + for (_, prop) in value.flat_iter() { + seq.serialize_element(prop)?; + } + + seq.end() +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de> +{ + struct PropertyMapVisitor; + + impl<'de> Visitor<'de> for PropertyMapVisitor { + type Value = PropertyMap; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("a property map") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut map = MultiMap::new() as PropertyMap; + while let Some(prop) = seq.next_element::()? { + map.insert(prop.name.clone(), prop); + } + + Ok(map) + } + } + + deserializer.deserialize_seq(PropertyMapVisitor) +} -- cgit v1.2.3-70-g09d2