@@ -366,4 +366,56 @@ describe('SecurityPage', () => {
366366 }
367367 } ) ;
368368 } ) ;
369+
370+ it ( 'does not leak the previous user device activity across a user switch' , async ( ) => {
371+ const makeSession = ( sessionId : string , city : string ) =>
372+ ( {
373+ pathRoot : '/me/sessions' ,
374+ id : sessionId ,
375+ status : 'active' ,
376+ expireAt : '2022-12-01T01:55:44.636Z' ,
377+ abandonAt : '2022-12-24T01:55:44.636Z' ,
378+ lastActiveAt : '2022-11-24T12:11:49.328Z' ,
379+ latestActivity : {
380+ id : 'sess_activity_1' ,
381+ deviceType : 'Macintosh' ,
382+ browserName : 'Chrome' ,
383+ browserVersion : '107.0.0.0' ,
384+ country : 'Greece' ,
385+ city,
386+ isMobile : false ,
387+ } ,
388+ actor : null ,
389+ revoke : vi . fn ( ) . mockResolvedValue ( { } ) ,
390+ } ) as any as SessionWithActivitiesResource ;
391+
392+ // User A renders the Security page, caching their sessions in the
393+ // module-scoped `useFetch` cache.
394+ const { wrapper : wrapperA , fixtures : fixturesA } = await createFixtures ( f => {
395+ f . withUser ( { id : 'user_a' , email_addresses : [ 'a@clerk.com' ] } ) ;
396+ } ) ;
397+ fixturesA . clerk . user ! . getSessions . mockReturnValue (
398+ Promise . resolve ( [ makeSession ( fixturesA . clerk . session ! . id , 'Athens' ) ] ) ,
399+ ) ;
400+
401+ const { unmount } = render ( < SecurityPage /> , { wrapper : wrapperA } ) ;
402+ await waitFor ( ( ) => expect ( fixturesA . clerk . user ?. getSessions ) . toHaveBeenCalled ( ) ) ;
403+ await screen . findByText ( / A t h e n s / i) ;
404+ unmount ( ) ;
405+
406+ // User B mounts the Security page within the stale window. Because the cache
407+ // is keyed by `user.id`, B must trigger a fresh fetch and never sees A's
408+ // cached device activity.
409+ const { wrapper : wrapperB , fixtures : fixturesB } = await createFixtures ( f => {
410+ f . withUser ( { id : 'user_b' , email_addresses : [ 'b@clerk.com' ] } ) ;
411+ } ) ;
412+ fixturesB . clerk . user ! . getSessions . mockReturnValue (
413+ Promise . resolve ( [ makeSession ( fixturesB . clerk . session ! . id , 'Berlin' ) ] ) ,
414+ ) ;
415+
416+ render ( < SecurityPage /> , { wrapper : wrapperB } ) ;
417+ await waitFor ( ( ) => expect ( fixturesB . clerk . user ?. getSessions ) . toHaveBeenCalled ( ) ) ;
418+ await screen . findByText ( / B e r l i n / i) ;
419+ expect ( screen . queryByText ( / A t h e n s / i) ) . not . toBeInTheDocument ( ) ;
420+ } ) ;
369421} ) ;
0 commit comments