From 2c48e37f8341e0d207dd3260c439a0729464b03d Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 27 Feb 2026 10:07:30 +0000 Subject: feat: add --bunker-url to account login for non-interactive use allows non-interactive bunker:// URL login without requiring --nsec, by connecting to the remote signer and saving credentials to git config --- src/lib/login/fresh.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'src/lib') diff --git a/src/lib/login/fresh.rs b/src/lib/login/fresh.rs index 0b5922b..8d3a806 100644 --- a/src/lib/login/fresh.rs +++ b/src/lib/login/fresh.rs @@ -112,6 +112,62 @@ pub async fn fresh_login_or_signup( Ok((signer, user_ref, source)) } +/// Non-interactive login using a `bunker://` URL provided directly. +/// +/// Parses the URL, generates a fresh app key, connects to the remote signer, +/// and saves the resulting credentials to git config. +pub async fn login_with_bunker_url( + git_repo: &Option<&Repo>, + #[cfg(test)] client: Option<&MockConnect>, + #[cfg(not(test))] client: Option<&Client>, + bunker_url: &str, + save_local: bool, + signer_relays: &[String], +) -> Result<(Arc, UserRef, SignerInfoSource)> { + let url = NostrConnectURI::parse(bunker_url) + .context("invalid bunker:// URL - must be a valid bunker:// URI")?; + + let (app_key, _) = generate_nostr_connect_app(client, signer_relays)?; + + let printer = Arc::new(Mutex::new(Printer::default())); + { + let mut p = printer.lock().await; + p.println("connecting to remote signer...".to_string()); + } + + let (signer, user_public_key, bunker_uri) = + listen_for_remote_signer(&app_key, &url, printer).await?; + + let signer_info = SignerInfo::Bunker { + bunker_uri: bunker_uri.to_string(), + bunker_app_key: app_key.secret_key().to_secret_hex(), + npub: Some(user_public_key.to_bech32()?), + }; + + let _ = save_to_git_config(git_repo, &signer_info, !save_local).await; + + let user_ref = get_user_details( + &user_public_key, + client, + if let Some(git_repo) = git_repo { + Some(git_repo.get_path()?) + } else { + None + }, + false, + false, + ) + .await?; + + let source = if save_local { + SignerInfoSource::GitLocal + } else { + SignerInfoSource::GitGlobal + }; + print_logged_in_as(&user_ref, client.is_none(), &source)?; + Ok((signer, user_ref, source)) +} + pub async fn get_fresh_nsec_signer() -> Result< Option<( Arc, -- cgit v1.2.3