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,
}