@@ -41,6 +41,9 @@ public class OAuth2ClientCredentials extends OAuth implements ClientCredentials<
4141
4242 private OAuth2AccessToken oAuth2AccessToken ;
4343 private final AccessTokenRetrieverService accessTokenRetrieverService ;
44+
45+ // Flag to prevent recursive refresh attempts during token retrieval
46+ private volatile boolean isRefreshing = false ;
4447
4548 public OAuth2ClientCredentials (AccessTokenRetrieverService accessTokenRetrieverService ) {
4649 Assert .notNull (accessTokenRetrieverService , "accessTokenRetrieverService must not be null" );
@@ -49,27 +52,46 @@ public OAuth2ClientCredentials(AccessTokenRetrieverService accessTokenRetrieverS
4952
5053 @ Override
5154 public synchronized void applyToParams (List <Pair > queryParams , Map <String , String > headerParams , Map <String , String > cookieParams ) {
52- if (oAuth2AccessToken != null &&
53- // refresh 5 minutes before token expiration
54- oAuth2AccessToken .getExpiresAt ().minus (5 , ChronoUnit .MINUTES ).isBefore (Instant .now ())) {
55- oAuth2AccessToken = null ;
56- setAccessToken (null );
57- refreshOAuth2AccessToken ();
55+ // Skip refresh check if we're already in the middle of refreshing (prevents recursive calls)
56+ if (!isRefreshing ) {
57+ // Determine if token refresh is needed:
58+ // 1. Token exists but is about to expire (within 5 minutes) - original behavior
59+ // 2. Token is null but was previously set (stuck state after failed refresh) - fix for GFS customer issue
60+ boolean tokenExpiring = oAuth2AccessToken != null &&
61+ oAuth2AccessToken .getExpiresAt ().minus (5 , ChronoUnit .MINUTES ).isBefore (Instant .now ());
62+ boolean tokenMissingButWasSet = oAuth2AccessToken == null && getAccessToken () == null ;
63+
64+ if (tokenExpiring || tokenMissingButWasSet ) {
65+ // Clear existing token state before refresh attempt
66+ oAuth2AccessToken = null ;
67+ setAccessToken (null );
68+
69+ try {
70+ refreshOAuth2AccessToken ();
71+ } catch (Exception e ) {
72+ log .error ("Failed to refresh OAuth2 access token. Will retry on next API call." , e );
73+ // Don't rethrow - let the API call proceed and fail with 401/403
74+ // The next API call will trigger another refresh attempt
75+ }
76+ }
5877 }
5978 super .applyToParams (queryParams , headerParams , cookieParams );
6079 }
6180
6281 public void refreshOAuth2AccessToken () {
6382 log .debug ("Attempting to refresh OAuth2 access token..." );
6483
84+ isRefreshing = true ;
6585 try {
6686 oAuth2AccessToken = accessTokenRetrieverService .getOAuth2AccessToken ();
87+
88+ if (oAuth2AccessToken == null ) {
89+ throw new OAuth2TokenRetrieverException ("Failed to get OAuth2 access token" );
90+ }
6791 } catch (IOException | InvalidKeyException e ) {
6892 throw new OAuth2TokenRetrieverException ("Failed to get OAuth2 access token" , e );
69- }
70-
71- if (oAuth2AccessToken == null ) {
72- throw new OAuth2TokenRetrieverException ("Failed to get OAuth2 access token" );
93+ } finally {
94+ isRefreshing = false ;
7395 }
7496 }
7597
0 commit comments