fix: Refactor Bitbucket Cloud's OAuth2 host provider to support rotating refresh tokens - #2429
Closed
danielrfraser wants to merge 1 commit into
Closed
Conversation
…ting refresh tokens by always storing refresh tokens and access tokens against the resolved Bitbucket username
danielrfraser
force-pushed
the
dfraser/refactor-bitbucket-cloud-oauth-behaviour
branch
from
September 2, 2026 02:58
deb1a19 to
c4972b7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updates the
BitbucketHostProviderto always store the refresh credentials using the resolved username from bitbucket.Background
Bitbucket Cloud's OAuth implementation is moving to rotating refresh tokens. These refresh tokens have a TTL of 3 months, but are immediately expired (~10 minutes) after their first usage. This means after each
refresh_tokengrant flow it's expected that clients store the newly issuedrefresh_tokenfrom the OAuth token set.Another change is that the
access_tokenandrefresh_tokenare far larger than the previous tokens. Regularly exceeding 2500 characters. This will cause issues withCredentialStorewhich have limits on the credential length. The default for windowswincredmanis one such store which has issues. We will be asking customers using GCM on windows (likely via git output and a deprecation notice) that they'll need to change credential store that git credential manager is configured to use todpapiwhich doesn't have the size constraints.Why does the Bitbucket OAuth code in GCM need changing?
When the initial
authorization_codegrant flow is run, theaccess_tokenandrefresh_tokenreturned are stored in the configuredCredentialStoreagainst the validated Bitbucketusername(resolved by callingapi.bitbucket.org/2.0/userwith theaccess_token).When the
access_tokenstored expires, therefresh_tokengrant flow automatically runs to generate fresh credentials. This flow looks for arefresh_tokenin the Credential Store stored under theaccountprovided by therequest.UserNameproperty.In some cases this property is null (if the git remote URI configured specifies no username). When the account is null, the Credential Store will return back the first Credential that matches the git host. This will find the
refresh_tokenstored by the original authorization flow. For git remotes configured with ausernamethe CredentialManager will look for a secret belonging exactly to that account.When, the
refresh_tokengrant flow has concluded and theaccess_tokenhas been validated the newrefresh_tokenis stored in CredentialStore against theaccountprovided by theremoteUri.GetUserName()function. However, becauseremoteUriis built withincludeUser=False(which is the default) therefresh_tokencredential will always be stored against anullaccount in the credential store.Because of the discrepency between the locations where we store the
refresh_tokenand retrieve it, Therefresh_tokenwill only works the first time therefresh_tokengrant flow is executed. Subsequentrefresh_tokengrant flows will retrieve the original (and now expired)refresh_token- which will be blocked. This causes the auth code will fall into a new authorization grant flow. The broken behaviour would then loop in this manner indefinitely requiring constant reauthorization.The change
This fix brings the
refresh_tokengrant flow inline with the implementation of theauthorization_codegrant flow. This stores therefresh_tokenagainst theaccountwhich is the resolved Bitbucketusername. This should mean that regardless of whether you've set yourusernamein the git remote URI, the latestrefresh_tokenwill be used to initiate therefresh_tokenflow.Rollout
Once the version containing this release is shipped / bundled into Git for Windows, we will be announcing a deprecation and publicly documenting the steps required to continue authing using the OAuth credentials. We will likely run a series of brownouts against the older versions of GCM prior to the complete disablement of the non-rotating refresh tokens.
We will also highlight in our deprecation / announcement (In official Atlassian channels) that any Windows users need to move from using the default
CredentialStoreofwincredmanontodpapi.I sparred with the problem and fix with my colleague @mminns who worked on some of the original code some 8+ years ago.
Fixes: #2428