Authenficiation finally work
This commit is contained in:
parent
9e518db87c
commit
ad3988ed29
@ -1,11 +1,11 @@
|
|||||||
use std::{fmt, net::TcpListener, sync::Arc};
|
use std::{fmt, net::TcpListener, sync::Arc};
|
||||||
|
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use reqwest::{header::{CONTENT_TYPE, CONNECTION, ACCEPT}, Client};
|
use reqwest::{header::{CONTENT_TYPE, CONNECTION, ACCEPT, AUTHORIZATION}, Client};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use tokio::sync::mpsc;
|
use tokio::{sync::mpsc, join};
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use warp::{Filter, http::Response};
|
use warp::{Filter, http::Response};
|
||||||
use anyhow::{bail, Result, anyhow};
|
use anyhow::{bail, Result, anyhow};
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ struct AccessRefreshToken {
|
|||||||
refresh_token: String
|
refresh_token: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug, Serialize)]
|
||||||
struct XboxAuthData {
|
struct XboxAuthData {
|
||||||
token: String,
|
token: String,
|
||||||
uhs: String
|
uhs: String
|
||||||
@ -123,8 +124,8 @@ impl Authentification {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let token = AccessRefreshToken {
|
let token = AccessRefreshToken {
|
||||||
access_token: received["access_token"].to_string(),
|
access_token: String::from(received["access_token"].as_str().unwrap()),
|
||||||
refresh_token: received["refresh_token"].to_string()
|
refresh_token: String::from(received["refresh_token"].as_str().unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(token)
|
Ok(token)
|
||||||
@ -150,29 +151,125 @@ impl Authentification {
|
|||||||
.await?
|
.await?
|
||||||
.json()
|
.json()
|
||||||
.await?;
|
.await?;
|
||||||
|
Ok(XboxAuthData {
|
||||||
let xbox_auth = XboxAuthData {
|
token: String::from(received["Token"].as_str().unwrap()),
|
||||||
token: received["Token"].to_string(),
|
uhs: String::from(received["DisplayClaims"]["xui"][0]["uhs"].as_str().unwrap())
|
||||||
uhs: received["DisplayClaims"]["xui"][0]["uhs"].to_string()
|
})
|
||||||
};
|
|
||||||
|
|
||||||
Ok(xbox_auth)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_xsts_token() -> Result<()> {
|
async fn fetch_xsts_token(xbl_token: &XboxAuthData, reqwest_client: &Client) -> Result<String> {
|
||||||
|
let request_body = json!({
|
||||||
|
"Properties": {
|
||||||
bail!("Not implemented yet")
|
"SandboxId": "RETAIL",
|
||||||
|
"UserTokens": [xbl_token.token]
|
||||||
|
},
|
||||||
|
"RelyingParty": "rp://api.minecraftservices.com/",
|
||||||
|
"TokenType": "JWT"
|
||||||
|
});
|
||||||
|
let received = reqwest_client
|
||||||
|
.post("https://xsts.auth.xboxlive.com/xsts/authorize")
|
||||||
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
.header(ACCEPT, "application/json")
|
||||||
|
.json(&request_body)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if received.status() == 200 {
|
||||||
|
let data : Value = received.json().await?;
|
||||||
|
Ok(String::from(data["Token"].as_str().unwrap()))
|
||||||
|
} else if received.status() == 401 {
|
||||||
|
let data : Value = received.json().await?;
|
||||||
|
match data["XErr"].as_u64().unwrap() {
|
||||||
|
2148916233 => {
|
||||||
|
bail!("Please sign up to xbox")
|
||||||
|
},
|
||||||
|
2148916235 => {
|
||||||
|
bail!("Xbox Live is unavailable in your country")
|
||||||
|
},
|
||||||
|
2148916236 | 2148916237 => {
|
||||||
|
bail!("Your account need adult verification, please visit xbox page")
|
||||||
|
},
|
||||||
|
2148916238 => {
|
||||||
|
bail!("This account is marked as owned by a child and we cannot process until this account is added to a Family by an adult")
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
bail!("An unknow error occured, error code: {}", data["XErr"].as_u64().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("xsts return status code {}", received.status())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn launch(prompt: Prompt, app: tauri::AppHandle) -> Result<()> {
|
async fn minecraft_auth(uhs: &String, xsts: String, reqwest_client: &Client) -> Result<String> {
|
||||||
|
let request_body: Value = json!({
|
||||||
|
"identityToken": format!("XBL3.0 x={};{}", uhs, xsts)
|
||||||
|
});
|
||||||
|
let received : Value = reqwest_client
|
||||||
|
.post("https://api.minecraftservices.com/authentication/login_with_xbox")
|
||||||
|
.header(CONTENT_TYPE, "application/json")
|
||||||
|
.header(ACCEPT, "application/json")
|
||||||
|
.json(&request_body)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
Ok(String::from(received["access_token"].as_str().unwrap())) // return jwt
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_game_ownership(mc_token: &String, reqwest_client: &Client) -> Result<bool> {
|
||||||
|
let received : Value = reqwest_client
|
||||||
|
.get("https://api.minecraftservices.com/entitlements/mcstore")
|
||||||
|
.header(AUTHORIZATION, format!("Bearer {}", mc_token))
|
||||||
|
.header(ACCEPT, "application/json")
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
if received.is_object() {
|
||||||
|
let arr = received.as_object().unwrap();
|
||||||
|
Ok(arr.len() != 0)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_minecraft_profile(mc_token: &String, reqwest_client: &Client) -> Result<(String, String)> {
|
||||||
|
let received: Value = reqwest_client
|
||||||
|
.get("https://api.minecraftservices.com/minecraft/profile")
|
||||||
|
.header(AUTHORIZATION, format!("Bearer {}", mc_token))
|
||||||
|
.header(ACCEPT, "application/json")
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
if let Some(val) = received.get("error") {
|
||||||
|
bail!(String::from(val.as_str().unwrap()))
|
||||||
|
} else {
|
||||||
|
Ok((String::from(received["id"].as_str().unwrap()), String::from(received["name"].as_str().unwrap())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn login(prompt: Prompt, app: tauri::AppHandle) -> Result<(String, String)> {
|
||||||
let reqwest_client = Client::new();
|
let reqwest_client = Client::new();
|
||||||
let oauth_token = Self::fetch_oauth2_token(prompt, app).await?;
|
let oauth_token = Self::fetch_oauth2_token(prompt, app).await?;
|
||||||
let access_refresh_token = Self::fetch_token(oauth_token.0, oauth_token.1, &reqwest_client).await?;
|
let access_refresh_token = Self::fetch_token(oauth_token.0, oauth_token.1, &reqwest_client).await?;
|
||||||
let xbox_auth = Self::auth_xbox_live(access_refresh_token, &reqwest_client).await?;
|
let xbox_auth = Self::auth_xbox_live(access_refresh_token, &reqwest_client).await?;
|
||||||
|
let xsts = Self::fetch_xsts_token(&xbox_auth, &reqwest_client).await?;
|
||||||
|
let mc_token = Self::minecraft_auth(&xbox_auth.uhs, xsts, &reqwest_client).await?;
|
||||||
Ok(())
|
println!("mc token : {}", mc_token);
|
||||||
|
let (is_mc_owner, profile) = join!(Self::fetch_game_ownership(&mc_token, &reqwest_client), Self::fetch_minecraft_profile(&mc_token, &reqwest_client));
|
||||||
|
match is_mc_owner {
|
||||||
|
Ok(is_mc_owner) => {
|
||||||
|
if is_mc_owner || profile.is_ok() /* game pass owner if have a game profile but isn't a owner */ {
|
||||||
|
profile
|
||||||
|
} else {
|
||||||
|
bail!("Not a owner of a minecraft copy")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
bail!(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn listen(port_holder: TcpListener) -> Result<ReceivedCode> {
|
async fn listen(port_holder: TcpListener) -> Result<ReceivedCode> {
|
||||||
|
@ -15,11 +15,11 @@ fn greet(name: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn second_window(app: tauri::AppHandle, _window: tauri::Window) -> Result<(), String> {
|
async fn second_window(app: tauri::AppHandle, _window: tauri::Window) -> Result<String, ()> {
|
||||||
let result = Authentification::launch(Prompt::SelectAccount, app).await;
|
let result = Authentification::login(Prompt::SelectAccount, app).await;
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(val) => Ok(format!("Hello {}", val.1)),
|
||||||
Err(err) => Err(err.to_string())
|
Err(err) => Ok(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user