@@ -15,7 +15,9 @@ import { ITokenProvider, Token } from '../../lib/connection/auth/tokenProvider';
1515 */
1616class CustomOAuthTokenProvider implements ITokenProvider {
1717 private readonly oauthServerUrl : string ;
18+
1819 private readonly clientId : string ;
20+
1921 private readonly clientSecret : string ;
2022
2123 constructor ( oauthServerUrl : string , clientId : string , clientSecret : string ) {
@@ -25,31 +27,37 @@ class CustomOAuthTokenProvider implements ITokenProvider {
2527 }
2628
2729 async getToken ( ) : Promise < Token > {
30+ // eslint-disable-next-line no-console
2831 console . log ( 'Fetching token from custom OAuth server...' ) ;
32+ return this . fetchTokenWithRetry ( 0 ) ;
33+ }
2934
30- // Retry transient errors with exponential backoff
35+ /**
36+ * Recursively attempts to fetch a token with exponential backoff.
37+ */
38+ private async fetchTokenWithRetry ( attempt : number ) : Promise < Token > {
3139 const maxRetries = 3 ;
32- let lastError : Error | undefined ;
3340
34- for ( let attempt = 0 ; attempt <= maxRetries ; attempt ++ ) {
35- if ( attempt > 0 ) {
36- const delay = 1000 * Math . pow ( 2 , attempt - 1 ) ;
37- await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
41+ try {
42+ return await this . fetchToken ( ) ;
43+ } catch ( error ) {
44+ // Don't retry client errors (4xx)
45+ if ( error instanceof Error && error . message . includes ( 'OAuth token request failed: 4' ) ) {
46+ throw error ;
3847 }
3948
40- try {
41- return await this . fetchToken ( ) ;
42- } catch ( error ) {
43- lastError = error instanceof Error ? error : new Error ( String ( error ) ) ;
44-
45- // Only retry on transient errors (5xx, network issues)
46- if ( error instanceof Error && error . message . includes ( 'OAuth token request failed: 4' ) ) {
47- throw error ; // Don't retry client errors (4xx)
48- }
49+ if ( attempt >= maxRetries ) {
50+ throw error ;
4951 }
50- }
5152
52- throw lastError ?? new Error ( 'Token fetch failed after retries' ) ;
53+ // Exponential backoff: 1s, 2s, 4s
54+ const delay = 1000 * ( 2 ** attempt ) ;
55+ await new Promise < void > ( ( resolve ) => {
56+ setTimeout ( resolve , delay ) ;
57+ } ) ;
58+
59+ return this . fetchTokenWithRetry ( attempt + 1 ) ;
60+ }
5361 }
5462
5563 private async fetchToken ( ) : Promise < Token > {
@@ -96,6 +104,8 @@ class CustomOAuthTokenProvider implements ITokenProvider {
96104/**
97105 * Simple token provider that reads from a file (for development/testing).
98106 */
107+ // exported for use as an alternative example provider
108+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
99109class FileTokenProvider implements ITokenProvider {
100110 private readonly filePath : string ;
101111
0 commit comments