Change:
var err error token := ts.cache.getToken(azureTokenKey) <ins> if token != nil && !token.token.IsExpired() { return token, nil } // retrieve from config if no cache </ins> if token == nil { <del> token, err = ts.retrieveTokenFromCfg() if err != nil { token, err = ts.source.Token() if err != nil { return nil, fmt.Errorf("acquiring a new fresh token: %v", err) } </del> <ins> tokenFromCfg, err := ts.retrieveTokenFromCfg() if err == nil { token = tokenFromCfg </ins> } <ins> } if token != nil { // cache and return if the token is as good // avoids frequent persistor calls </ins> if !token.token.IsExpired() { ts.cache.setToken(azureTokenKey, token) <del> err = ts.storeTokenInCfg(token) if err != nil { return nil, fmt.Errorf("storing the token in configuration: %v", err) } </del> <ins> return token, nil </ins> } <del> } if token.token.IsExpired() { token, err = ts.refreshToken(token) if err != nil { return nil, fmt.Errorf("refreshing the expired token: %v", err) </del> <ins> klog.V(4).Info("Refreshing token.") tokenFromRefresh, err := ts.Refresh(token) switch { case err == nil: token = tokenFromRefresh case autorest.IsTokenRefreshError(err): klog.V(4).Infof("Failed to refresh expired token, proceed to auth: %v", err) // reset token to nil so that the token source will be used to acquire new token = nil default: return nil, fmt.Errorf("unexpected error when refreshing token: %v", err) </ins> } <del> ts.cache.setToken(azureTokenKey, token) err = ts.storeTokenInCfg(token) </del> <ins> } if token == nil { tokenFromSource, err := ts.source.Token() </ins> if err != nil { <del> return nil, fmt.Errorf("storing the refreshed token in configuration: %v", err) </del> <ins> return nil, fmt.Errorf("failed acquiring new token: %v", err) </ins> } <ins> token = tokenFromSource } // sanity check if token == nil { return nil, fmt.Errorf("unable to acquire token") } // corner condition, newly got token is valid but expired if token.token.IsExpired() { return nil, fmt.Errorf("newly acquired token is expired") </ins> } <ins> err = ts.storeTokenInCfg(token) if err != nil { return nil, fmt.Errorf("storing the refreshed token in configuration: %v", err) } ts.cache.setToken(azureTokenKey, token) </ins> return token, nil }

Discussion:
<!-- Please only use this template for submitting enhancement requests --What would you like to be : Currently, when AAD login is configured, refresh token timeout would make kubectl print an error, not able to re-login without manually remove the tokens. Expect kubectl to promote user to login again. Why is this needed: Configure refresh-token timeout from Azure is a way to force re-login after inactivity. kubectl doesn't promote re-login makes users harder to apply timeout policy.
/sig azure
The label(s) cannot be appled. These labels are supported: <detailsIn response to : /sig azure Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
The label(s) cannot be appled. These labels are supported: <detailsIn response to : /sig platform/azure Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/area provider/azure
the root cause of this issue is in this first gets the cached token (result of device code flow) at it's trying to refresh an expired token at and failed. An error is returned. Since the expired token is still in the cache, it will always hit this path until user removes the token from kubeconfig. the fix should be: the token from cache a retry to re-initiate the device code flow in
The label(s) cannot be appled. These labels are supported: <detailsIn response to : /sig platform/azure Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig cli These SIGs are my best guesses for this issue. Please comment if I am incorrect about one. I am a bot run by vllry. ‍
I'm working on it.
are you working on it? I will grab it if I dont hear it from you :)
have the code ready will try PR this weekend Best, Ping
/assign
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
Closing as merged