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

Commit 94c0643

Browse files
committed
feat: reused the oauth
feat: device code
1 parent 2d1fdcb commit 94c0643

3 files changed

Lines changed: 225 additions & 0 deletions

File tree

src/code.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#![forbid(unsafe_code)]
2+
#![warn(clippy::pedantic)]
3+
4+
use serde::{Deserialize, Serialize};
5+
use reqwest::Client;
6+
7+
#[derive(Debug, Serialize, Deserialize)]
8+
pub struct PreResponse {
9+
pub device_code: String,
10+
pub user_code: String,
11+
pub verification_uri: String,
12+
pub expires_in: u32,
13+
pub interval: u16,
14+
pub message: String,
15+
}
16+
17+
18+
#[derive(Debug, Serialize, Deserialize)]
19+
pub struct Response {
20+
pub expires_in: u16,
21+
access_token: String,
22+
}
23+
24+
25+
26+
27+
28+
pub async fn code(client_id: &str, content: &str) -> Result<PreResponse, reqwest::Error> {
29+
let request_url = format!(
30+
"https://login.microsoftonline.com/common/oauth2/v2.0/devicecode?client_id={}",
31+
client_id
32+
);
33+
34+
let client = Client::new();
35+
let response = client
36+
.post(request_url)
37+
.header("Content-Type", content)
38+
.send()
39+
.await?;
40+
41+
let response_data: PreResponse = response.json().await?;
42+
43+
Ok(response_data)
44+
}
45+
46+
47+
48+
pub async fn auth(device_code: &str, client_id: &str) -> Result<(u16, String), reqwest::Error> {
49+
let client = Client::new();
50+
let request_url = format!(
51+
"https://login.microsoftonline.com/common/oauth2/v2.0/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id={}&device_code={}",
52+
client_id,
53+
device_code
54+
);
55+
56+
let request = client.post(request_url).header("Content-Type", "application/x-www-form-urlencoded").send().await?;
57+
58+
let response_data: Response = request.json().await?;
59+
60+
let expires_in = response_data.expires_in;
61+
let token = response_data.access_token;
62+
63+
64+
Ok((expires_in, token))
65+
}
66+
67+
68+

src/lib.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* Minecraft-Essentials
2+
* Copyright (C) 2024 minecraft-essentials
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License v3.0
15+
* along with this program.
16+
*/
17+
18+
#![doc = include_str!("../README.md")]
19+
#![forbid(unsafe_code, missing_docs)]
20+
#![warn(clippy::pedantic)]
21+
22+
mod server;
23+
mod token;
24+
mod xbox;
25+
mod xts;
26+
mod code;
27+
28+
// External Imports
29+
use rand::Rng;
30+
31+
// Local Imports
32+
pub use server::Info as ServerInfo;
33+
34+
/// Minecraft OAuth Authentification Method.
35+
pub struct Oauth {
36+
url: String,
37+
port: u16,
38+
}
39+
40+
41+
42+
/// Implemation of the oauth.
43+
impl Oauth {
44+
/// Create the oauth url.
45+
pub fn new(clientid: &str) -> Self {
46+
// Randomized port part.
47+
let mut rng = rand::thread_rng();
48+
let port = rng.gen_range(25535..=65535);
49+
50+
let url = format!("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={}&response_type=code&redirect_uri=http://localhost:{}&response_mode=query&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read&state=12345", clientid, port);
51+
52+
// Returns the port and url as self.
53+
Self { url, port }
54+
}
55+
56+
/// Returns the URL of the OAuth object.
57+
pub fn url(&self) -> &str {
58+
&self.url
59+
}
60+
61+
/// The launch function
62+
pub async fn launch(&self) -> std::io::Result<ServerInfo> {
63+
// Launches the temporary http server.
64+
server::launch(self.port).await
65+
}
66+
}
67+
68+
/// Minecraft Device Code Authentification Method.
69+
pub struct DeviceCode {
70+
url: String,
71+
message: String,
72+
expires_in: u32,
73+
user_code: String,
74+
device_code: String,
75+
}
76+
77+
78+
/// Implemation of the device code.
79+
impl DeviceCode {
80+
/// Proccess to get the code.
81+
pub async fn new(client_id: &str) -> Result<Self, reqwest::Error> {
82+
pub const CONTENT_TYPE: &str = "application/x-www-form-urlencoded";
83+
let response_data = code::code(client_id, CONTENT_TYPE).await?;
84+
85+
Ok(Self {
86+
url: response_data.verification_uri,
87+
message: response_data.message,
88+
expires_in: response_data.expires_in,
89+
user_code: response_data.user_code,
90+
device_code: response_data.device_code,
91+
})
92+
}
93+
94+
/// The prelaunch stuff.
95+
pub fn prelaunch(&self) -> (&str, &str, u32, &str) {
96+
(&self.url, &self.message, self.expires_in, &self.user_code)
97+
}
98+
99+
/// The launch function
100+
pub async fn launch(&self, client_id: &str) {
101+
let _ = code::auth(&self.device_code, client_id).await;
102+
}
103+
}
104+
105+
106+
107+
/// Authentification Data once done.
108+
#[derive(Debug, Clone, PartialEq, Eq)]
109+
pub struct AuthData {
110+
/// The access token.
111+
pub access_token: String,
112+
/// The Optional UUID.
113+
pub uuid: Option<String>,
114+
}

src/server.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![forbid(unsafe_code, missing_docs)]
2+
#![warn(clippy::pedantic)]
3+
4+
//! The server for OAauth login Technically
5+
6+
use actix_web::{web, App, HttpResponse, HttpServer};
7+
use serde::Deserialize;
8+
use std::str;
9+
use tokio::sync::mpsc;
10+
11+
/// Infomation from the temporary http server.
12+
#[derive(Deserialize)]
13+
pub struct Info {
14+
/// The code
15+
pub code: String,
16+
/// The state
17+
pub state: String,
18+
}
19+
20+
pub async fn launch(port: u16) -> std::io::Result<Info> {
21+
let (tx, mut rx) = mpsc::channel::<Info>(1);
22+
23+
let server = tokio::spawn(
24+
HttpServer::new(move || {
25+
App::new().app_data(tx.clone()).route(
26+
"/",
27+
web::get().to(|web::Query(info): web::Query<Info>, tx: web::Data<mpsc::Sender<Info>>| async move {
28+
tx.try_send(info).unwrap();
29+
HttpResponse::Ok()
30+
}),
31+
)
32+
})
33+
.bind(format!("127.0.0.1:{}", port))?
34+
.workers(1)
35+
.run(),
36+
);
37+
38+
let info = rx.recv().await.expect("server did not launch");
39+
40+
server.abort();
41+
42+
Ok(info)
43+
}

0 commit comments

Comments
 (0)