ExportXMLWordPrintable

    • 937.3.3

      Summary

      When building Pull Requests from forks on Bitbucket Cloud, the upstream (target repo) fetch fails with Authentication failed. The root cause is that GitClientAuthenticatorExtension only applies the transformed credential (with x-bitbucket-api-token-auth username) to the primary remote URL, but not to additional remotes (upstream). The upstream remote falls back to the raw Jenkins credential which uses the original username (e.g., email), which Bitbucket Cloud rejects for API token authentication.

      Affected Component

      GitClientAuthenticatorExtension.decorate() in bitbucket-branch-source-plugin

      Environment

      • Bitbucket Cloud (bitbucket.org)
      • Bitbucket Branch Source plugin version 937.3.2
      • Git plugin with git-client 6.x
      • Jenkins credential type: Username with Password (email + API token)
      • Organization Folder or Multibranch Pipeline

      Steps to Reproduce

      1. Set up a Bitbucket Cloud organization folder in Jenkins
      2. Configure scan credentials as "Username with Password" where username is the user's email and password is a Bitbucket API token
      3. Create a fork of a repository within the same workspace
      4. Create a branch in the fork and open a Pull Request targeting the main branch of the original repository
      5. Observe the PR build triggered by the organization folder

      Expected Behavior

      Both the fork (origin) and the target repo (upstream) fetches authenticate successfully using the configured credential.

      Actual Behavior

      • The fork fetch succeeds — the credential is transformed via BitbucketUserAPITokenAuthenticator.getCredentialsForSCM() to use x-bitbucket-api-token-auth as username
      • The upstream fetch fails with:

      stderr: remote: You may not have access to this repository or it no longer exists in this workspace.
      fatal: Authentication failed for 'https://bitbucket.org/<workspace>/<repo>.git/'

      Log Evidence

      Fetching upstream changes from https://bitbucket.org/<workspace>/<fork-repo>.git
      using GIT_ASKPASS to set credentials User API token for <cred-id> ← transformed credential
      > git fetch ... https://bitbucket.org/<workspace>/<fork-repo>.git ... ← SUCCESS

      Fetching upstream changes from https://bitbucket.org/<workspace>/<target-repo>.git
      using GIT_ASKPASS to set credentials <cred-id> ← raw credential (no transformation)
      > git fetch ... https://bitbucket.org/<workspace>/<target-repo>.git ... ← FAILS

      Note the different descriptions logged: "User API token for X" (transformed) vs just "X" (raw).

      Root Cause Analysis

      Flow for primary remote (works):

      1. BitbucketSCMSource.build() creates GitClientAuthenticatorExtension with the primary remote URL
      2. During checkout, GitSCM.createClient() adds raw credentials for all remotes
      3. GitClientAuthenticatorExtension.decorate() then overwrites the primary URL's credential with the transformed one (username = x-bitbucket-api-token-auth)
      4. Fetch succeeds

      Flow for upstream remote (fails):

      1. GitSCM.createClient() adds raw credentials (username = email) for the upstream URL
      2. GitClientAuthenticatorExtension.decorate() does NOT touch the upstream URL — it only knows about the primary URL
      3. Fetch uses email as username → Bitbucket Cloud rejects it

      Relevant code:

      GitClientAuthenticatorExtension.decorate() — only applies to single URL:

      public GitClient decorate(GitSCM scm, GitClient git) throws GitException {
          if (credentialsId != null) {
              BitbucketAuthenticator authenticator = authenticator();
              credentials = authenticator.getCredentialsForSCM();
          }
          if (credentials != null) {
              git.addCredentials(url, credentials);  // only the primary remote URL
          }
          return git;
      }

      BitbucketSCMSource.build() — only passes primary URL:

      return scmBuilder
          .withExtension(new GitClientAuthenticatorExtension(checkoutURL, serverUrl, scmOwner, checkoutCredentialsId))
          .build();

      BitbucketGitSCMBuilder.withPullRequestRemote() — adds upstream as additional remote:

      withAdditionalRemote(upstreamRemoteName, getCloneLink(primaryCloneLinks), refSpec);

      This upstream URL never gets the transformed credential.

      Proposed Fix

      Option A: Modify GitClientAuthenticatorExtension.decorate() to apply credentials to all remotes

      @Override
      public GitClient decorate(GitSCM scm, GitClient git) throws GitException {
          StandardUsernameCredentials credentials = this.credentials;
          if (credentialsId != null) {
              BitbucketAuthenticator authenticator = authenticator();
              if (authenticator == null) {
                  throw new IllegalStateException(...);
              }
              credentials = authenticator.getCredentialsForSCM();
          }
          if (credentials != null) {
              // Apply to all configured remotes, not just the primary
              for (UserRemoteConfig uc : scm.getUserRemoteConfigs()) {
                  git.addCredentials(uc.getUrl(), credentials);
              }
          }
          return git;
      }

      Option B: Pass all remote URLs to GitClientAuthenticatorExtension

      Change BitbucketSCMSource.build() to collect all remote URLs (primary + additional) and pass them to the extension, then have decorate() iterate over all of them.

      Option C: Minimal change in BitbucketGitSCMBuilder

      In withPullRequestRemote(), after calling withAdditionalRemote(...), also register a second GitClientAuthenticatorExtension for the upstream URL. This may require allowing multiple instances of the same extension type.

      Recommended: Option A

      Option A is the simplest fix with the least API changes. The transformed credential (using x-bitbucket-api-token-auth) is valid for all Bitbucket Cloud repos accessible by the API token, so applying it to all remotes is safe and correct.

      Workaround

      None available. Changing the credential username to x-bitbucket-api-token-auth fixes the upstream fetch but breaks the Bitbucket API calls (repo scanning, PR discovery) that require the email as username. Users cannot work around this without a plugin fix.

      The only alternative is to avoid fork-based PRs entirely and use branches within the same repository.

            Assignee:
            Nikolas Falco
            Reporter:
            Rafael Fuchs
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: