-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* initial commit * merge fixes * added sanitizing * linter * Improve sign in UI * simple simple! * bump version --------- Co-authored-by: CodexAdrian <83074853+CodexAdrian@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me>
- Loading branch information
1 parent
49bfb06
commit 6d9d403
Showing
29 changed files
with
702 additions
and
288 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//! Main authentication flow for Hydra | ||
|
||
use serde::Deserialize; | ||
|
||
use crate::prelude::Credentials; | ||
|
||
use super::stages::{ | ||
bearer_token, player_info, poll_response, xbl_signin, xsts_token, | ||
}; | ||
|
||
#[derive(Debug, Deserialize)] | ||
pub struct OauthFailure { | ||
pub error: String, | ||
} | ||
|
||
pub struct SuccessfulLogin { | ||
pub name: String, | ||
pub icon: String, | ||
pub token: String, | ||
pub refresh_token: String, | ||
pub expires_after: i64, | ||
} | ||
|
||
pub async fn wait_finish(device_code: String) -> crate::Result<Credentials> { | ||
// Loop, polling for response from Microsoft | ||
let oauth = poll_response::poll_response(device_code).await?; | ||
|
||
// Get xbl token from oauth token | ||
let xbl_token = xbl_signin::login_xbl(&oauth.access_token).await?; | ||
|
||
// Get xsts token from xbl token | ||
let xsts_response = xsts_token::fetch_token(&xbl_token.token).await?; | ||
|
||
match xsts_response { | ||
xsts_token::XSTSResponse::Unauthorized(err) => { | ||
Err(crate::ErrorKind::HydraError(format!( | ||
"Error getting XBox Live token: {}", | ||
err | ||
)) | ||
.as_error()) | ||
} | ||
xsts_token::XSTSResponse::Success { token: xsts_token } => { | ||
// Get xsts bearer token from xsts token | ||
let bearer_token = | ||
bearer_token::fetch_bearer(&xsts_token, &xbl_token.uhs) | ||
.await | ||
.map_err(|err| { | ||
crate::ErrorKind::HydraError(format!( | ||
"Error getting bearer token: {}", | ||
err | ||
)) | ||
})?; | ||
|
||
// Get player info from bearer token | ||
let player_info = player_info::fetch_info(&bearer_token).await.map_err(|_err| { | ||
crate::ErrorKind::HydraError("No Minecraft account for profile. Make sure you own the game and have set a username through the official Minecraft launcher." | ||
.to_string()) | ||
})?; | ||
|
||
// Create credentials | ||
let credentials = Credentials::new( | ||
uuid::Uuid::parse_str(&player_info.id)?, // get uuid from player_info.id which is a String | ||
player_info.name, | ||
bearer_token, | ||
oauth.refresh_token, | ||
chrono::Utc::now() | ||
+ chrono::Duration::seconds(oauth.expires_in), | ||
); | ||
|
||
// Put credentials into state | ||
let state = crate::State::get().await?; | ||
{ | ||
let mut users = state.users.write().await; | ||
users.insert(&credentials).await?; | ||
} | ||
|
||
if state.settings.read().await.default_user.is_none() { | ||
let mut settings = state.settings.write().await; | ||
settings.default_user = Some(credentials.id); | ||
} | ||
|
||
Ok(credentials) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//! Login route for Hydra, redirects to the Microsoft login page before going to the redirect route | ||
use std::collections::HashMap; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::{hydra::MicrosoftError, util::fetch::REQWEST_CLIENT}; | ||
|
||
use super::MICROSOFT_CLIENT_ID; | ||
|
||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct DeviceLoginSuccess { | ||
pub device_code: String, | ||
pub user_code: String, | ||
pub verification_uri: String, | ||
pub expires_in: u64, | ||
pub interval: u64, | ||
pub message: String, | ||
} | ||
|
||
pub async fn init() -> crate::Result<DeviceLoginSuccess> { | ||
// Get the initial URL | ||
let client_id = MICROSOFT_CLIENT_ID; | ||
|
||
// Get device code | ||
// Define the parameters | ||
let mut params = HashMap::new(); | ||
params.insert("client_id", client_id); | ||
params.insert("scope", "XboxLive.signin offline_access"); | ||
|
||
// urlencoding::encode("XboxLive.signin offline_access")); | ||
let req = REQWEST_CLIENT.post("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode") | ||
.header("Content-Type", "application/x-www-form-urlencoded").form(¶ms).send().await?; | ||
|
||
match req.status() { | ||
reqwest::StatusCode::OK => Ok(req.json().await?), | ||
_ => { | ||
let microsoft_error = req.json::<MicrosoftError>().await?; | ||
Err(crate::ErrorKind::HydraError(format!( | ||
"Error from Microsoft: {:?}", | ||
microsoft_error.error_description | ||
)) | ||
.into()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pub mod complete; | ||
pub mod init; | ||
pub mod refresh; | ||
mod stages; | ||
|
||
use serde::Deserialize; | ||
|
||
const MICROSOFT_CLIENT_ID: &str = "c4502edb-87c6-40cb-b595-64a280cf8906"; | ||
|
||
#[derive(Deserialize)] | ||
pub struct MicrosoftError { | ||
pub error: String, | ||
pub error_description: String, | ||
pub error_codes: Vec<u64>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use std::collections::HashMap; | ||
|
||
use reqwest::StatusCode; | ||
use serde::Deserialize; | ||
|
||
use crate::{ | ||
hydra::{MicrosoftError, MICROSOFT_CLIENT_ID}, | ||
util::fetch::REQWEST_CLIENT, | ||
}; | ||
|
||
#[derive(Debug, Deserialize)] | ||
pub struct OauthSuccess { | ||
pub token_type: String, | ||
pub scope: String, | ||
pub expires_in: i64, | ||
pub access_token: String, | ||
pub refresh_token: String, | ||
} | ||
|
||
pub async fn refresh(refresh_token: String) -> crate::Result<OauthSuccess> { | ||
let mut params = HashMap::new(); | ||
params.insert("grant_type", "refresh_token"); | ||
params.insert("client_id", MICROSOFT_CLIENT_ID); | ||
params.insert("refresh_token", &refresh_token); | ||
|
||
// Poll the URL in a loop until we are successful. | ||
// On an authorization_pending response, wait 5 seconds and try again. | ||
let resp = REQWEST_CLIENT | ||
.post("https://login.microsoftonline.com/consumers/oauth2/v2.0/token") | ||
.header("Content-Type", "application/x-www-form-urlencoded") | ||
.form(¶ms) | ||
.send() | ||
.await?; | ||
|
||
match resp.status() { | ||
StatusCode::OK => { | ||
let oauth = resp.json::<OauthSuccess>().await.map_err(|err| { | ||
crate::ErrorKind::HydraError(format!( | ||
"Could not decipher successful response: {}", | ||
err | ||
)) | ||
})?; | ||
Ok(oauth) | ||
} | ||
_ => { | ||
let failure = | ||
resp.json::<MicrosoftError>().await.map_err(|err| { | ||
crate::ErrorKind::HydraError(format!( | ||
"Could not decipher failure response: {}", | ||
err | ||
)) | ||
})?; | ||
Err(crate::ErrorKind::HydraError(format!( | ||
"Error refreshing token: {}", | ||
failure.error | ||
)) | ||
.as_error()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use serde_json::json; | ||
|
||
const MCSERVICES_AUTH_URL: &str = | ||
"https://api.minecraftservices.com/launcher/login"; | ||
|
||
pub async fn fetch_bearer(token: &str, uhs: &str) -> crate::Result<String> { | ||
let client = reqwest::Client::new(); | ||
let body = client | ||
.post(MCSERVICES_AUTH_URL) | ||
.json(&json!({ | ||
"xtoken": format!("XBL3.0 x={};{}", uhs, token), | ||
"platform": "PC_LAUNCHER" | ||
})) | ||
.send() | ||
.await? | ||
.text() | ||
.await?; | ||
|
||
serde_json::from_str::<serde_json::Value>(&body)? | ||
.get("access_token") | ||
.and_then(serde_json::Value::as_str) | ||
.map(String::from) | ||
.ok_or( | ||
crate::ErrorKind::HydraError(format!( | ||
"Response didn't contain valid bearer token. body: {body}" | ||
)) | ||
.into(), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
//! MSA authentication stages | ||
|
||
pub mod bearer_token; | ||
pub mod player_info; | ||
pub mod poll_response; | ||
pub mod xbl_signin; | ||
pub mod xsts_token; |
Oops, something went wrong.