Auth bindings (#58)

* basic framework. still has errors

* added functionality for main endpoints + some structuring

* formatting

* unused code

* mimicked CLI function with wait_for process

* added basic auth bindings

* made PR changes, added playground

* cargo fmt

* removed missed println

* misc tests fixes

* cargo fmt

* added windows support

* cargo fmt

* all OS use dunce

* restructured profile slightly; fixed mac bug

* profile changes, new main.rs

* fixed requested pr + canonicaliation bug

* fixed regressed bug in ui

* fixed regressed bugs

* fixed git error

* typo

* ran prettier

* clippy

* playground clippy

* ported profile loading fix

* profile change for real, url println and clippy

* PR changes

* auth bindings + semisynch flow

* fixed dropping task error

* prettier, eslint, clippy

* removed debugging modifications

* removed unused function that eslinter missed :(

* fixed settings not being released

---------

Co-authored-by: Wyatt <wyatt@modrinth.com>
This commit is contained in:
Wyatt Verchere
2023-03-31 18:44:26 -07:00
committed by GitHub
parent f48959a816
commit 6a05276a21
14 changed files with 447 additions and 46 deletions

View File

@@ -7,30 +7,22 @@ use dunce::canonicalize;
use std::path::Path;
use theseus::{prelude::*, profile_create::profile_create};
use tokio::process::Child;
use tokio::sync::{oneshot, RwLockWriteGuard};
use tokio::sync::RwLockWriteGuard;
// We use this function directly to call authentication procedure
// Note: "let url = match url" logic is handled differently, so that if there is a rate limit in the other set causing that one to end early,
// we can see the error message in this thread rather than a Recv error on 'rx' when the receiver is mysteriously droppped
// A simple Rust implementation of the authentication run
// 1) call the authenticate_begin_flow() function to get the URL to open (like you would in the frontend)
// 2) open the URL in a browser
// 3) call the authenticate_await_complete_flow() function to get the credentials (like you would in the frontend)
pub async fn authenticate_run() -> theseus::Result<Credentials> {
println!("Adding new user account to Theseus");
println!("A browser window will now open, follow the login flow there.");
let url = auth::authenticate_begin_flow().await?;
let (tx, rx) = oneshot::channel::<url::Url>();
let flow = tokio::spawn(auth::authenticate(tx));
let url = rx.await;
let url = match url {
Ok(url) => url,
Err(e) => {
flow.await.unwrap()?;
return Err(e.into());
}
};
println!("URL {}", url.as_str());
webbrowser::open(url.as_str())?;
let credentials = flow.await.unwrap()?;
let credentials = auth::authenticate_await_complete_flow().await?;
State::sync().await?;
println!("Logged in user {}.", credentials.username);
Ok(credentials)
}
@@ -92,28 +84,44 @@ async fn main() -> theseus::Result<()> {
.await?;
State::sync().await?;
println!("Authenticating.");
// Attempt to create credentials and run.
let proc_lock = match authenticate_run().await {
// Attempt to get the default user, if it exists, and refresh their credentials
let default_user_uuid = {
let settings = st.settings.read().await;
settings.default_user.clone()
};
let credentials = if let Some(uuid) = default_user_uuid {
println!("Attempting to refresh existing authentication.");
auth::refresh(uuid, false).await
} else {
println!("Freshly authenticating.");
authenticate_run().await
};
// Check attempt to get Credentials
// If successful, run the profile and store the RwLock to the process
let proc_lock = match credentials {
Ok(credentials) => {
println!("Running.");
println!("Preparing to run Minecraft.");
profile::run(&canonicalize(&profile_path)?, &credentials).await
}
Err(e) => {
// If Hydra could not be accessed, for testing, attempt to load credentials from disk and do the same
println!("Could not authenticate: {}.\nAttempting stored authentication.",e);
// Attempt to load credentials if Hydra is down/rate limit hit
let users = auth::users().await.unwrap();
let credentials = users.first().unwrap();
println!("Running.");
let users = auth::users().await.expect(
"Could not access any stored users- state was dropped.",
);
let credentials = users
.first()
.expect("Hydra failed, and no stored users were found.");
println!("Preparing to run Minecraft.");
profile::run(&canonicalize(&profile_path)?, credentials).await
}
}?;
println!("Started. Waiting...");
// Spawn a thread and hold the lock to the process until it ends
println!("Started Minecraft. Waiting for process to end...");
let mut proc: RwLockWriteGuard<Child> = proc_lock.write().await;
profile::wait_for(&mut proc).await?;
// Run MC
Ok(())
}