feat: auth
This commit is contained in:
parent
6c80a84972
commit
a8eb841bd7
13 changed files with 1298 additions and 22 deletions
121
Cargo.lock
generated
121
Cargo.lock
generated
|
|
@ -294,6 +294,12 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
|
|
@ -892,6 +898,18 @@ version = "0.8.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
|
@ -1152,6 +1170,20 @@ version = "1.0.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
|
|
@ -1161,6 +1193,26 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "2.5.0"
|
||||
|
|
@ -1259,6 +1311,16 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "field-offset"
|
||||
version = "0.3.6"
|
||||
|
|
@ -1592,6 +1654,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
|||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1730,6 +1793,17 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk"
|
||||
version = "0.18.1"
|
||||
|
|
@ -2934,6 +3008,7 @@ dependencies = [
|
|||
"async-trait",
|
||||
"async_zip",
|
||||
"base64 0.22.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"digest",
|
||||
|
|
@ -2950,6 +3025,7 @@ dependencies = [
|
|||
"onelauncher_gamemode",
|
||||
"onelauncher_macro",
|
||||
"onelauncher_migration",
|
||||
"p256",
|
||||
"paste",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
|
|
@ -3066,6 +3142,18 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.18.3"
|
||||
|
|
@ -3406,6 +3494,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
|
|
@ -3834,6 +3931,16 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560"
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
|
|
@ -4211,6 +4318,20 @@ version = "4.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array",
|
||||
"pkcs8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selectors"
|
||||
version = "0.22.0"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ rand = { workspace = true }
|
|||
url = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
|
||||
# integration
|
||||
discord-rich-presence = { workspace = true }
|
||||
|
|
@ -76,6 +77,7 @@ sha1 = { workspace = true }
|
|||
sha2 = { workspace = true }
|
||||
murmur2 = { workspace = true }
|
||||
md-5 = { workspace = true }
|
||||
p256 = { workspace = true }
|
||||
|
||||
# io
|
||||
zip = { workspace = true }
|
||||
|
|
|
|||
25
packages/core/examples/auth.rs
Normal file
25
packages/core/examples/auth.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use onelauncher_core::{api::{credentials, proxy::ProxyDynamic}, error::LauncherResult, initialize_core, store::CoreOptions, utils::io::IOError};
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> LauncherResult<()> {
|
||||
initialize_core(CoreOptions::default(), ProxyDynamic::new()).await?;
|
||||
|
||||
let flow = credentials::begin().await?;
|
||||
println!("{flow:#?}");
|
||||
|
||||
let stdin = tokio::io::stdin();
|
||||
let mut reader = tokio::io::BufReader::new(stdin);
|
||||
let mut code = String::new();
|
||||
|
||||
println!("Please enter the code you received:");
|
||||
reader.read_line(&mut code).await.map_err(IOError::from)?;
|
||||
println!("Please wait");
|
||||
code = code.trim().to_string();
|
||||
|
||||
let creds = credentials::finish(&code, flow).await?;
|
||||
|
||||
println!("Credentials: {creds:#?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
87
packages/core/src/api/credentials.rs
Normal file
87
packages/core/src/api/credentials.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//! Authentication flow manager
|
||||
|
||||
use crate::{error::LauncherResult, store::{credentials::{MinecraftCredentials, MinecraftLogin}, State}};
|
||||
|
||||
/// Begin a Microsoft authentication flow.
|
||||
#[tracing::instrument]
|
||||
pub async fn begin() -> LauncherResult<MinecraftLogin> {
|
||||
let state = State::get().await?;
|
||||
let mut store = state.credentials.write().await;
|
||||
|
||||
store.begin().await
|
||||
}
|
||||
|
||||
/// Complete a Microsoft authentication flow to recieve [`MinecraftCredentials`].
|
||||
#[tracing::instrument]
|
||||
pub async fn finish(code: &str, flow: MinecraftLogin) -> LauncherResult<MinecraftCredentials> {
|
||||
let state = State::get().await?;
|
||||
let mut store = state.credentials.write().await;
|
||||
|
||||
store.finish(code, flow).await
|
||||
}
|
||||
|
||||
/// Get the current default user if it exists by [`uuid::Uuid`].
|
||||
#[tracing::instrument]
|
||||
pub async fn get_default_user() -> LauncherResult<Option<uuid::Uuid>> {
|
||||
let state = State::get().await?;
|
||||
let store = state.credentials.read().await;
|
||||
|
||||
Ok(store.default_user)
|
||||
}
|
||||
|
||||
/// Set the current default user by [`uuid::Uuid`].
|
||||
#[tracing::instrument]
|
||||
pub async fn set_default_user(user: Option<uuid::Uuid>) -> LauncherResult<()> {
|
||||
let user = match user {
|
||||
Some(user) => Some(get_user(user).await?.id),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let state = State::get().await?;
|
||||
let mut store = state.credentials.write().await;
|
||||
|
||||
store.default_user = user;
|
||||
store.save().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a user account by its [`uuid::Uuid`] from the global store.
|
||||
#[tracing::instrument]
|
||||
pub async fn remove_user(user: uuid::Uuid) -> LauncherResult<()> {
|
||||
let state = State::get().await?;
|
||||
let mut store = state.credentials.write().await;
|
||||
|
||||
store.remove(user).await?;
|
||||
|
||||
if store.default_user == Some(user) {
|
||||
set_default_user(Some(user)).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a list of user [`MinecraftCredentials`].
|
||||
#[tracing::instrument]
|
||||
pub async fn users() -> LauncherResult<Vec<MinecraftCredentials>> {
|
||||
let state = State::get().await?;
|
||||
let store = state.credentials.read().await;
|
||||
|
||||
Ok(store.users.values().cloned().collect())
|
||||
}
|
||||
|
||||
/// Get a specifc user's [`MinecraftCredentials`] by their [`uuid::Uuid`].
|
||||
/// Use [`crate::store::MinecraftState#refresh`] instead.
|
||||
#[tracing::instrument]
|
||||
pub async fn get_user(user: uuid::Uuid) -> LauncherResult<MinecraftCredentials> {
|
||||
let state = State::get().await?;
|
||||
let store = state.credentials.read().await;
|
||||
|
||||
let user = store
|
||||
.users
|
||||
.get(&user)
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to get nonexistent user with uuid {user}"))?
|
||||
.clone();
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod cluster;
|
||||
pub mod credentials;
|
||||
pub mod ingress;
|
||||
pub mod game;
|
||||
pub mod proxy;
|
||||
|
|
|
|||
|
|
@ -32,22 +32,6 @@ pub const MCLOGS_API_URL: &str = "https://api.mclo.gs/1";
|
|||
/// <https://skyclient.co/> metadata base url.
|
||||
pub const SKYCLIENT_BASE_URL: &str = "https://raw.githubusercontent.com/SkyblockClient/SkyblockClient-REPO/refs/heads/main/v1";
|
||||
|
||||
// =========== Paths ===========
|
||||
/// The public `settings.json` file used to store the global [`Settings`] state.
|
||||
///
|
||||
/// [`Settings`]: crate::store::Settings
|
||||
pub const SETTINGS_FILE: &str = "settings.json";
|
||||
/// The public `authentication.json` file used to store the global [`MinecraftAuth`] state.
|
||||
///
|
||||
/// [`MinecraftAuth`]: crate::store::MinecraftAuth
|
||||
pub const AUTH_FILE: &str = "authentication.json";
|
||||
|
||||
/// The current [`Settings`] format version, bumped for breaking changes.
|
||||
/// If updated, a config file migration logic **NEEDS** to be implemented.
|
||||
///
|
||||
/// [`Settings`]: crate::store::Settings
|
||||
pub const CURRENT_SETTINGS_FORMAT_VERSION: u32 = 2;
|
||||
|
||||
// =========== Hacky Mojang-spec OS constants ===========
|
||||
#[cfg(target_os = "windows")]
|
||||
pub const TARGET_OS: &str = "windows";
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ pub enum LauncherError {
|
|||
#[error(transparent)]
|
||||
MetadataError(#[from] crate::store::metadata::MetadataError),
|
||||
#[error(transparent)]
|
||||
DaoError(#[from] DaoError),
|
||||
#[error(transparent)]
|
||||
ClusterError(#[from] crate::api::cluster::ClusterError),
|
||||
#[error(transparent)]
|
||||
MinecraftAuthError(#[from] crate::store::credentials::MinecraftAuthError),
|
||||
#[error(transparent)]
|
||||
DaoError(#[from] DaoError),
|
||||
|
||||
#[error(transparent)]
|
||||
SerdeError(#[from] serde_json::Error),
|
||||
|
|
|
|||
1049
packages/core/src/store/credentials.rs
Normal file
1049
packages/core/src/store/credentials.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -70,6 +70,7 @@ macro_rules! dirs_impl {
|
|||
dirs_impl! {
|
||||
db_file => |this| this.base_dir.join("user_data.db"),
|
||||
settings_file => |this| this.base_dir.join("settings.json"),
|
||||
auth_file => |this| this.base_dir.join("auth.json"),
|
||||
|
||||
launcher_logs_dir => |this| this.base_dir.join("logs"),
|
||||
clusters_dir => |this| this.base_dir.join("clusters"),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ pub mod ingress;
|
|||
pub mod proxy;
|
||||
pub mod semaphore;
|
||||
pub mod metadata;
|
||||
pub mod credentials;
|
||||
mod core_options;
|
||||
mod dirs;
|
||||
mod settings;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use onelauncher_entity::setting_profiles;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{constants, send_warning, utils::io, LauncherResult};
|
||||
use crate::{send_warning, utils::io, LauncherResult};
|
||||
|
||||
use super::Dirs;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ impl Default for Settings {
|
|||
discord_enabled: false,
|
||||
enable_gamemode: false,
|
||||
max_concurrent_requests: 25,
|
||||
settings_version: constants::CURRENT_SETTINGS_FORMAT_VERSION,
|
||||
settings_version: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use tokio::sync::{OnceCell, RwLock};
|
|||
|
||||
use crate::LauncherResult;
|
||||
|
||||
use super::credentials::CredentialsStore;
|
||||
use super::Settings;
|
||||
use super::discord::DiscordRPC;
|
||||
use super::ingress::IngressProcessor;
|
||||
|
|
@ -17,6 +18,7 @@ pub struct State {
|
|||
pub ingress_processor: IngressProcessor,
|
||||
pub settings: RwLock<Settings>,
|
||||
pub db: DatabaseConnection,
|
||||
pub credentials: RwLock<CredentialsStore>,
|
||||
pub metadata: RwLock<Metadata>,
|
||||
pub rpc: Option<DiscordRPC>,
|
||||
}
|
||||
|
|
@ -40,8 +42,8 @@ impl State {
|
|||
let ingress_processor = IngressProcessor::new();
|
||||
let settings = Settings::new().await;
|
||||
let db = super::db::create_pool().await?;
|
||||
let credentials = CredentialsStore::initialize().await?;
|
||||
let metadata = Metadata::new();
|
||||
|
||||
let rpc = match DiscordRPC::initialize() {
|
||||
Ok(rpc) => Some(rpc),
|
||||
Err(err) => {
|
||||
|
|
@ -54,6 +56,7 @@ impl State {
|
|||
ingress_processor,
|
||||
settings: RwLock::new(settings),
|
||||
db,
|
||||
credentials: RwLock::new(credentials),
|
||||
metadata: RwLock::new(metadata),
|
||||
rpc,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ impl FetchSemaphore {
|
|||
}
|
||||
}
|
||||
|
||||
static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
pub(crate) static REQWEST_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
reqwest::ClientBuilder::new()
|
||||
.tcp_keepalive(Some(std::time::Duration::from_secs(15)))
|
||||
.use_rustls_tls()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue