@@ -107,6 +107,8 @@ export class AtlasService {
107107
108108 private static fetch : typeof fetch = fetch ;
109109
110+ private static refreshing = false ;
111+
110112 private static get clientId ( ) {
111113 if ( ! process . env . COMPASS_CLIENT_ID ) {
112114 throw new Error ( 'COMPASS_CLIENT_ID is required' ) ;
@@ -164,44 +166,70 @@ export class AtlasService {
164166 // to the refresh-succeeded event and then kick off the "refresh"
165167 // programmatically to be able to get the actual tokens back and sync them
166168 // to the service state
167- let refreshing = false ;
168169 this . oidcPluginLogger . on ( 'mongodb-oidc-plugin:refresh-succeeded' , ( ) => {
169- // In case our call to REFRESH_TOKEN_CALLBACK somehow started another
170- // token refresh instead of just returning token from the plugin state, we
171- // short circuit if plugin logged a refresh-succeeded event and this
172- // listener got triggered
173- if ( refreshing ) {
174- return ;
175- }
176- refreshing = true ;
177- void this . plugin . mongoClientOptions . authMechanismProperties
178- // WARN: in the oidc plugin refresh callback is actually the same method
179- // as sign in, so calling it here means that potentially this can start
180- // an actual sign in flow for the user instead of just trying to refresh
181- // the token
182- . REFRESH_TOKEN_CALLBACK (
183- { clientId : this . clientId , issuer : this . issuer } ,
184- {
185- // Required driver specific stuff
186- version : 0 ,
187- }
188- )
189- . then ( ( token ) => {
190- this . token = token ;
191- this . oidcPluginSyncedFromLoggerState = 'authenticated' ;
192- this . oidcPluginLogger . emit ( 'atlas-service-token-refreshed' ) ;
193- } )
194- . catch ( ( ) => {
195- this . token = null ;
196- this . oidcPluginSyncedFromLoggerState = 'error' ;
197- this . oidcPluginLogger . emit ( 'atlas-service-token-refresh-failed' ) ;
198- } )
199- . finally ( ( ) => {
200- refreshing = false ;
201- } ) ;
170+ void this . refreshToken ( ) ;
202171 } ) ;
203172 }
204173
174+ private static async refreshToken ( ) {
175+ // In case our call to REFRESH_TOKEN_CALLBACK somehow started another
176+ // token refresh instead of just returning token from the plugin state, we
177+ // short circuit if plugin logged a refresh-succeeded event and this
178+ // listener got triggered
179+ if ( this . refreshing ) {
180+ return ;
181+ }
182+ this . refreshing = true ;
183+ try {
184+ await Promise . race ( [
185+ // When oidc-plugin logged that token was refreshed, the token is not
186+ // actually refreshed yet in the plugin state and so calling `REFRESH_TOKEN_CALLBACK`
187+ // causes weird behavior that actually opens the browser again, to work
188+ // around that we wait for the state update event in addition. We can't
189+ // guarantee that this event will be emitted for our particular state as
190+ // this is not something oidc-plugin exposes, but we can ignore this for
191+ // now as only one auth state is created in this instance of oidc-plugin
192+ once ( this . oidcPluginLogger , 'mongodb-oidc-plugin:state-updated' ) ,
193+ // At the same time refresh can still fail at this stage, so to avoid
194+ // refresh being stuck, we also wait for refresh-failed event and throw
195+ // if it happens to avoid calling `REFRESH_TOKEN_CALLBACK`
196+ once ( this . oidcPluginLogger , 'mongodb-oidc-plugin:refresh-failed' ) . then (
197+ ( ) => {
198+ throw new Error ( 'Refresh failed' ) ;
199+ }
200+ ) ,
201+ ] ) ;
202+ try {
203+ const token =
204+ await this . plugin . mongoClientOptions . authMechanismProperties
205+ // WARN: in the oidc plugin refresh callback is actually the same
206+ // method as sign in, so calling it here means that potentially
207+ // this can start an actual sign in flow for the user instead of
208+ // just trying to refresh the token
209+ . REFRESH_TOKEN_CALLBACK (
210+ { clientId : this . clientId , issuer : this . issuer } ,
211+ {
212+ // Required driver specific stuff
213+ version : 0 ,
214+ }
215+ ) ;
216+ this . token = token ;
217+ this . oidcPluginSyncedFromLoggerState = 'authenticated' ;
218+ this . oidcPluginLogger . emit ( 'atlas-service-token-refreshed' ) ;
219+ } catch {
220+ // REFRESH_TOKEN_CALLBACK call failed for some reason
221+ this . token = null ;
222+ this . oidcPluginSyncedFromLoggerState = 'error' ;
223+ this . oidcPluginLogger . emit ( 'atlas-service-token-refresh-failed' ) ;
224+ }
225+ } catch {
226+ // encountered 'mongodb-oidc-plugin:refresh-failed' event, do nothing, we
227+ // already have a listener for this event
228+ } finally {
229+ this . refreshing = false ;
230+ }
231+ }
232+
205233 private static async maybeWaitForToken ( {
206234 signal,
207235 } : { signal ?: AbortSignal } = { } ) {
0 commit comments