aboutsummaryrefslogtreecommitdiff
path: root/azalea-auth/src/sessionserver.rs
blob: fe0b694a4e80c9f5b24f5bc00b7e9f3223633619 (plain)
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
//! Tell Mojang you're joining a multiplayer server.
//!
use serde::Deserialize;
use serde_json::json;
use thiserror::Error;
use uuid::Uuid;

#[derive(Debug, Error)]
pub enum SessionServerError {
    #[error("Error sending HTTP request to sessionserver: {0}")]
    HttpError(#[from] reqwest::Error),
    #[error("Multiplayer is not enabled for this account")]
    MultiplayerDisabled,
    #[error("This account has been banned from multiplayer")]
    Banned,
    #[error("Unknown sessionserver error: {0}")]
    Unknown(String),
    #[error("Unexpected response from sessionserver (status code {status_code}): {body}")]
    UnexpectedResponse { status_code: u16, body: String },
}

#[derive(Deserialize)]
pub struct ForbiddenError {
    pub error: String,
    pub path: String,
}

/// Tell Mojang's servers that you are going to join a multiplayer server,
/// which is required to join online-mode servers. The server ID is an empty
/// string.
pub async fn join(
    access_token: &str,
    public_key: &[u8],
    private_key: &[u8],
    uuid: &Uuid,
    server_id: &str,
) -> Result<(), SessionServerError> {
    let client = reqwest::Client::new();

    let server_hash = azalea_crypto::hex_digest(&azalea_crypto::digest_data(
        server_id.as_bytes(),
        public_key,
        private_key,
    ));

    let mut encode_buffer = Uuid::encode_buffer();
    let undashed_uuid = uuid.simple().encode_lower(&mut encode_buffer);

    let data = json!({
        "accessToken": access_token,
        "selectedProfile": undashed_uuid,
        "serverId": server_hash
    });
    let res = client
        .post("https://sessionserver.mojang.com/session/minecraft/join")
        .json(&data)
        .send()
        .await?;

    match res.status() {
        reqwest::StatusCode::NO_CONTENT => Ok(()),
        reqwest::StatusCode::FORBIDDEN => {
            let forbidden = res.json::<ForbiddenError>().await?;
            match forbidden.error.as_str() {
                "InsufficientPrivilegesException" => Err(SessionServerError::MultiplayerDisabled),
                "UserBannedException" => Err(SessionServerError::Banned),
                _ => Err(SessionServerError::Unknown(forbidden.error)),
            }
        }
        status_code => {
            let body = res.text().await?;
            Err(SessionServerError::UnexpectedResponse {
                status_code: status_code.as_u16(),
                body,
            })
        }
    }
}