diff --git a/Cargo.lock b/Cargo.lock index 29a7d4e..cf9bfbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,6 +144,7 @@ dependencies = [ "pubky", "pubky-common", "rpassword", + "tokio", "url", ] @@ -1011,6 +1012,24 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-util" version = "0.1.6" @@ -1216,13 +1235,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1250,16 +1270,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.36.1" @@ -1569,6 +1579,54 @@ dependencies = [ "psl-types", ] +[[package]] +name = "quinn" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.36" @@ -1688,6 +1746,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", @@ -1696,19 +1755,40 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rpassword" version = "7.3.1" @@ -1736,6 +1816,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1745,6 +1831,47 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.23.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -2107,34 +2234,44 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.11" @@ -2349,6 +2486,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.2" @@ -2469,6 +2612,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/examples/authz/authenticator/Cargo.toml b/examples/authz/authenticator/Cargo.toml index 2938b6d..6bce048 100644 --- a/examples/authz/authenticator/Cargo.toml +++ b/examples/authz/authenticator/Cargo.toml @@ -9,4 +9,5 @@ clap = { version = "4.5.16", features = ["derive"] } pubky = { version = "0.1.0", path = "../../../pubky" } pubky-common = { version = "0.1.0", path = "../../../pubky-common" } rpassword = "7.3.1" +tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } url = "2.5.2" diff --git a/examples/authz/authenticator/src/main.rs b/examples/authz/authenticator/src/main.rs index 2643d7d..b5464e3 100644 --- a/examples/authz/authenticator/src/main.rs +++ b/examples/authz/authenticator/src/main.rs @@ -1,40 +1,79 @@ use anyhow::Result; use clap::Parser; +use pubky::PubkyClient; +use pubky_common::{auth::AuthToken, capabilities::Capability, crypto::PublicKey}; use std::path::PathBuf; use url::Url; +/// local testnet HOMESERVER +const HOMESERVER: &str = "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"; + #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Cli { + /// Path to a recovery_file of the Pubky you want to sign in with + recovery_file: PathBuf, + /// Pubky Auth url url: Url, - - /// Path to a recovery_file of the Pubky you want to sign in with - // #[arg(short, long, value_name = "FILE")] - recovery_file: PathBuf, - // /// Mutable data public key. - // public_key: String, } -fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<()> { let cli = Cli::parse(); let url = cli.url; - dbg!(url); + + let mut required_capabilities = vec![]; + let mut relay = "".to_string(); + let client_secret: [u8; 32]; + + for (name, value) in url.query_pairs() { + if name == "relay" { + relay = value.to_string(); + } + if name == "secret" { + // client_secret = value.to_string(); + } + if name == "capabilities" { + println!("\nRequired Capabilities:"); + + for cap_str in value.split(',') { + if let Ok(cap) = Capability::try_from(cap_str) { + println!(" {} : {:?}", cap.resource, cap.abilities); + required_capabilities.push(cap) + }; + } + } + } let recovery_file = std::fs::read(&cli.recovery_file)?; - println!("Successfully opened recovery file"); + // println!("\nSuccessfully opened recovery file"); - // // println!("Enter Pubky Auth URL to start the consent form:"); - // // let pubky_auth_url = rl.readline("> ")?; - // // dbg!(pubky_auth_url); + // === Consent form === - println!("Enter your recovery_file's passphrase to confirm:"); + println!("\nEnter your recovery_file's passphrase to confirm:"); let passphrase = rpassword::read_password()?; let keypair = pubky_common::recovery_file::decrypt_recovery_file(&recovery_file, &passphrase)?; println!("Successfully decrypted recovery file..."); + let client = PubkyClient::testnet(); + + // For the purposes of this demo, we need to make sure + // the user has an account on the local homeserver. + if client.signin(&keypair).await.is_err() { + client + .signup(&keypair, &PublicKey::try_from(HOMESERVER).unwrap()) + .await?; + }; + + client + .authorize(&keypair, required_capabilities, [0; 32], &relay) + .await?; + + println!("Sending AuthToken to the client..."); + Ok(()) } diff --git a/pubky/src/native.rs b/pubky/src/native.rs index ecae6b0..d782f08 100644 --- a/pubky/src/native.rs +++ b/pubky/src/native.rs @@ -69,6 +69,13 @@ impl PubkyClient { PubkyClientBuilder::default() } + pub fn testnet() -> Self { + Self::test(&Testnet { + bootstrap: vec!["localhost:6881".to_string()], + nodes: vec![], + }) + } + pub fn test(testnet: &Testnet) -> Self { let pkarr = PkarrClient::builder() .dht_settings(DhtSettings { diff --git a/pubky/src/shared/auth.rs b/pubky/src/shared/auth.rs index 11b312e..abadc6c 100644 --- a/pubky/src/shared/auth.rs +++ b/pubky/src/shared/auth.rs @@ -1,7 +1,8 @@ use reqwest::{Method, StatusCode}; use pkarr::{Keypair, PublicKey}; -use pubky_common::{auth::AuthToken, capabilities::Capability, session::Session}; +use pubky_common::{auth::AuthToken, capabilities::Capability, crypto::encrypt, session::Session}; +use url::Url; use crate::{error::Result, PubkyClient}; @@ -20,13 +21,13 @@ impl PubkyClient { let homeserver = homeserver.to_string(); let Endpoint { - public_key: audience, + public_key: server, mut url, } = self.resolve_endpoint(&homeserver).await?; url.set_path("/signup"); - let body = AuthToken::sign(keypair, &audience, vec![Capability::root()]).serialize(); + let body = AuthToken::sign(keypair, &server, vec![Capability::root()]).serialize(); let response = self .request(Method::POST, url.clone()) @@ -83,13 +84,13 @@ impl PubkyClient { let pubky = keypair.public_key(); let Endpoint { - public_key: audience, + public_key: homeserver, mut url, } = self.resolve_pubky_homeserver(&pubky).await?; url.set_path("/session"); - let body = AuthToken::sign(keypair, &audience, vec![Capability::root()]).serialize(); + let body = AuthToken::sign(keypair, &homeserver, vec![Capability::root()]).serialize(); let response = self.request(Method::POST, url).body(body).send().await?; @@ -97,6 +98,39 @@ impl PubkyClient { Ok(()) } + + pub async fn authorize( + &self, + keypair: &Keypair, + capabilities: Vec, + client_secret: [u8; 32], + relay: &str, + ) -> Result<()> { + let pubky = keypair.public_key(); + let Endpoint { + public_key: homeserver, + .. + } = self.resolve_pubky_homeserver(&pubky).await?; + + let token = AuthToken::sign(keypair, &homeserver, capabilities); + + let encrypted_token = encrypt(&token.serialize(), &client_secret)?; + + let mut callback = Url::parse(relay)?; + let mut path_segments = callback.path_segments_mut().unwrap(); + path_segments.push("8Y69yafXgEMafLJKoJ_Ht5zPOVMWuZx_HfKY03U4MTI"); + + drop(path_segments); + + dbg!(callback.to_string()); + + self.request(Method::POST, callback) + .body(encrypted_token) + .send() + .await?; + + Ok(()) + } } #[cfg(test)]