1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
mod entitlements;
mod microsoft;
mod minecraft;
mod minecraft_profile;
mod xbl;
mod xsts;
use crate::error::{AuthError, AuthResult};
pub use microsoft::DeviceCodeInfo;
use time::OffsetDateTime;
use uuid::Uuid;
#[cfg_attr(doc_cfg, doc(cfg(feature = "auth")))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug)]
pub struct CobbleProfile {
pub uuid: Uuid,
pub profile_id: String,
pub player_name: String,
pub microsoft_refresh_token: String,
pub minecraft_token: String,
#[cfg_attr(feature = "serde", serde(with = "time::serde::rfc3339"))]
pub minecraft_token_exp: OffsetDateTime,
}
impl CobbleProfile {
pub fn access_token(&self) -> String {
self.minecraft_token.clone()
}
#[instrument(name = "setup_profile_authentication", level = "debug", skip_all)]
pub async fn setup_authentication(client_id: String) -> AuthResult<DeviceCodeInfo> {
microsoft::setup_authentication(client_id).await
}
#[instrument(name = "authenticate_profile", level = "debug", skip_all)]
pub async fn authenticate(info: DeviceCodeInfo) -> AuthResult<Self> {
let microsoft = info.finish_authentication().await?;
let xbl = xbl::authenticate(µsoft.access_token).await?;
let xsts = xsts::authenticate(&xbl.access_token).await?;
let minecraft = minecraft::authenticate(&xsts.access_token, &xbl.user_hash).await?;
let entitlements = entitlements::get_entitlements(&minecraft.access_token).await?;
let minecraft_entitlement = entitlements
.entitlements
.iter()
.find(|e| e.name == "product_minecraft");
if minecraft_entitlement.is_none() {
trace!("User does not have 'product_minecraft' entitlements");
return Err(AuthError::Unauthorized);
}
let profile = minecraft_profile::get_profile(&minecraft.access_token).await?;
Ok(CobbleProfile {
uuid: Uuid::new_v4(),
profile_id: profile.id,
player_name: profile.name,
microsoft_refresh_token: microsoft.refresh_token,
minecraft_token: minecraft.access_token,
minecraft_token_exp: minecraft.expiration,
})
}
#[instrument(name = "refresh_profile", level = "debug", skip_all)]
pub async fn refresh(&mut self, client_id: String) -> AuthResult<()> {
let microsoft =
microsoft::refresh_token(client_id, self.microsoft_refresh_token.clone()).await?;
let xbl = xbl::authenticate(µsoft.access_token).await?;
let xsts = xsts::authenticate(&xbl.access_token).await?;
let minecraft = minecraft::authenticate(&xsts.access_token, &xbl.user_hash).await?;
self.microsoft_refresh_token = microsoft.refresh_token;
self.minecraft_token = minecraft.access_token;
self.minecraft_token_exp = minecraft.expiration;
Ok(())
}
}