Skip to content
This repository was archived by the owner on Mar 14, 2026. It is now read-only.

Commit 46ac428

Browse files
committed
feat: minecraft-auth partial support
fixup: form string
1 parent 9845ba8 commit 46ac428

5 files changed

Lines changed: 173 additions & 80 deletions

File tree

crates/core/src/auth/microsoft.rs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#![forbid(unsafe_code)]
22
#![warn(clippy::pedantic)]
33

4-
use crate::{errors::AuthErrors, trait_alias::*};
4+
use std::collections::HashMap;
5+
6+
use crate::{MOJANG_REDIR_URL, errors::AuthErrors, trait_alias::*};
57
use reqwest::Client;
68
use serde::{Deserialize, Serialize};
79
use tokio::{io::AsyncReadExt, net::TcpListener, sync::mpsc};
@@ -154,10 +156,7 @@ fn parse_oauth(data: &[u8]) -> Result<OuathInfo, AuthErrors> {
154156
/// OAuth Auth Token Infomation.
155157
#[derive(Deserialize, Debug)]
156158
pub struct OuathToken {
157-
pub token_type: String,
158-
pub scope: String,
159-
pub expires_in: u16,
160-
pub ext_expires_in: u16,
159+
pub expires_in: u64,
161160
pub access_token: String,
162161
pub refresh_token: String,
163162
}
@@ -168,34 +167,38 @@ pub fn ouath_token(
168167
code: Option<&str>,
169168
client_id: &str,
170169
scope: &str,
171-
port: u16,
172-
client_secret: &str,
170+
redirect_uri: String,
171+
client_secret: Option<&str>,
173172
) -> impl AsyncSendSync<Result<OuathToken, AuthErrors>> {
174-
let url = format!("https://login.microsoftonline.com/consumers/oauth2/v2.0/token");
175-
let mut body = format!(
176-
"client_id={}&scope={}&client_secret={}",
177-
client_id, scope, client_secret
178-
);
173+
let url = if redirect_uri == MOJANG_REDIR_URL {
174+
"https://login.live.com/oauth20_token.srf"
175+
} else {
176+
"https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
177+
};
178+
179+
// Use owned Strings for both keys and values
180+
let mut form = HashMap::new();
181+
form.insert("client_id".to_string(), client_id.to_string());
182+
form.insert("scope".to_string(), scope.to_string());
183+
184+
if let Some(secret) = client_secret {
185+
form.insert("client_secret".to_string(), secret.to_string());
186+
}
179187

180188
if let Some(token) = refresh_token {
181-
body.push_str(&format!(
182-
"&grant_type=refresh_token&refresh_token={}",
183-
token
184-
));
189+
form.insert("grant_type".to_string(), "refresh_token".to_string());
190+
form.insert("refresh_token".to_string(), token); // token is already a String
185191
} else if let Some(auth_code) = code {
186-
let redirect_uri = format!("http://localhost:{}", port);
187-
body.push_str(&format!(
188-
"&grant_type=authorization_code&code={}&redirect_uri={}",
189-
auth_code, redirect_uri
190-
));
191-
};
192+
form.insert("grant_type".to_string(), "authorization_code".to_string());
193+
form.insert("code".to_string(), auth_code.to_string());
194+
form.insert("redirect_uri".to_string(), redirect_uri);
195+
}
192196

193197
async move {
194198
'out: {
195-
let result = client.post(url).body(body).send().await;
199+
let result = client.post(url).form(&form).send().await;
196200

197201
let std::result::Result::Ok(response) = result else {
198-
println!("Part 1");
199202
break 'out Err(AuthErrors::ResponseError(
200203
"Failed to send request".to_string(),
201204
));
@@ -205,7 +208,8 @@ pub fn ouath_token(
205208
.text()
206209
.await
207210
.map_err(|_| AuthErrors::ResponseError("Failed to send request".to_string()))?;
208-
let std::result::Result::Ok(token) = serde_json::from_str::<OuathToken>(&text) else {
211+
212+
let std::result::Result::Ok(token) = serde_json::from_str(&text) else {
209213
break 'out Err(AuthErrors::ResponseError(
210214
"Failed to send request, Check your Client Secret.".to_string(),
211215
));

crates/core/src/auth/xbox.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#![forbid(unsafe_code)]
22
#![warn(clippy::pedantic)]
33

4-
use crate::trait_alias::*;
4+
use crate::{MOJANG_REDIR_URL, trait_alias::*};
55
use reqwest::Client;
66

7-
use reqwest::header::{self, HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
7+
use reqwest::header::{self, ACCEPT, CONTENT_TYPE, HeaderMap, HeaderValue};
88
use serde::Deserialize;
9-
use serde_json::{json, Value};
9+
use serde_json::{Value, json};
1010

1111
use crate::errors::AuthErrors;
1212
#[derive(Deserialize, Debug)]
@@ -29,18 +29,25 @@ pub struct XblOutput {
2929
pub display_claims: DisplayClaims,
3030
}
3131

32-
pub fn xbl(client: Client, token: &str) -> impl AsyncSendSync<Result<XblOutput, AuthErrors>> {
32+
pub fn xbl(
33+
client: Client,
34+
token: &str,
35+
redirect_url: String,
36+
) -> impl AsyncSendSync<Result<XblOutput, AuthErrors>> {
3337
let url = format!("https://user.auth.xboxlive.com/user/authenticate");
3438
let rps_ticket = format!("d={}", token);
3539
let mut headers = HeaderMap::new();
36-
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
3740
headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
3841

3942
let body = json!({
4043
"Properties": {
4144
"AuthMethod": "RPS",
4245
"SiteName": "user.auth.xboxlive.com",
43-
"RpsTicket": rps_ticket,
46+
"RpsTicket": if redirect_url == MOJANG_REDIR_URL {
47+
token
48+
} else {
49+
&rps_ticket
50+
},
4451
},
4552
"RelyingParty": "http://auth.xboxlive.com",
4653
"TokenType": "JWT"
@@ -68,6 +75,9 @@ async fn xbl_internal(
6875
"Failed to send request to xbox".to_string(),
6976
));
7077
};
78+
79+
println!("Text: {:?}", response.status());
80+
7181
let text = response
7282
.text()
7383
.await

crates/core/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ pub const EXPERIMENTAL_MESSAGE: &str =
4141
#[cfg(feature = "launch")]
4242
pub(crate) const MANIFEST_URL: &str =
4343
"https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";
44+
45+
#[cfg(feature = "auth")]
46+
pub(crate) const MOJANG_REDIR_URL: &str = "https://login.live.com/oauth20_desktop.srf";

0 commit comments

Comments
 (0)