diff options
| author | EightFactorial <murphkev000@gmail.com> | 2023-01-21 20:14:23 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-21 22:14:23 -0600 |
| commit | 9ee5e71bb13e596248fde000d8717c86276b0ce1 (patch) | |
| tree | bd6363af53bca9bbd3dede1c7ee59615b94eb107 /azalea-auth/src/sessionserver.rs | |
| parent | 1059afa6fcf8b2776fd25dac07ed2e76ab48bed3 (diff) | |
| download | azalea-drasl-9ee5e71bb13e596248fde000d8717c86276b0ce1.tar.xz | |
Server functions and proxy example (#59)
* A couple useful things for servers
* Add proxy example
* Use Uuid's serde feature
* Add const options to proxy example
* Example crates go in dev-dependencies
* Warn instead of error
* Log address on login
* Requested changes
* add a test for deserializing game profile + random small changes
Co-authored-by: mat <github@matdoes.dev>
Diffstat (limited to 'azalea-auth/src/sessionserver.rs')
| -rwxr-xr-x | azalea-auth/src/sessionserver.rs | 94 |
1 files changed, 82 insertions, 12 deletions
diff --git a/azalea-auth/src/sessionserver.rs b/azalea-auth/src/sessionserver.rs index 685cfa25..502ae098 100755 --- a/azalea-auth/src/sessionserver.rs +++ b/azalea-auth/src/sessionserver.rs @@ -1,11 +1,15 @@ //! Tell Mojang you're joining a multiplayer server. +use log::debug; +use reqwest::StatusCode; use serde::Deserialize; use serde_json::json; use thiserror::Error; use uuid::Uuid; +use crate::game_profile::{GameProfile, SerializableGameProfile}; + #[derive(Debug, Error)] -pub enum SessionServerError { +pub enum ClientSessionServerError { #[error("Error sending HTTP request to sessionserver: {0}")] HttpError(#[from] reqwest::Error), #[error("Multiplayer is not enabled for this account")] @@ -24,6 +28,18 @@ pub enum SessionServerError { UnexpectedResponse { status_code: u16, body: String }, } +#[derive(Debug, Error)] +pub enum ServerSessionServerError { + #[error("Error sending HTTP request to sessionserver: {0}")] + HttpError(#[from] reqwest::Error), + #[error("Invalid or expired session")] + InvalidSession, + #[error("Unexpected response from sessionserver (status code {status_code}): {body}")] + UnexpectedResponse { status_code: u16, body: String }, + #[error("Unknown sessionserver error: {0}")] + Unknown(String), +} + #[derive(Deserialize)] pub struct ForbiddenError { pub error: String, @@ -39,7 +55,7 @@ pub async fn join( private_key: &[u8], uuid: &Uuid, server_id: &str, -) -> Result<(), SessionServerError> { +) -> Result<(), ClientSessionServerError> { let client = reqwest::Client::new(); let server_hash = azalea_crypto::hex_digest(&azalea_crypto::digest_data( @@ -63,28 +79,82 @@ pub async fn join( .await?; match res.status() { - reqwest::StatusCode::NO_CONTENT => Ok(()), - reqwest::StatusCode::FORBIDDEN => { + StatusCode::NO_CONTENT => Ok(()), + StatusCode::FORBIDDEN => { let forbidden = res.json::<ForbiddenError>().await?; match forbidden.error.as_str() { - "InsufficientPrivilegesException" => Err(SessionServerError::MultiplayerDisabled), - "UserBannedException" => Err(SessionServerError::Banned), + "InsufficientPrivilegesException" => { + Err(ClientSessionServerError::MultiplayerDisabled) + } + "UserBannedException" => Err(ClientSessionServerError::Banned), "AuthenticationUnavailableException" => { - Err(SessionServerError::AuthServersUnreachable) + Err(ClientSessionServerError::AuthServersUnreachable) } - "InvalidCredentialsException" => Err(SessionServerError::InvalidSession), - "ForbiddenOperationException" => Err(SessionServerError::ForbiddenOperation), - _ => Err(SessionServerError::Unknown(forbidden.error)), + "InvalidCredentialsException" => Err(ClientSessionServerError::InvalidSession), + "ForbiddenOperationException" => Err(ClientSessionServerError::ForbiddenOperation), + _ => Err(ClientSessionServerError::Unknown(forbidden.error)), } } status_code => { // log the headers - log::debug!("Error headers: {:#?}", res.headers()); + debug!("Error headers: {:#?}", res.headers()); let body = res.text().await?; - Err(SessionServerError::UnexpectedResponse { + Err(ClientSessionServerError::UnexpectedResponse { status_code: status_code.as_u16(), body, }) } } } + +/// Ask Mojang's servers if the player joining is authenticated. +/// Included in the reply is the player's skin and cape. +/// The IP field is optional and equivalent to enabling +/// 'prevent-proxy-connections' in server.properties +pub async fn serverside_auth( + username: &str, + public_key: &[u8], + private_key: &[u8; 16], + ip: Option<&str>, +) -> Result<GameProfile, ServerSessionServerError> { + let hash = azalea_crypto::hex_digest(&azalea_crypto::digest_data( + "".as_bytes(), + public_key, + private_key, + )); + + let url = reqwest::Url::parse_with_params( + "https://sessionserver.mojang.com/session/minecraft/hasJoined", + if let Some(ip) = ip { + vec![("username", username), ("serverId", &hash), ("ip", ip)] + } else { + vec![("username", username), ("serverId", &hash)] + }, + ) + .expect("URL should always be valid"); + + let res = reqwest::get(url).await?; + + match res.status() { + StatusCode::OK => {} + StatusCode::NO_CONTENT => { + return Err(ServerSessionServerError::InvalidSession); + } + StatusCode::FORBIDDEN => { + return Err(ServerSessionServerError::Unknown( + res.json::<ForbiddenError>().await?.error, + )) + } + status_code => { + // log the headers + debug!("Error headers: {:#?}", res.headers()); + let body = res.text().await?; + return Err(ServerSessionServerError::UnexpectedResponse { + status_code: status_code.as_u16(), + body, + }); + } + }; + + Ok(res.json::<SerializableGameProfile>().await?.into()) +} |
