cobble_core/profile/
mod.rs

1mod entitlements;
2mod microsoft;
3mod minecraft;
4mod minecraft_profile;
5mod xbl;
6mod xsts;
7
8use crate::error::{AuthError, AuthResult};
9pub use microsoft::DeviceCodeInfo;
10use time::OffsetDateTime;
11use uuid::Uuid;
12
13/// A profile for Minecraft online mode.
14/// 
15/// ```rust
16///# use cobble_core::profile::CobbleProfile;
17///# use cobble_core::error::AuthResult;
18///# async fn authenticate() -> AuthResult<()> {
19/// // See the oauth2 crate for information to create a Microsoft Application for `MS_GRAPH_ID`.
20/// let code_info = CobbleProfile::setup_authentication("<MS_GRAPH_ID>".to_string()).await?;
21/// 
22/// // Print and URL
23/// println!("URL: {}", &code_info.verification_url());
24/// println!("CODE: {}", &code_info.user_code());
25/// 
26/// // Waits until authentication is done
27/// let profile: CobbleProfile = CobbleProfile::authenticate(code_info).await?;
28///#
29///# Ok(())
30///# }
31/// ```
32#[cfg_attr(doc_cfg, doc(cfg(feature = "auth")))]
33#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
34#[derive(Clone, Debug)]
35pub struct CobbleProfile {
36    /// Profile [`UUID`](uuid::Uuid).
37    pub uuid: Uuid,
38    /// Minecraft profile ID
39    pub profile_id: String,
40    /// Minecraft player name
41    pub player_name: String,
42    /// Microsoft refresh token
43    pub microsoft_refresh_token: String,
44    /// Minecraft token
45    pub minecraft_token: String,
46    /// Minecraft token expiration
47    #[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
48    pub minecraft_token_exp: OffsetDateTime,
49}
50
51impl CobbleProfile {
52    /// Gets the Minecraft access token
53    pub fn access_token(&self) -> String {
54        self.minecraft_token.clone()
55    }
56
57    /// Setup the authentication process.
58    /// Returns a struct used to retrieve the device code and verification URL.
59    /// The result of this call is used to finish the authentication.
60    #[instrument(name = "setup_profile_authentication", level = "debug", skip_all)]
61    pub async fn setup_authentication(client_id: String) -> AuthResult<DeviceCodeInfo> {
62        microsoft::setup_authentication(client_id).await
63    }
64
65    /// Finish authentication by polling microsoft for the token.
66    /// Subsequently authenticates with XBoxLive, XBoxLiveSecurity and Minecraft.
67    #[instrument(name = "authenticate_profile", level = "debug", skip_all)]
68    pub async fn authenticate(info: DeviceCodeInfo) -> AuthResult<Self> {
69        let microsoft = info.finish_authentication().await?;
70        let xbl = xbl::authenticate(&microsoft.access_token).await?;
71        let xsts = xsts::authenticate(&xbl.access_token).await?;
72        let minecraft = minecraft::authenticate(&xsts.access_token, &xbl.user_hash).await?;
73
74        let entitlements = entitlements::get_entitlements(&minecraft.access_token).await?;
75
76        let minecraft_entitlement = entitlements
77            .entitlements
78            .iter()
79            .find(|e| e.name == "product_minecraft");
80
81        if minecraft_entitlement.is_none() {
82            trace!("User does not have 'product_minecraft' entitlements");
83            return Err(AuthError::Unauthorized);
84        }
85
86        let profile = minecraft_profile::get_profile(&minecraft.access_token).await?;
87
88        Ok(CobbleProfile {
89            uuid: Uuid::new_v4(),
90            profile_id: profile.id,
91            player_name: profile.name,
92            microsoft_refresh_token: microsoft.refresh_token,
93            minecraft_token: minecraft.access_token,
94            minecraft_token_exp: minecraft.expiration,
95        })
96    }
97
98    /// Refreshes the Minecraft access token.
99    #[instrument(name = "refresh_profile", level = "debug", skip_all)]
100    pub async fn refresh(&mut self, client_id: String) -> AuthResult<()> {
101        let microsoft =
102            microsoft::refresh_token(client_id, self.microsoft_refresh_token.clone()).await?;
103        let xbl = xbl::authenticate(&microsoft.access_token).await?;
104        let xsts = xsts::authenticate(&xbl.access_token).await?;
105        let minecraft = minecraft::authenticate(&xsts.access_token, &xbl.user_hash).await?;
106
107        self.microsoft_refresh_token = microsoft.refresh_token;
108        self.minecraft_token = minecraft.access_token;
109        self.minecraft_token_exp = minecraft.expiration;
110
111        Ok(())
112    }
113}