Skip to content

Commit cd8d65b

Browse files
authored
Merge pull request #17 from bitwarden/anders/session-refreshed-event
Handle re-pair with known clients
2 parents 7511c86 + b7d9ecb commit cd8d65b

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

crates/bw-rat-client/src/clients/user_client.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ pub enum UserClientEvent {
7878
/// Domain
7979
domain: String,
8080
},
81+
/// A known/cached device reconnected — transport keys refreshed, no re-verification needed
82+
SessionRefreshed {
83+
/// The remote device's identity fingerprint
84+
fingerprint: IdentityFingerprint,
85+
},
8186
/// Client disconnected
8287
ClientDisconnected {},
8388
/// An error occurred
@@ -407,6 +412,13 @@ impl UserClient {
407412
self.session_store.cache_session(source)?;
408413
self.session_store
409414
.save_transport_state(&source, transport)?;
415+
416+
event_tx
417+
.send(UserClientEvent::SessionRefreshed {
418+
fingerprint: source,
419+
})
420+
.await
421+
.ok();
410422
} else if is_psk_connection {
411423
// PSK connection: trust established via pre-shared key, no verification needed
412424
self.transports.insert(source, transport.clone());

crates/bw-remote/src/command/listen.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ async fn run_event_loop(
462462
// Update session panel with pending label
463463
app.set_session_panel(session_info_messages(sessions, Some("New session (awaiting connection)")));
464464
}
465+
UserClientEvent::SessionRefreshed { .. } => {
466+
// Known device reconnected — clear pending label
467+
app.set_session_panel(session_info_messages(sessions, None));
468+
}
465469
_ => {}
466470
}
467471
}

crates/bw-remote/src/command/util.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ pub fn format_connect_event(event: &RemoteClientEvent) -> Option<Message> {
215215
///
216216
/// Returns `None` for events handled structurally by the caller
217217
/// (e.g., `Listening`, `CredentialRequest`, `HandshakeFingerprint`).
218+
#[allow(clippy::string_slice)]
218219
pub fn format_listen_event(event: &UserClientEvent) -> Option<Message> {
219220
match event {
220221
UserClientEvent::Listening {} => None,
@@ -315,6 +316,21 @@ pub fn format_listen_event(event: &UserClientEvent) -> Option<Message> {
315316
],
316317
)),
317318

319+
UserClientEvent::SessionRefreshed { fingerprint } => {
320+
let fp_hex = hex::encode(fingerprint.0);
321+
let short = &fp_hex[..12.min(fp_hex.len())];
322+
Some(Message::rich(
323+
MessageKind::Success,
324+
vec![
325+
Span::styled(
326+
"Known client re-paired and connected — transport keys refreshed: ",
327+
text(),
328+
),
329+
Span::styled(short.to_string(), val_style()),
330+
],
331+
))
332+
}
333+
318334
UserClientEvent::ClientDisconnected {} => {
319335
Some(Message::new(MessageKind::Info, "Client disconnected"))
320336
}

0 commit comments

Comments
 (0)