diff --git a/.env b/.env deleted file mode 100644 index d21acab..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -WEBKIT_DISABLE_DMABUF_RENDERER=1 \ No newline at end of file diff --git a/app.vue b/app.vue index 4b92728..c5500da 100644 --- a/app.vue +++ b/app.vue @@ -23,6 +23,9 @@ switch (state.status) { case AppStatus.SignedOut: router.push("/auth"); break; + case AppStatus.SignedInNeedsReauth: + router.push("/auth/signedout"); + break; } listen("auth/processing", () => { diff --git a/components/Header.vue b/components/Header.vue index 2b16acf..dfe8d0b 100644 --- a/components/Header.vue +++ b/components/Header.vue @@ -1,9 +1,9 @@ diff --git a/components/InitiateAuthModule.vue b/components/InitiateAuthModule.vue new file mode 100644 index 0000000..00627e4 --- /dev/null +++ b/components/InitiateAuthModule.vue @@ -0,0 +1,93 @@ + + + diff --git a/components/WindowControl.vue b/components/WindowControl.vue index 3610162..f777504 100644 --- a/components/WindowControl.vue +++ b/components/WindowControl.vue @@ -1,28 +1,7 @@ diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2083da5..474c913 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -931,7 +931,9 @@ version = "0.1.0" dependencies = [ "ciborium", "directories", + "hex", "log", + "openssl", "os_info", "rayon", "reqwest", @@ -945,6 +947,7 @@ dependencies = [ "tauri-plugin-shell", "tauri-plugin-single-instance", "url", + "uuid", "webbrowser", "zstd", ] @@ -3203,9 +3206,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "once_cell", "rustls-pki-types", @@ -4613,7 +4616,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.15", + "rand 0.8.5", "serde", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1cd046f83ea2c4e920d6ee9f7c3537ef928d75dce5d84a87c2c5d6b3999a3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index a26e11a..1e2d353 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -35,6 +35,18 @@ os_info = "3.8.2" tauri-plugin-deep-link = "2" log = "0.4.22" structured-logger = "1.0.3" +hex = "0.4.3" + +[dependencies.uuid] +version = "1.10.0" +features = [ + "v4", # Lets you generate random UUIDs + "fast-rng", # Use a faster (but still sufficiently random) RNG + "macro-diagnostics", # Enable better diagnostics for compile-time UUIDs +] + +[dependencies.openssl] +version = "0.10.66" [dependencies.rustbreak] version = "2" @@ -43,4 +55,3 @@ features = ["bin_enc"] # You can also use "yaml_enc" or "bin_enc" [dependencies.reqwest] version = "0.12" features = ["json", "blocking"] - diff --git a/src-tauri/src/auth.rs b/src-tauri/src/auth.rs index 1c7403b..d0ff6f8 100644 --- a/src-tauri/src/auth.rs +++ b/src-tauri/src/auth.rs @@ -1,14 +1,22 @@ use std::{ borrow::{Borrow, BorrowMut}, + fmt::format, sync::Mutex, }; use log::info; +use openssl::{ + ec::EcKey, + hash::MessageDigest, + pkey::PKey, + sign::{self, Signer}, +}; use serde::{Deserialize, Serialize}; use tauri::{App, AppHandle, Emitter, Error, EventLoopMessage, Manager, Wry}; use url::Url; +use uuid::Uuid; -use crate::{data::DatabaseCerts, AppState, AppStatus, User, DB}; +use crate::{data::DatabaseAuth, AppState, AppStatus, User, DB}; #[derive(Serialize)] struct InitiateRequestBody { @@ -41,6 +49,52 @@ macro_rules! unwrap_or_return { }; } +pub fn sign_nonce(private_key: String, nonce: String) -> Result { + let client_private_key = EcKey::private_key_from_pem(private_key.as_bytes()).unwrap(); + let pkey_private_key = PKey::from_ec_key(client_private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey_private_key).unwrap(); + signer.update(nonce.as_bytes()).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let hex_signature = hex::encode(signature); + + return Ok(hex_signature); +} + +pub fn generate_authorization_header() -> String { + let certs = { + let db = DB.borrow_data().unwrap(); + db.auth.clone().unwrap() + }; + + let nonce = Uuid::new_v4().to_string(); + let signature = sign_nonce(certs.private, nonce.clone()).unwrap(); + + return format!("Nonce {} {} {}", certs.clientId, nonce, signature); +} + +pub fn fetch_user() -> Result { + let base_url = { + let handle = DB.borrow_data().unwrap(); + Url::parse(&handle.base_url).unwrap() + }; + + let endpoint = base_url.join("/api/v1/client/user").unwrap(); + let header = generate_authorization_header(); + + let client = reqwest::blocking::Client::new(); + let response = client + .get(endpoint.to_string()) + .header("Authorization", header) + .send() + .unwrap(); + + let user = response.json::().unwrap(); + + return Ok(user); +} + pub fn recieve_handshake(app: AppHandle, path: String) { // Tell the app we're processing app.emit("auth/processing", ()).unwrap(); @@ -63,7 +117,7 @@ pub fn recieve_handshake(app: AppHandle, path: String) { token: token.to_string(), }; - let endpoint = unwrap_or_return!(base_url.join("/api/v1/client/handshake"), app); + let endpoint = unwrap_or_return!(base_url.join("/api/v1/client/auth/handshake"), app); let client = reqwest::blocking::Client::new(); let response = unwrap_or_return!(client.post(endpoint).json(&body).send(), app); info!("server responded with {}", response.status()); @@ -71,9 +125,10 @@ pub fn recieve_handshake(app: AppHandle, path: String) { { let mut handle = DB.borrow_data_mut().unwrap(); - handle.certs = Some(DatabaseCerts { + handle.auth = Some(DatabaseAuth { private: response_struct.private, cert: response_struct.certificate, + clientId: response_struct.id, }); drop(handle); DB.save().unwrap(); @@ -86,6 +141,8 @@ pub fn recieve_handshake(app: AppHandle, path: String) { } app.emit("auth/finished", ()).unwrap(); + + fetch_user().unwrap(); } #[tauri::command] @@ -97,7 +154,7 @@ pub async fn auth_initiate<'a>() -> Result<(), String> { let current_os_info = os_info::get(); - let endpoint = base_url.join("/api/v1/client/initiate").unwrap(); + let endpoint = base_url.join("/api/v1/client/auth/initiate").unwrap(); let body = InitiateRequestBody { name: format!("Drop Desktop Client"), platform: current_os_info.os_type().to_string(), @@ -124,10 +181,13 @@ pub fn setup() -> Result<(AppStatus, Option), Error> { let data = DB.borrow_data().unwrap(); // If we have certs, exit for now - if data.certs.is_some() { - // TODO: check if it's still valid, and fetch user information - info!("have existing certs, assuming logged in..."); - return Ok((AppStatus::SignedInNeedsReauth, None)); + if data.auth.is_some() { + let user_result = fetch_user(); + if user_result.is_err() { + return Ok((AppStatus::SignedInNeedsReauth, None)); + + } + return Ok((AppStatus::SignedIn, Some(user_result.unwrap()))) } drop(data); diff --git a/src-tauri/src/data.rs b/src-tauri/src/data.rs index 4e52be4..2dd9ebd 100644 --- a/src-tauri/src/data.rs +++ b/src-tauri/src/data.rs @@ -7,14 +7,15 @@ use serde::Deserialize; use crate::DB; #[derive(serde::Serialize, Clone, Deserialize)] -pub struct DatabaseCerts { +pub struct DatabaseAuth { pub private: String, pub cert: String, + pub clientId: String, } #[derive(serde::Serialize, Clone, Deserialize)] pub struct Database { - pub certs: Option, + pub auth: Option, pub base_url: String, } @@ -24,7 +25,7 @@ pub type DatabaseInterface = pub fn setup() -> DatabaseInterface { let db_path = BaseDirs::new().unwrap().data_dir().join("drop"); let default = Database { - certs: None, + auth: None, base_url: "".to_string(), }; let db = match fs::exists(db_path.clone()).unwrap() { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4f7d644..fadfc0c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -13,7 +13,7 @@ use auth::{auth_initiate, recieve_handshake}; use data::DatabaseInterface; use log::info; use remote::use_remote; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use structured_logger::{json::new_writer, Builder}; use tauri_plugin_deep_link::DeepLinkExt; @@ -24,7 +24,7 @@ pub enum AppStatus { SignedIn, SignedInNeedsReauth, } -#[derive(Clone, Copy, Serialize)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct User {} #[derive(Clone, Copy, Serialize)]