Skip to content

Commit 91c8f0a

Browse files
committed
connect: fix NostrConnectRequest::Connect message handling
Pull-Request: #1111 Signed-off-by: Yuki Kishimoto <[email protected]>
1 parent 19756ee commit 91c8f0a

File tree

4 files changed

+53
-92
lines changed

4 files changed

+53
-92
lines changed

signer/nostr-connect/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
2424
-->
2525

26+
## Unreleased
27+
28+
### Fixed
29+
30+
- Fix `NostrConnectRequest::Connect` message handling (https://github.com/rust-nostr/nostr/pull/1111)
31+
2632
## v0.43.0 - 2025/07/28
2733

2834
### Breaking changes

signer/nostr-connect/src/client.rs

Lines changed: 29 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,41 @@ use crate::error::Error;
2323
#[derive(Debug, Clone)]
2424
pub struct NostrConnect {
2525
uri: NostrConnectURI,
26-
app_keys: Keys,
26+
client_keys: Keys,
2727
remote_signer_public_key: OnceCell<PublicKey>,
2828
user_public_key: OnceCell<PublicKey>,
2929
pool: RelayPool,
3030
timeout: Duration,
3131
opts: RelayOptions,
32-
secret: Option<String>,
3332
auth_url_handler: Option<Arc<dyn AuthUrlHandler>>,
3433
}
3534

3635
impl NostrConnect {
3736
/// Construct Nostr Connect client
3837
pub fn new(
3938
uri: NostrConnectURI,
40-
app_keys: Keys,
39+
client_keys: Keys,
4140
timeout: Duration,
4241
opts: Option<RelayOptions>,
4342
) -> Result<Self, Error> {
4443
// Check app keys
4544
if let NostrConnectURI::Client { public_key, .. } = &uri {
46-
if public_key != &app_keys.public_key {
45+
if public_key != &client_keys.public_key {
4746
return Err(Error::PublicKeyNotMatchAppKeys);
4847
}
4948
}
5049

5150
Ok(Self {
52-
app_keys,
51+
uri,
52+
client_keys,
5353
// NOT set the remote_signer_public_key, also if bunker URI!
5454
// If you already set remote_signer_public_key, you'll need another field to know if boostrap was already done.
55+
// If the URI is bunker, the remote_signer_public_key is set in the bootstrap method.
5556
remote_signer_public_key: OnceCell::new(),
5657
user_public_key: OnceCell::new(),
5758
pool: RelayPool::default(),
5859
timeout,
5960
opts: opts.unwrap_or_default(),
60-
secret: uri.secret().map(|secret| secret.to_string()),
61-
uri,
6261
auth_url_handler: None,
6362
})
6463
}
@@ -85,10 +84,10 @@ impl NostrConnect {
8584
/// #[tokio::main]
8685
/// async fn main() -> Result<()> {
8786
/// let uri = NostrConnectURI::parse("bunker://79dff8f82963424e0bb02708a22e44b4980893e3a4be0fa3cb60a43b946764e3?relay=wss://relay.nsec.app")?;
88-
/// let app_keys = Keys::generate();
87+
/// let client_keys = Keys::generate();
8988
/// let timeout = Duration::from_secs(60);
9089
///
91-
/// let mut connect = NostrConnect::new(uri, app_keys, timeout, None)?;
90+
/// let mut connect = NostrConnect::new(uri, client_keys, timeout, None)?;
9291
///
9392
/// // Set auth_url handler
9493
/// connect.auth_url_handler(MyAuthUrlHandler);
@@ -128,32 +127,7 @@ impl NostrConnect {
128127
let remote_signer_public_key: PublicKey = match self.uri.remote_signer_public_key() {
129128
Some(public_key) => *public_key,
130129
None => {
131-
match get_remote_signer_public_key(&self.app_keys, notifications, self.timeout)
132-
.await?
133-
{
134-
GetRemoteSignerPublicKey::RemoteOnly(public_key) => public_key,
135-
GetRemoteSignerPublicKey::WithUserPublicKey { remote, user } => {
136-
// Check if user public key was already set
137-
match self.user_public_key.get().copied() {
138-
Some(set_user_public_key) => {
139-
// User public key was already set but not match the one received by the signer.
140-
if set_user_public_key != user {
141-
return Err(Error::UserPublicKeyNotMatch {
142-
expected: Box::new(user),
143-
local: Box::new(set_user_public_key),
144-
});
145-
}
146-
}
147-
None => {
148-
// No user public key in cell, set it.
149-
self.user_public_key.set(user)?;
150-
}
151-
}
152-
153-
// Return remote signer public key
154-
remote
155-
}
156-
}
130+
get_remote_signer_public_key(&self.client_keys, notifications, self.timeout).await?
157131
}
158132
};
159133

@@ -166,7 +140,7 @@ impl NostrConnect {
166140
}
167141

168142
async fn subscribe(&self) -> Result<Receiver<RelayPoolNotification>, Error> {
169-
let public_key: PublicKey = self.app_keys.public_key();
143+
let public_key: PublicKey = self.client_keys.public_key();
170144

171145
let filter = Filter::new()
172146
.pubkey(public_key)
@@ -183,10 +157,10 @@ impl NostrConnect {
183157
Ok(notifications)
184158
}
185159

186-
/// Get local app keys
160+
/// Get client keys, used for communicating with the remote signer
187161
#[inline]
188162
pub fn local_keys(&self) -> &Keys {
189-
&self.app_keys
163+
&self.client_keys
190164
}
191165

192166
/// Get signer relays
@@ -200,7 +174,8 @@ impl NostrConnect {
200174
Ok(NostrConnectURI::Bunker {
201175
remote_signer_public_key: *self.remote_signer_public_key().await?,
202176
relays: self.relays().to_vec(),
203-
secret: self.secret.clone(),
177+
// Not use the secret. The secret is used only for the first connection.
178+
secret: None,
204179
})
205180
}
206181

@@ -238,16 +213,16 @@ impl NostrConnect {
238213
req: NostrConnectRequest,
239214
remote_signer_public_key: PublicKey,
240215
) -> Result<ResponseResult, Error> {
241-
let secret_key: &SecretKey = self.app_keys.secret_key();
216+
let secret_key: &SecretKey = self.client_keys.secret_key();
242217

243218
// Convert request to event
244219
let msg = NostrConnectMessage::request(&req);
245220
tracing::debug!("Sending '{msg}' NIP46 message");
246221

247222
let req_id = msg.id().to_string();
248223
let event: Event =
249-
EventBuilder::nostr_connect(&self.app_keys, remote_signer_public_key, msg)?
250-
.sign_with_keys(&self.app_keys)?;
224+
EventBuilder::nostr_connect(&self.client_keys, remote_signer_public_key, msg)?
225+
.sign_with_keys(&self.client_keys)?;
251226

252227
let mut notifications = self.pool.notifications();
253228

@@ -309,8 +284,8 @@ impl NostrConnect {
309284
/// Connect msg
310285
async fn connect(&self, remote_signer_public_key: PublicKey) -> Result<(), Error> {
311286
let req = NostrConnectRequest::Connect {
312-
public_key: remote_signer_public_key,
313-
secret: self.secret.clone(),
287+
remote_signer_public_key,
288+
secret: self.uri.secret().map(|secret| secret.to_string()),
314289
};
315290
let res = self
316291
.send_request_with_pk(req, remote_signer_public_key)
@@ -392,23 +367,18 @@ impl NostrConnect {
392367
}
393368
}
394369

395-
enum GetRemoteSignerPublicKey {
396-
WithUserPublicKey { remote: PublicKey, user: PublicKey },
397-
RemoteOnly(PublicKey),
398-
}
399-
400370
async fn get_remote_signer_public_key(
401-
app_keys: &Keys,
371+
client_keys: &Keys,
402372
mut notifications: Receiver<RelayPoolNotification>,
403373
timeout: Duration,
404-
) -> Result<GetRemoteSignerPublicKey, Error> {
374+
) -> Result<PublicKey, Error> {
405375
time::timeout(Some(timeout), async {
406376
while let Ok(notification) = notifications.recv().await {
407377
if let RelayPoolNotification::Event { event, .. } = notification {
408378
if event.kind == Kind::NostrConnect {
409379
// Decrypt content
410380
let msg: String = nip44::decrypt(
411-
app_keys.secret_key(),
381+
client_keys.secret_key(),
412382
&event.pubkey,
413383
event.content.as_str(),
414384
)?;
@@ -418,27 +388,13 @@ async fn get_remote_signer_public_key(
418388
// Parse message
419389
let msg: NostrConnectMessage = NostrConnectMessage::from_json(msg)?;
420390

421-
// Match message
422-
match &msg {
423-
NostrConnectMessage::Request { .. } => {
424-
if let Ok(NostrConnectRequest::Connect { public_key, .. }) =
425-
msg.to_request()
426-
{
427-
return Ok(GetRemoteSignerPublicKey::WithUserPublicKey {
428-
remote: event.pubkey,
429-
user: public_key,
430-
});
431-
}
432-
}
433-
NostrConnectMessage::Response { .. } => {
434-
if let Ok(NostrConnectResponse {
435-
result: Some(ResponseResult::Ack),
436-
error: None,
437-
}) = msg.to_response(NostrConnectMethod::Connect)
438-
{
439-
return Ok(GetRemoteSignerPublicKey::RemoteOnly(event.pubkey));
440-
}
441-
}
391+
// Check if it's a `connect` response
392+
if let Ok(NostrConnectResponse {
393+
result: Some(ResponseResult::Ack),
394+
error: None,
395+
}) = msg.to_response(NostrConnectMethod::Connect)
396+
{
397+
return Ok(event.pubkey);
442398
}
443399
}
444400
}

signer/nostr-connect/src/error.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ pub enum Error {
3737
UnexpectedUri,
3838
/// Public key not match
3939
PublicKeyNotMatchAppKeys,
40-
/// User public key not match
41-
// TODO: remove these `Box<T>`. Currently clippy return the following warning: "the `Err`-variant returned from this function is very large"
42-
UserPublicKeyNotMatch {
43-
/// The expected user public key, sent by the signer
44-
expected: Box<PublicKey>,
45-
/// The local set user public key
46-
local: Box<PublicKey>,
47-
},
4840
}
4941

5042
impl std::error::Error for Error {}
@@ -63,10 +55,6 @@ impl fmt::Display for Error {
6355
Self::Timeout => f.write_str("timeout"),
6456
Self::UnexpectedUri => f.write_str("unexpected URI"),
6557
Self::PublicKeyNotMatchAppKeys => f.write_str("public key not match app keys"),
66-
Self::UserPublicKeyNotMatch { expected, local } => write!(
67-
f,
68-
"user public key not match: expected={expected}, local={local}"
69-
),
7058
}
7159
}
7260
}

signer/nostr-connect/src/signer.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl NostrConnectRemoteSigner {
107107

108108
async fn send_connect_ack(&self, public_key: PublicKey) -> Result<(), Error> {
109109
let req: NostrConnectRequest = NostrConnectRequest::Connect {
110-
public_key: self.keys.user.public_key(),
110+
remote_signer_public_key: self.keys.signer.public_key(),
111111
secret: self.secret.clone(),
112112
};
113113

@@ -194,14 +194,25 @@ impl NostrConnectRemoteSigner {
194194
.approve(&event.pubkey, &req)
195195
{
196196
match req {
197-
NostrConnectRequest::Connect { secret, .. } => {
198-
if self.match_secret(secret) {
199-
NostrConnectResponse::with_result(
200-
ResponseResult::Ack,
201-
)
197+
NostrConnectRequest::Connect {
198+
remote_signer_public_key,
199+
secret,
200+
} => {
201+
if remote_signer_public_key
202+
== self.keys.signer.public_key
203+
{
204+
if self.match_secret(secret) {
205+
NostrConnectResponse::with_result(
206+
ResponseResult::Ack,
207+
)
208+
} else {
209+
NostrConnectResponse::with_error(
210+
"Secret not match",
211+
)
212+
}
202213
} else {
203214
NostrConnectResponse::with_error(
204-
"Secret not match",
215+
"Remote signer public key not match",
205216
)
206217
}
207218
}

0 commit comments

Comments
 (0)