@@ -102,6 +102,50 @@ describe("LoginManager", () => {
102102 expect ( globalThis . fetch ) . toHaveBeenCalledTimes ( 1 )
103103 } )
104104
105+ it ( "should not refresh again if token was already refreshed by another tab" , async ( ) => {
106+ const manager1 = new SyncLoginManager ( storage , "https://api.example.com" , undefined , "client1" , "openid" )
107+ const manager2 = new SyncLoginManager ( storage , "https://api.example.com" , undefined , "client1" , "openid" )
108+
109+ await manager1 . tryRefreshToken ( )
110+ expect ( globalThis . fetch ) . toHaveBeenCalledTimes ( 1 )
111+
112+ const result = await manager2 . tryRefreshToken ( )
113+ expect ( result ) . toBe ( true )
114+ expect ( globalThis . fetch ) . toHaveBeenCalledTimes ( 1 )
115+ } )
116+
117+ it ( "should re-read token from storage inside lock to avoid using stale refresh token" , async ( ) => {
118+ let fetchCallCount = 0
119+ globalThis . fetch = vi . fn ( ( ) => {
120+ fetchCallCount ++
121+ if ( fetchCallCount === 1 ) {
122+ return Promise . resolve ( {
123+ ok : true ,
124+ status : 200 ,
125+ json : ( ) =>
126+ Promise . resolve ( {
127+ access_token : "new_access_token" ,
128+ refresh_token : "new_refresh_token" ,
129+ expires_in : 3600 ,
130+ } ) ,
131+ } as Response )
132+ }
133+ return Promise . resolve ( {
134+ ok : false ,
135+ status : 400 ,
136+ } as Response )
137+ } )
138+
139+ const manager1 = new SyncLoginManager ( storage , "https://api.example.com" , undefined , "client1" , "openid" )
140+ const manager2 = new SyncLoginManager ( storage , "https://api.example.com" , undefined , "client1" , "openid" )
141+
142+ await manager1 . tryRefreshToken ( )
143+
144+ const result = await manager2 . tryRefreshToken ( )
145+ expect ( result ) . toBe ( true )
146+ expect ( globalThis . fetch ) . toHaveBeenCalledTimes ( 1 )
147+ } )
148+
105149 it ( "should handle failed refresh" , async ( ) => {
106150 globalThis . fetch = vi . fn ( ( ) =>
107151 Promise . resolve ( {
0 commit comments