Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/USER_GUIDE.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ If you want be able to perform git push operation from CLI than you have to setu

image::images/screenshot-17.png[]

=== App Passwords (Bitbucket Cloud only)
=== App Passwords - DEPRECATED (Bitbucket Cloud only)

Bitbucket https://community.atlassian.com/t5/Bitbucket-articles/Announcement-Bitbucket-Cloud-account-password-usage-for-Git-over/ba-p/1948231[deprecated usage of Atlassian account password] for Bitbucket API and Git over HTTPS starting from March 1st, 2022.

Expand All @@ -203,6 +203,21 @@ First, create a new _app password_ in Bitbucket as instructed in the https://sup
Then create a new _Username with password credentials_ in Jenkins, enter the Bitbucket username (not the email) in the _Username_ field and the created app password in the _Password_ field.

IMPORTANT: App passwords do not support email address as a username for authentication. Using the email address will raise an authentication error in scanning/checkout process.
The app passoword authentication has been deprecated by Atlassian and starting from September 9th, 2025 will not longer available for creation. https://www.atlassian.com/blog/bitbucket/bitbucket-cloud-transitions-to-api-tokens-enhancing-security-with-app-password-deprecation[Read here] for more informations.

=== User API Token (Bitbucket Cloud only)

Bitbucket https://www.atlassian.com/blog/bitbucket/bitbucket-cloud-transitions-to-api-tokens-enhancing-security-with-app-password-deprecation[deprecated usage of Atlassian App Password] for Bitbucket API and Git over HTTPS starting from September 9th, 2025.

The plugin can make use of an user API token with scopes instead of the app password.

First, create a new _User API token_ in Bitbucket as instructed in the https://support.atlassian.com/bitbucket-cloud/docs/create-an-api-token/[Bitbucket API Token Documentation]. At least allow _read_ access for project, repositories and pull requests. Also, you may need to allow _read_ and _write_ access for webhooks depending on your pipeline's triggers.

image::images/screenshot-22.png[]

Then create a new _Username with password credentials_ in Jenkins, enter the Atlassian Account email (not the username) in the _Username_ field and the created API token in the _Password_ field.

IMPORTANT: API token requires the email address as a username to authenticate using the BitbucketAPIs. Using the username will raise an authentication error in scanning/checkout process. API token without scope does not work.

=== OAuth credentials (Bitbucket Cloud only)

Expand Down
Binary file added docs/images/screenshot-22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.impl.client.ICheckedCallable;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketAccessTokenAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketOAuthAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketUserAPITokenAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketUsernamePasswordAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
Expand Down Expand Up @@ -147,6 +148,7 @@ protected boolean isSupportedAuthenticator(@CheckForNull BitbucketAuthenticator
return authenticator == null
|| authenticator instanceof BitbucketAccessTokenAuthenticator
|| authenticator instanceof BitbucketOAuthAuthenticator
|| authenticator instanceof BitbucketUserAPITokenAuthenticator // API access token
|| authenticator instanceof BitbucketUsernamePasswordAuthenticator; // app password
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* The MIT License
*
* Copyright (c) 2025, Falco Nikolas
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


package com.cloudbees.jenkins.plugins.bitbucket.impl.credentials;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import hudson.model.Descriptor.FormException;
import hudson.util.Secret;
import java.nio.charset.StandardCharsets;
import org.apache.hc.client5.http.utils.Base64;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;

/**
* Authenticator that uses a username and password (probably the default)
*/
public class BitbucketUserAPITokenAuthenticator implements BitbucketAuthenticator {

private final String encodedAuth;
private final String credentialsId;
private final Secret password;

/**
* Constructor.
* @param credentials the username/password that will be used
*/
public BitbucketUserAPITokenAuthenticator(StandardUsernamePasswordCredentials credentials) {
credentialsId = credentials.getId();
password = credentials.getPassword();
String auth = credentials.getUsername() + ":" + Secret.toString(password);
encodedAuth = Base64.encodeBase64String(auth.getBytes(StandardCharsets.ISO_8859_1));
}

@Override
public void configureRequest(HttpRequest request) {
final String authHeader = "Basic " + encodedAuth;
request.setHeader(HttpHeaders.AUTHORIZATION, authHeader);
}

@Override
public StandardUsernameCredentials getCredentialsForSCM() {
try {
return new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, getId(), "User API token for " + getId(), "x-bitbucket-api-token-auth", Secret.toString(password));
} catch (FormException e) {
throw new RuntimeException(e);
}
}

@Override
public String getId() {
return credentialsId;

Check warning on line 77 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketUserAPITokenAuthenticator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 62-77 are not covered by tests
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* The MIT License
*
* Copyright (c) 2025, Falco Nikolas
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.cloudbees.jenkins.plugins.bitbucket.impl.credentials;

import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import jenkins.authentication.tokens.api.AuthenticationTokenSource;

/**
* Source for username/password authenticators.
*/
@Extension
public class BitbucketUserAPITokenAuthenticatorSource
extends AuthenticationTokenSource<BitbucketUserAPITokenAuthenticator, StandardUsernamePasswordCredentials> {

/**
* Constructor.
*/
public BitbucketUserAPITokenAuthenticatorSource() {
super(BitbucketUserAPITokenAuthenticator.class, StandardUsernamePasswordCredentials.class);
}

/**
* Converts username/password credentials to an authenticator.
*
* @param credentials the username/password combo
* @return an authenticator that will use them.
*/
@NonNull
@Override
public BitbucketUserAPITokenAuthenticator convert(@NonNull StandardUsernamePasswordCredentials credentials) {
return new BitbucketUserAPITokenAuthenticator(credentials);
}

/**
* {@inheritDoc}
*/
@Override
public CredentialsMatcher matcher() {
return CredentialsMatchers.allOf(super.matcher(), new BitbucketUserAPITokenCredentialMatcher());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* The MIT License
*
* Copyright (c) 2025, Falco Nikolas
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.cloudbees.jenkins.plugins.bitbucket.impl.credentials;

import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import hudson.util.Secret;

// Although the CredentialsMatcher documentation says that the best practice
// is to implement CredentialsMatcher.CQL too, this class does not implement
// CredentialsMatcher.CQL, for the following reasons:
//
// * CQL supports neither method calls like StringUtils.isNotBlank(username)
// nor any regular-expression matching that could be used instead.
// * There don't seem to be any public credential-provider plugins that
// would benefit from CQL.
public class BitbucketUserAPITokenCredentialMatcher implements CredentialsMatcher {
private static final long serialVersionUID = -9196480589659636909L;

private static final int API_ACCESS_TOKEN_LENGTH = 192;

/**
* {@inheritDoc}
*/
@Override
public boolean matches(Credentials item) {
if (!(item instanceof StandardUsernamePasswordCredentials)) { // safety check

Check warning on line 49 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketUserAPITokenCredentialMatcher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 49 is only partially covered, one branch is missing
return false;

Check warning on line 50 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketUserAPITokenCredentialMatcher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 50 is not covered by tests
}

StandardUsernamePasswordCredentials credential = ((StandardUsernamePasswordCredentials) item);
String username = credential.getUsername();
String password = Secret.toString(credential.getPassword());

boolean isEMail = username.contains(".") && username.contains("@");

Check warning on line 57 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketUserAPITokenCredentialMatcher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 57 is only partially covered, one branch is missing
boolean validSecretLength = password.length() == API_ACCESS_TOKEN_LENGTH;

return isEMail && validSecretLength;

Check warning on line 60 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/credentials/BitbucketUserAPITokenCredentialMatcher.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 60 is only partially covered, one branch is missing
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.api.endpoint.BitbucketEndpointProvider;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketOAuthAuthenticatorSource;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketUserAPITokenAuthenticatorSource;
import com.cloudbees.jenkins.plugins.bitbucket.impl.credentials.BitbucketUsernamePasswordAuthenticatorSource;
import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
Expand Down Expand Up @@ -229,7 +230,8 @@ private static <T> CredentialsMatcher matcher(AuthenticationTokenContext<T> cont
if (source.fits(context)) {
CredentialsMatcher matcher = source.matcher();
if (source instanceof BitbucketUsernamePasswordAuthenticatorSource
|| source instanceof BitbucketOAuthAuthenticatorSource) {
|| source instanceof BitbucketOAuthAuthenticatorSource
|| source instanceof BitbucketUserAPITokenAuthenticatorSource) {
matcher = new TimeBoxedCredentialsMatcher(matcher);
}
matchers.add(matcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.PullRequestBranchType;
import com.cloudbees.jenkins.plugins.bitbucket.client.branch.BitbucketCloudAuthor;
import com.cloudbees.jenkins.plugins.bitbucket.client.branch.BitbucketCloudCommit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import com.cloudbees.jenkins.plugins.bitbucket.trait.BranchDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait.TrustTeamForks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.BitbucketEnvVarExtension;
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.GitClientAuthenticatorExtension;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestDestination;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestEvent;
Expand All @@ -38,6 +39,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait.TrustEveryone;
import edu.umd.cs.findbugs.annotations.NonNull;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.endpoint.BitbucketEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketIntegrationClientFactory;
import com.cloudbees.jenkins.plugins.bitbucket.client.pullrequest.BitbucketCloudPullRequestCommit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketCredentialsUtils;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import com.cloudbees.jenkins.plugins.bitbucket.trait.BranchDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.OriginPullRequestDiscoveryTrait;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketCloudApiClient;
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import com.cloudbees.jenkins.plugins.bitbucket.trait.BranchDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.OriginPullRequestDiscoveryTrait;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.TaskListener;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCommit;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.test.util.BitbucketClientMockUtils;
import com.cloudbees.jenkins.plugins.bitbucket.trait.BranchDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ForkPullRequestDiscoveryTrait;
import com.cloudbees.jenkins.plugins.bitbucket.trait.OriginPullRequestDiscoveryTrait;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMockApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.hooks.WebhookAutoRegisterListener;
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketCloudEndpoint;
Expand Down
Loading
Loading