@@ -130,6 +130,37 @@ impl PendingFileOps {
130130 }
131131}
132132
133+ /// Client-supplied auth credentials and user identity for the daemon.
134+ ///
135+ /// Populated by `Initialize` and `Authenticate` messages. The auth token
136+ /// is used for server API calls; the user identity is forwarded to Sentry
137+ /// so crash reports from the daemon are attributed to the connecting user.
138+ ///
139+ /// All fields are intentionally retained across proxy connection teardown
140+ /// and cleared only by daemon process exit.
141+ struct DaemonAuthContext {
142+ /// Bearer credential set by Initialize / Authenticate.
143+ auth_token : Option < String > ,
144+ /// User ID from the most recent `Initialize` handshake (Firebase UID).
145+ #[ cfg( feature = "crash_reporting" ) ]
146+ sentry_user_id : String ,
147+ /// User email from the most recent `Initialize` handshake.
148+ #[ cfg( feature = "crash_reporting" ) ]
149+ sentry_user_email : String ,
150+ }
151+
152+ impl DaemonAuthContext {
153+ fn new ( ) -> Self {
154+ Self {
155+ auth_token : None ,
156+ #[ cfg( feature = "crash_reporting" ) ]
157+ sentry_user_id : String :: new ( ) ,
158+ #[ cfg( feature = "crash_reporting" ) ]
159+ sentry_user_email : String :: new ( ) ,
160+ }
161+ }
162+ }
163+
133164/// The top-level server-side orchestrator model.
134165///
135166/// Receives `ClientMessage`s from connected proxy sessions and routes
@@ -164,13 +195,8 @@ pub struct ServerModel {
164195 executors : HashMap < SessionId , Arc < LocalCommandExecutor > > ,
165196 /// Tracks in-flight file write/delete operations and handles cleanup.
166197 pending_file_ops : PendingFileOps ,
167- /// Daemon-wide bearer credential for the identity-scoped daemon.
168- ///
169- /// The token is written by Initialize when the client supplies a
170- /// non-empty credential, or by Authenticate during token rotation. It is
171- /// intentionally retained across proxy connection teardown and cleared
172- /// only by daemon process exit.
173- auth_token : Option < String > ,
198+ /// Daemon-wide auth credentials and user identity.
199+ auth : DaemonAuthContext ,
174200}
175201
176202impl Entity for ServerModel {
@@ -195,7 +221,7 @@ impl ServerModel {
195221 host_id,
196222 executors : HashMap :: new ( ) ,
197223 pending_file_ops : PendingFileOps :: new ( ) ,
198- auth_token : None ,
224+ auth : DaemonAuthContext :: new ( ) ,
199225 } ;
200226 // Subscribe to FileModel and RepoMetadataModel events
201227 // file operation results and repo metadata pushes are forwarded to all
@@ -530,18 +556,15 @@ impl ServerModel {
530556 // Update crash reporting based on client-supplied preferences.
531557 #[ cfg( feature = "crash_reporting" ) ]
532558 {
559+ if !msg. user_id . is_empty ( ) {
560+ self . auth . sentry_user_id = msg. user_id . clone ( ) ;
561+ }
562+ if !msg. user_email . is_empty ( ) {
563+ self . auth . sentry_user_email = msg. user_email . clone ( ) ;
564+ }
565+
533566 if msg. crash_reporting_enabled {
534- if !msg. user_id . is_empty ( ) {
535- crate :: crash_reporting:: set_user_id (
536- crate :: auth:: UserUid :: new ( & msg. user_id ) ,
537- if msg. user_email . is_empty ( ) {
538- None
539- } else {
540- Some ( msg. user_email )
541- } ,
542- ctx,
543- ) ;
544- }
567+ self . apply_sentry_user_id ( ctx) ;
545568 } else {
546569 crate :: crash_reporting:: uninit_sentry ( ) ;
547570 }
@@ -562,7 +585,25 @@ impl ServerModel {
562585 /// Extracted so unit tests can call it without a `ModelContext`.
563586 fn apply_initialize_auth ( & mut self , msg : & Initialize ) {
564587 if !msg. auth_token . is_empty ( ) {
565- self . auth_token = Some ( msg. auth_token . clone ( ) ) ;
588+ self . auth . auth_token = Some ( msg. auth_token . clone ( ) ) ;
589+ }
590+ }
591+
592+ /// Sets the Sentry user identity from the stored `DaemonAuthContext`.
593+ /// Called both during `Initialize` and when re-enabling crash reporting
594+ /// via `UpdatePreferences`.
595+ #[ cfg( feature = "crash_reporting" ) ]
596+ fn apply_sentry_user_id ( & self , ctx : & mut warpui:: AppContext ) {
597+ if !self . auth . sentry_user_id . is_empty ( ) {
598+ crate :: crash_reporting:: set_user_id (
599+ crate :: auth:: UserUid :: new ( & self . auth . sentry_user_id ) ,
600+ if self . auth . sentry_user_email . is_empty ( ) {
601+ None
602+ } else {
603+ Some ( self . auth . sentry_user_email . clone ( ) )
604+ } ,
605+ ctx,
606+ ) ;
566607 }
567608 }
568609
@@ -580,10 +621,9 @@ impl ServerModel {
580621 #[ cfg( feature = "crash_reporting" ) ]
581622 {
582623 if msg. crash_reporting_enabled {
583- // Re-enable if not already initialized. Use stored auth info.
584- // If we don't have user info, init_sentry with no user is fine.
585624 if !crate :: crash_reporting:: is_initialized ( ) {
586625 crate :: crash_reporting:: init ( ctx) ;
626+ self . apply_sentry_user_id ( ctx) ;
587627 }
588628 } else {
589629 crate :: crash_reporting:: uninit_sentry ( ) ;
@@ -598,11 +638,11 @@ impl ServerModel {
598638 log:: warn!( "Received Authenticate notification with empty auth token; ignoring" ) ;
599639 return ;
600640 }
601- self . auth_token = Some ( msg. auth_token ) ;
641+ self . auth . auth_token = Some ( msg. auth_token ) ;
602642 }
603643
604644 pub fn auth_token ( & self ) -> Option < & str > {
605- self . auth_token . as_deref ( )
645+ self . auth . auth_token . as_deref ( )
606646 }
607647
608648 /// Handles `Abort` by cancelling the in-progress request it targets.
0 commit comments