use deepslate_protocol::types::GameProfile;
use serde::Deserialize;
use tracing::debug;
const HAS_JOINED_URL: &str = "https://sessionserver.mojang.com/session/minecraft/hasJoined";
#[derive(Debug, Deserialize)]
struct MojangProfile {
id: String,
name: String,
#[serde(default)]
properties: Vec<MojangProperty>,
}
#[derive(Debug, Deserialize)]
struct MojangProperty {
name: String,
value: String,
signature: Option<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("player is not authenticated with Mojang")]
NotAuthenticated,
#[error("unexpected response from session server: HTTP {0}")]
UnexpectedStatus(u16),
#[error("invalid UUID in response: {0}")]
InvalidUuid(String),
}
pub async fn verify_player(
client: &reqwest::Client,
username: &str,
server_id: &str,
) -> Result<GameProfile, AuthError> {
debug!(username, "verifying player with Mojang session server");
let response = client
.get(HAS_JOINED_URL)
.query(&[("username", username), ("serverId", server_id)])
.send()
.await?;
match response.status().as_u16() {
200 => {}
204 => return Err(AuthError::NotAuthenticated),
status => return Err(AuthError::UnexpectedStatus(status)),
}
let profile: MojangProfile = response.json().await?;
let uuid =
uuid::Uuid::parse_str(&profile.id).map_err(|e| AuthError::InvalidUuid(e.to_string()))?;
Ok(GameProfile {
id: uuid,
name: profile.name,
properties: profile
.properties
.into_iter()
.map(|p| deepslate_protocol::types::ProfileProperty {
name: p.name,
value: p.value,
signature: p.signature,
})
.collect(),
})
}