cobble-core 1.2.0

Library for managing, installing and launching Minecraft instances and more.
Documentation
use crate::consts;
use crate::error::{AuthError, AuthResult};
use serde::Deserialize;
use serde_json::json;

#[derive(Debug, Clone)]
pub struct XblToken {
    pub access_token: String,
    pub user_hash: String,
}

#[instrument(name = "authenticate_xbl", level = "trace", skip_all)]
pub async fn authenticate(ms_token: &str) -> AuthResult<XblToken> {
    let client = reqwest::Client::new();
    let response = client
        .post(consts::XBL_TOKEN_URL)
        .json(&json!({
            "Properties": {
                "AuthMethod": "RPS",
                "SiteName": "user.auth.xboxlive.com",
                "RpsTicket": format!("d={}", ms_token)
            },
            "RelyingParty": "http://auth.xboxlive.com",
            "TokenType": "JWT"
        }))
        .send()
        .await?
        .error_for_status()?
        .json::<XboxLiveTokenResponse>()
        .await?;

    Ok(XblToken {
        access_token: response.token,
        user_hash: response
            .display_claims
            .xui
            .first()
            .map(|uhs| uhs.uhs.clone())
            .ok_or(AuthError::NoXblUserHash)?,
    })
}

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct XboxLiveTokenResponse {
    token: String,
    display_claims: DisplayClaims,
}

#[derive(Clone, Debug, Deserialize)]
struct DisplayClaims {
    xui: Vec<UserHash>,
}

#[derive(Clone, Debug, Deserialize)]
struct UserHash {
    uhs: String,
}