Child pages
  • Authenticating scripted clients
Skip to end of metadata
Go to start of metadata

To make scripted clients (such as wget) invoke operations that require authorization (such as scheduling a build), use HTTP BASIC authentication to specify the user name and the API token. This is often more convenient than emulating the form-based authentication.

API token is new since 1.426

Earlier versions of Jenkins require you to specify your real password, and it is only available when your security realm is password-based (for example, OpenID, Crowd and CAS plugins authenticate you without a password, so you simply don't have any password!) Specifying the real password is still supported after 1.426, but it is not recommended because the risk of revealing password, and the human tendency to reuse the same password in different places.

The API token is available in your personal configuration page. Click your name on the top right corner on every page, then click "Configure" to see your API token. (The URL $root/me/configure is a good shortcut.) You can also change your API token from here.

Note that Jenkins does not do any authorization negotiation. Ie. it immediately returns a 403 (Forbidden) response instead of a 401 (Unauthorized) response, so make sure to send the authentication information from the first request (aka "preemptive authentication".)

In a groovy script, this could look something like this (using commons-httpclient):

import org.apache.commons.httpclient.*

import org.apache.commons.httpclient.auth.*
import org.apache.commons.httpclient.methods.*

@Grab(group='commons-httpclient', module='commons-httpclient', version='3.1')
void createNewJenkinsProject() {

  def server = "server"
  def jenkinsHost = "https://${server}/jenkins/"
  def projectName = "TEST"
  def configurationFile = "config.xml"

  def username = "username"
  def apiToken = "apiToken"

  def client = new HttpClient()
  client.state.setCredentials(
    new AuthScope( server, 443, "realm"),
    new UsernamePasswordCredentials( username, apiToken )
  )

  // Jenkins does not do any authentication negotiation,
  // ie. it does not return a 401 (Unauthorized)
  // but immediately a 403 (Forbidden)
  client.params.authenticationPreemptive = true

  def post = new PostMethod( "${jenkinsHost}/createItem?name=${projectName}" )
  post.doAuthentication = true

  File input = new File(configurationFile);
  RequestEntity entity = new FileRequestEntity(input, "text/xml; charset=UTF-8");
  post.setRequestEntity(entity);
  try {
    int result = client.executeMethod(post)
    println "Return code: ${result}"
    post.responseHeaders.each{ println it.toString().trim() }
    println post.getResponseBodyAsString()
  } finally {
    post.releaseConnection()
  }
}
createNewJenkinsProject()

wget note

Note: If you are using wget 1.11 against Jenkins version 1.586 and above with the JENKINS-25169 fix, you might need to use the following options:

wget --auth-no-challenge --http-user=user --http-password=apiToken --secure-protocol=TLSv1 http://jenkins.yourcompany.com/job/your_job/build?token=TOKEN

Note: If you are using wget 1.11, you might need to use the following options:

 wget --auth-no-challenge --http-user=user --http-password=apiToken http://jenkins.yourcompany.com/job/your_job/build?token=TOKEN

With wget 1.10.x the following is enough (but will not work with 1.11.x) :

 wget http://user:apiToken@jenkins.yourcompany.com/job/your_job/build?token=TOKEN

See this RedHat bug report for more detailled explanations: https://bugzilla.redhat.com/show_bug.cgi?id=446949 (this also affect other distributions)
(Report indicates that wget 1.0 and 1.1 don't appear to have a --auth-no-challenge option.)

Perl LWP example for a scripted client

The following Perl example uses the LWP module to start a Job via a "Trigger builds remotely" token:

#
# Use LWP to run a Jenkins job
# set authorization_basic on the request object
# to make use of BASIC HTTP authorization, apparently
# already handling the preemptive part correctly this
# way.
#
use strict;
use warnings;

use LWP;

my $server = 'srvname';
my $srvurl = "http://$server/jenkins";
my $uagent = LWP::UserAgent->new;
my $req = HTTP::Request->new(
  GET => "$srvurl/job/test/build?token=theTokenConfiguredForThisJob&cause=LWP+Test"
);
$req->authorization_basic('username@mydomain.com', 'apiToken');
my $res = $uagent->request($req);

# Check the outcome of the response
print "Result: " . $res->status_line . "\n";
print $res->headers->as_string;
print "\n";
if (!$res->is_success) {
  print "Failed\n";
}
else {
  print "Success!\n";
  # print $res->content, "\n";
}
Java example with httpclient 4.1.2

This will authenticate you on your jenkins and launch the defined build. Be careful on security issues since this sample is based on username/password authentication.

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * Simple class to launch a jenkins build on run@Cloud platform, should also work on every jenkins instance (not tested)
 *
 */
public class TestPreemptive {

	public static void main(String[] args) {

		// Credentials
		String username = "YOUR_USERNAME";
		String password = "YOUR_PASSWORD";

		// Jenkins url
		String jenkinsUrl = "JENKINS_URL";

		// Build name
		String jobName = "JOB";

		// Build token
		String buildToken = "BUILD_TOKEN";

		// Create your httpclient
		DefaultHttpClient client = new DefaultHttpClient();

		// Then provide the right credentials
		client.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
				new UsernamePasswordCredentials(username, password));

		// Generate BASIC scheme object and stick it to the execution context
		BasicScheme basicAuth = new BasicScheme();
		BasicHttpContext context = new BasicHttpContext();
		context.setAttribute("preemptive-auth", basicAuth);

		// Add as the first (because of the zero) request interceptor
		// It will first intercept the request and preemptively initialize the authentication scheme if there is not
		client.addRequestInterceptor(new PreemptiveAuth(), 0);

		// You get request that will start the build
		String getUrl = jenkinsUrl + "/job/" + jobName + "/build?token=" + buildToken;
		HttpGet get = new HttpGet(getUrl);

		try {
			// Execute your request with the given context
			HttpResponse response = client.execute(get, context);
			HttpEntity entity = response.getEntity();
			EntityUtils.consume(entity);
		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Preemptive authentication interceptor
	 *
	 */
	static class PreemptiveAuth implements HttpRequestInterceptor {

		/*
		 * (non-Javadoc)
		 *
		 * @see org.apache.http.HttpRequestInterceptor#process(org.apache.http.HttpRequest,
		 * org.apache.http.protocol.HttpContext)
		 */
		public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
			// Get the AuthState
			AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

			// If no auth scheme available yet, try to initialize it preemptively
			if (authState.getAuthScheme() == null) {
				AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
				CredentialsProvider credsProvider = (CredentialsProvider) context
						.getAttribute(ClientContext.CREDS_PROVIDER);
				HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
				if (authScheme != null) {
					Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost
							.getPort()));
					if (creds == null) {
						throw new HttpException("No credentials for preemptive authentication");
					}
					authState.setAuthScheme(authScheme);
					authState.setCredentials(creds);
				}
			}

		}

	}
}
Java example with httpclient 4.3.x

This will cause httpclient 4.3 to issue authentication preemptively:

import java.io.IOException;
import java.net.URI;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class JenkinsScraper {

	public String scrape(String urlString, String username, String password) throws ClientProtocolException, IOException {
		URI uri = URI.create(urlString);
		HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
		CredentialsProvider credsProvider = new BasicCredentialsProvider();
		credsProvider.setCredentials(new AuthScope(uri.getHost(), uri.getPort()), new UsernamePasswordCredentials(username, password));
		// Create AuthCache instance
		AuthCache authCache = new BasicAuthCache();
		// Generate BASIC scheme object and add it to the local auth cache
		BasicScheme basicAuth = new BasicScheme();
		authCache.put(host, basicAuth);
		CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
		HttpGet httpGet = new HttpGet(uri);
		// Add AuthCache to the execution context
		HttpClientContext localContext = HttpClientContext.create();
		localContext.setAuthCache(authCache);

		HttpResponse response = httpClient.execute(host, httpGet, localContext);

		return EntityUtils.toString(response.getEntity());
	}

}



  • No labels

11 Comments

  1. Will this still work with the active directory plugin enabled?  I tried authenticating from powershell using http basic authentication (passing in the api token for the password) with no luck.

    1. I have the same question.  When using active directory plugin, I have to provide my active directory password, which is definitely not what I want to do.

      1. Hi, I'm not sure if it was the ps code I was using at the time or something wrong with that version of jenkins but I am now able to get this to work in version 1.501

        1. How ? Could you share a snippet of your code or cmd line ?

  2. Hi there!

    I've successfully managed to run build with Java code example above.

    But i tried similar to directly download an artifact file from the Jenkins and didn't succeed :(

    I want to download a file with a similar Java code from above.

    This is the actual file i want to download.

    http://myJenkins:8080/job/staff-service/lastSuccessfulBuild/staff.rest.tomcat/artifact/1.3.0-SNAPSHOT/staff.rest.tomcat-1.3.0-SNAPSHOT.war

    I've also tried with wget but with No success :( Always getting 403 Forbiden or 404 Not found.

    If i put this link in web browser and enable Anonymous Read access in Jenkins i can download the file without problem!

    But I don't want to grant Anonymous access within Jenkins 'cause of security issues.

    Anyone willing to help? :)

    Thanks in advance!

  3. I actually find the solution:

    Here it is:

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import org.apache.http.HttpException;
    import org.apache.http.HttpHost;
    import org.apache.http.HttpRequest;
    import org.apache.http.HttpRequestInterceptor;
    import org.apache.http.HttpResponse;
    import org.apache.http.auth.AuthScheme;
    import org.apache.http.auth.AuthScope;
    import org.apache.http.auth.AuthState;
    import org.apache.http.auth.Credentials;
    import org.apache.http.auth.UsernamePasswordCredentials;
    import org.apache.http.client.CredentialsProvider;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.protocol.ClientContext;
    import org.apache.http.impl.auth.BasicScheme;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.protocol.BasicHttpContext;
    import org.apache.http.protocol.ExecutionContext;
    import org.apache.http.protocol.HttpContext;
    
    /**
     * Simple class to launch a jenkins build on run@Cloud platform, should also
     * work on every jenkins instance (not tested)
     * 
     */
    public class FileDownloader {
    
        @SuppressWarnings("deprecation")
        public static void main(String[] args) throws IOException {
    
            // Credentials
            String username = args[0];
            String password = args[1];
    
            // Jenkins url
            String parameter1 = args[2];
            String parameter2 = args[3];
            // Create your httpclient
            DefaultHttpClient client = new DefaultHttpClient();
    
            // Then provide the right credentials
            client.getCredentialsProvider().setCredentials(
            new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
            new UsernamePasswordCredentials(username, password));
    
            // Generate BASIC scheme object and stick it to the execution context
            BasicScheme basicAuth = new BasicScheme();
            BasicHttpContext context = new BasicHttpContext();
            context.setAttribute("preemptive-auth", basicAuth);
    
            // Add as the first (because of the zero) request interceptor
            // It will first intercept the request and preemptively initialize the
            // authentication scheme if there is not
            client.addRequestInterceptor(new PreemptiveAuth(), 0);
    
            HttpGet get = new HttpGet(parameter1);
    
            try {
                // Execute your request with the given context
                HttpResponse response = client.execute(get, context);
                InputStream input = response.getEntity().getContent();
                byte[] buffer = new byte[8 * 1024];
    
                try {
                    OutputStream output = new FileOutputStream(parameter2);
                    try {
                        int bytesRead;
                        while ((bytesRead = input.read(buffer)) != -1) {
                            output.write(buffer, 0, bytesRead);
                        }
                    } finally {
                        output.close();
                    }
                } finally {
                    input.close();
                }
    
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        /**
         * Preemptive authentication interceptor
         * 
         */
        static class PreemptiveAuth implements HttpRequestInterceptor {
    
            /*
             * (non-Javadoc)
             * 
             * @see org.apache.http.HttpRequestInterceptor#process(org.apache.http.
             * HttpRequest, org.apache.http.protocol.HttpContext)
             */
            @SuppressWarnings("deprecation")
     public void process(HttpRequest request, HttpContext context)
            throws HttpException, IOException {
                // Get the AuthState
                AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
    
                // If no auth scheme available yet, try to initialize it
                // preemptively
                if (authState.getAuthScheme() == null) {
                    AuthScheme authScheme = (AuthScheme) context.getAttribute("preemptive-auth");
                    CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
                    HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
                    if (authScheme != null) {
                        Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
                        if (creds == null) {
                            throw new HttpException(
                                "No credentials for preemptive authentication");
                        }
                        authState.setAuthScheme(authScheme);
                        authState.setCredentials(creds);
                    }
                }
    
            }
    
        }
    }
    
     USAGE:
    #javac FileDownloader.java
    
    #java FileDownloader username password "http://binary.file/you.want.to.download/test.zip" "c:\\absoluthe.path.you\\want.to.save.the.file\\test.zip"
    
    or without quotes so:
    
    #java FileDownloader username password http://binary.file/you.want.to.download/test.zip c:\\absoluthe.path.you\\want.to.save.the.file\\test.zip
    
  4. Is it me or is this relatively unsafe ? If a malicious user got hold of my API token, they'd have access to anything I have access to, right ? And this probably includes changing my password remotely ? Or does this token only allow read access anyway ?

    1. It does seem unsafe. Two-step auth seems more in order. Another option is to proxy your Jenkins set up, and add additional auth requirements (or limit the /api/ path to only specific IPs, etc).

  5. Since Jenkins ver. 1.586, the method to trigger builds remotely using TOKEN authentication URLs with wget described no longer work due to SSLv1 and SSLv2 is now disabled because of the POODLE vulnerability fix.

    The following error will appear:

    Resolving jenkins... 10.0.0.1
    Connecting to jenkins|10.0.0.1|:443... connected.
    Unable to establish SSL connection.
    The command for wget 1.11.x needs to include the --secure-protocol=TLSv1 option.
    wget --auth-no-challenge --http-user=user --http-password=apiToken --secure-protocol=TLSv1 http://jenkins.yourcompany.com/job/your_job/build?token=TOKEN
    
  6. I have found that GNU Wget 1.12 doesn't work with API authentication, but curl 7.19.7 does (we're using Jenkins 1.596) Probably could build it with different libraries, but who cares

    Curl does work, but it still complains that the user is anonymous, I'm guessing at the URL filtering stage.

    It's unclear to me in the URI, what should be the value of the "TOKEN"? The word "TOKEN"? The value of the apiToken or is it the username with which the apiToken is associated?

    Thank you

  7. Here is code to trigger builds via powershell..

     $acctname = "userId";
    $password = "userIdApiToken";
    $server = "serverName";
    $port = "portNumber";
    $jobName = "jobName";
    $jobToken = "jobToken";
    
    
    $params = @{uri = "http://${server}:${port}/job/${jobName}/build?token=${jobToken}";
                       Method = 'Get';
                       Headers = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$(${acctname}):$(${password})"));}
               }
    
    $var = invoke-restmethod @params