77 UsernamePasswordAuth
88} from "../types" ;
99import { TokenKey , inMemoryCache , noneCache } from "../common/tokenCache" ;
10+ import ReadWriteLock from "rwlock" ;
1011
1112type Login = {
1213 access_token : string ;
@@ -22,11 +23,16 @@ export class Authenticator {
2223 options : ConnectionOptions ;
2324
2425 accessToken ?: string ;
26+ private rwlock = new ReadWriteLock ( ) ;
2527
2628 constructor ( context : Context , options : ConnectionOptions ) {
27- context . httpClient . authenticator = this ;
2829 this . context = context ;
2930 this . options = options ;
31+ if ( context . httpClient . authenticator ) {
32+ return context . httpClient . authenticator ;
33+ } else {
34+ context . httpClient . authenticator = this ;
35+ }
3036 }
3137
3238 private getCacheKey ( ) : TokenKey | undefined {
@@ -157,21 +163,66 @@ export class Authenticator {
157163 ) ;
158164 }
159165
160- async authenticate ( ) {
161- const options = this . options . auth || this . options ;
162- const cachedToken = this . getCachedToken ( ) ;
166+ async authenticate ( ) : Promise < void > {
167+ // Try to get token from cache using read lock
168+ const cachedToken = await this . tryGetCachedToken ( ) ;
163169 if ( cachedToken ) {
164170 this . accessToken = cachedToken ;
165171 return ;
166172 }
167173
174+ // No cached token, acquire write lock and authenticate
175+ await this . acquireWriteLockAndAuthenticate ( ) ;
176+ }
177+
178+ private async tryGetCachedToken ( ) : Promise < string | undefined > {
179+ return new Promise ( ( resolve , reject ) => {
180+ this . rwlock . readLock ( releaseReadLock => {
181+ try {
182+ const cachedToken = this . getCachedToken ( ) ;
183+ releaseReadLock ( ) ;
184+ resolve ( cachedToken ) ;
185+ } catch ( error ) {
186+ releaseReadLock ( ) ;
187+ reject ( error ) ;
188+ }
189+ } ) ;
190+ } ) ;
191+ }
192+
193+ private async acquireWriteLockAndAuthenticate ( ) : Promise < void > {
194+ return new Promise ( ( resolve , reject ) => {
195+ this . rwlock . writeLock ( async releaseWriteLock => {
196+ try {
197+ // Double-check cache in case another thread authenticated while waiting
198+ const cachedToken = this . getCachedToken ( ) ;
199+ if ( cachedToken ) {
200+ this . accessToken = cachedToken ;
201+ releaseWriteLock ( ) ;
202+ return resolve ( ) ;
203+ }
204+
205+ await this . performAuthentication ( ) ;
206+
207+ releaseWriteLock ( ) ;
208+ resolve ( ) ;
209+ } catch ( error ) {
210+ releaseWriteLock ( ) ;
211+ reject ( error ) ;
212+ }
213+ } ) ;
214+ } ) ;
215+ }
216+
217+ private async performAuthentication ( ) : Promise < void > {
218+ const options = this . options . auth || this . options ;
219+
168220 if ( this . isUsernamePassword ( ) ) {
169- await this . authenticateUsernamePassword ( options as UsernamePasswordAuth ) ;
170- return ;
221+ return this . authenticateUsernamePassword ( options as UsernamePasswordAuth ) ;
171222 }
223+
172224 if ( this . isServiceAccount ( ) ) {
173- await this . authenticateServiceAccount ( options as ServiceAccountAuth ) ;
174- return ;
225+ return this . authenticateServiceAccount ( options as ServiceAccountAuth ) ;
175226 }
176227
177228 throw new Error ( "Please provide valid auth credentials" ) ;
0 commit comments