From 397080d94d8efdf2df95a1dc8dc82d4d7070028d Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 28 Jun 2024 17:12:21 +0100 Subject: feat(login): login with nostr address via nip46 currently using patched version of rust-nostr with function to fetch nip46 relays from nip05 providers. this patch has been merged so it will make it into the next rust-nostr release. --- Cargo.lock | 24 +++++--------- Cargo.toml | 10 +++--- src/login.rs | 89 ++++++++++++++++++++++++++++++++++----------------- test_utils/Cargo.toml | 4 +-- tests/login.rs | 5 +-- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5a8fa0..474adbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,8 +1822,7 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "nostr" version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7948938314ee0392f378ab1a5d58b4707f2207550bc410b1629a80a4f28af7d" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "aes 0.8.4", "base64 0.21.7", @@ -1852,8 +1851,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88a72f92fbd5d2514db36e07a864646f1c1f44931c4a5ea195f6961029af4b3" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-trait", "flatbuffers", @@ -1867,8 +1865,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b7bf72b02a24ccc7cf87033fa5ddfd57001c7d8c2e757321a7ca7a6df39876" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-utility", "async-wsocket", @@ -1883,8 +1880,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005915a59ee6401f23ba510c3a9ac4a07b457f80dfe1dc05cd2c8fdbde439246" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-utility", "atomic-destructor", @@ -1903,8 +1899,7 @@ dependencies = [ [[package]] name = "nostr-signer" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525574dc32fa07d64d04a6c72b534d97455b1ad954c29753c820c834c94e3704" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-utility", "nostr", @@ -1917,8 +1912,7 @@ dependencies = [ [[package]] name = "nostr-sqlite" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418555707a30105f738b3a54a1ae13ffca5e7ec10b4d27a8c20bedde636233c3" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-trait", "nostr", @@ -1932,8 +1926,7 @@ dependencies = [ [[package]] name = "nostr-zapper" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430c2527c0efd2e7f1a421b0c7df01a03b334a79c60c39cc7a1ca684f720f2bf" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-trait", "nostr", @@ -2034,8 +2027,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "nwc" version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fb91e4be3f6b872fc23c7714bbb500a58a1d59f458eb6eb9dd249fbec42fc2" +source = "git+https://github.com/DanConwayDev/nostr?branch=v0.32-get-nip46-from-nip05-address#429f3c1396f05bf08793f60ed1d92d7bc02ea438" dependencies = [ "async-utility", "nostr", diff --git a/Cargo.toml b/Cargo.toml index d41d870..a0e2d00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,11 +23,11 @@ futures = "0.3.28" git2 = "0.18.1" indicatif = "0.17.7" keyring = "2.0.5" -nostr = "0.32.0" -nostr-database = "0.32.0" -nostr-sdk = "0.32.0" -nostr-signer = "0.32.0" -nostr-sqlite = "0.32.0" +nostr = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } +nostr-database = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } +nostr-sdk = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } +nostr-signer = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } +nostr-sqlite = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } passwords = "3.1.13" scrypt = "0.11.0" serde = { version = "1.0.181", features = ["derive"] } diff --git a/src/login.rs b/src/login.rs index 218a079..38babaa 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,7 +1,10 @@ use std::{fs::create_dir_all, str::FromStr, time::Duration}; use anyhow::{bail, Context, Result}; -use nostr::{nips::nip46::NostrConnectURI, PublicKey}; +use nostr::{ + nips::{nip05::get_nip46, nip46::NostrConnectURI}, + PublicKey, +}; use nostr_database::Order; use nostr_sdk::{ Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrDatabase, NostrSigner, SingleLetterTag, @@ -348,41 +351,45 @@ async fn fresh_login( ) -> Result<(NostrSigner, UserRef)> { let mut public_key: Option = None; // prompt for nsec - let mut prompt = "login with bunker uri / nsec"; + let mut prompt = "login with nostr address / nsec"; let signer = loop { let input = Interactor::default() .input(PromptInputParms::default().with_prompt(prompt)) .context("failed to get nsec input from interactor")?; - match nostr::Keys::from_str(&input) { - Ok(key) => { - if let Err(error) = save_keys(git_repo, &key, always_save) { + if let Ok(keys) = nostr::Keys::from_str(&input) { + if let Err(error) = save_keys(git_repo, &keys, always_save) { + println!("{error}"); + } + break NostrSigner::Keys(keys); + } + let uri = if let Ok(uri) = NostrConnectURI::parse(&input) { + uri + } else if input.contains('@') { + if let Ok(uri) = fetch_nip46_uri_from_nip05(&input).await { + uri + } else { + prompt = "failed. try again with nostr address / bunker uri / nsec"; + continue; + } + } else { + prompt = "invalid. try again with nostr address / bunker uri / nsec"; + continue; + }; + let app_key = Keys::generate().secret_key()?.to_secret_hex(); + match get_nip46_signer_from_uri_and_key(&uri.to_string(), &app_key).await { + Ok(signer) => { + let pub_key = fetch_public_key(&signer).await?; + if let Err(error) = + save_bunker(git_repo, &pub_key, &uri.to_string(), &app_key, always_save) + { println!("{error}"); } - break NostrSigner::Keys(key); + public_key = Some(pub_key); + break signer; + } + Err(_) => { + prompt = "failed. try again with nostr address / bunker uri / nsec"; } - Err(_) => match NostrConnectURI::parse(&input) { - Ok(_) => { - let app_key = Keys::generate().secret_key()?.to_secret_hex(); - match get_nip46_signer_from_uri_and_key(&input, &app_key).await { - Ok(signer) => { - let pub_key = fetch_public_key(&signer).await?; - if let Err(error) = - save_bunker(git_repo, &pub_key, &input, &app_key, always_save) - { - println!("{error}"); - } - public_key = Some(pub_key); - break signer; - } - Err(_) => { - prompt = "invalid. try again with nostr address / nsec"; - } - } - } - Err(_) => { - prompt = "invalid. try again with nostr address / nsec"; - } - }, } }; let public_key = if let Some(public_key) = public_key { @@ -396,6 +403,30 @@ async fn fresh_login( Ok((signer, user_ref)) } +pub async fn fetch_nip46_uri_from_nip05(nip05: &str) -> Result { + let term = console::Term::stderr(); + term.write_line("contacting login service provider...")?; + let res = get_nip46(&nip05, None).await; + term.clear_last_lines(1)?; + match res { + Ok((signer_public_key, relays)) => { + if relays.is_empty() { + println!("nip05 provider isn't configured for remote login"); + bail!("nip05 provider isn't configured for remote login") + } + Ok(NostrConnectURI::Bunker { + signer_public_key, + relays, + secret: None, + }) + } + Err(error) => { + println!("error contacting login service provider: {error}"); + Err(error).context("error contacting login service provider") + } + } +} + fn save_bunker( git_repo: &Repo, public_key: &PublicKey, diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index 43f3629..92f4dfd 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -9,8 +9,8 @@ assert_cmd = "2.0.12" dialoguer = "0.10.4" directories = "5.0.1" git2 = "0.18.1" -nostr = "0.32.0" -nostr-sdk = "0.32.0" +nostr = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } +nostr-sdk = { git = "https://github.com/DanConwayDev/nostr", branch= "v0.32-get-nip46-from-nip05-address" } once_cell = "1.18.0" rand = "0.8" rexpect = { git = "https://github.com/rust-cli/rexpect.git", rev = "9eb61dd" } diff --git a/tests/login.rs b/tests/login.rs index 9da2a0e..4cc2dbe 100644 --- a/tests/login.rs +++ b/tests/login.rs @@ -3,7 +3,7 @@ use git::GitTestRepo; use serial_test::serial; use test_utils::*; -static EXPECTED_NSEC_PROMPT: &str = "login with bunker uri / nsec"; +static EXPECTED_NSEC_PROMPT: &str = "login with nostr address / nsec"; static EXPECTED_LOCAL_REPOSITORY_PROMPT: &str = "just for this repository?"; static EXPECTED_REQUIRE_PASSWORD_PROMPT: &str = "require password?"; static EXPECTED_SET_PASSWORD_PROMPT: &str = "encrypt with password"; @@ -909,7 +909,8 @@ mod with_offline_flag { #[test] fn prompts_for_nsec_until_valid() -> Result<()> { - let invalid_nsec_response = "invalid. try again with nostr address / nsec"; + let invalid_nsec_response = + "invalid. try again with nostr address / bunker uri / nsec"; let test_repo = GitTestRepo::default(); let mut p = CliTester::new_from_dir(&test_repo.dir, ["login", "--offline"]); -- cgit v1.2.3