feat(pubky): add PubkyClient::authorize()

This commit is contained in:
nazeh
2024-09-02 00:22:08 +03:00
parent cd32df075b
commit 25f757f94d
5 changed files with 270 additions and 37 deletions

190
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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(())
}

View File

@@ -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 {

View File

@@ -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<Capability>,
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)]