Using Oauth and the Jira Java Rest Client

Martin Cassidy February 17, 2014

I'm trying to use the Jira Jave Rest Client to log time against a ticket automatically with information from another system. This other system has the same usernames as jira so I want to log time using the actual user.

I need a way to authenticate as any user of Jria, without them needing to authorise me (this will be a trused application) I've setup an application link which seems to be what I need to do this, but I cannot find any example of what I need to use for the Java Rest Client. So far I have:

final OAuthParameters params = new OAuthParameters();
params.setConsumerKey("MyApplication");
params.setSignatureMethod("RSA-SHA1");

final OAuthSecrets secrets = new OAuthSecrets();

final JerseyJiraRestClientFactory factory = new JerseyJiraRestClientFactory();
final URI jiraServerUri = new URI(uri);
return factory.create(jiraServerUri, new OAuthAuthenticationHandler(params, secrets));

I can't find any example of what else I need to do.

7 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

3 votes
Answer accepted
Martin Cassidy April 27, 2014

After much playing around, and reading the source code of most of that project I've been able to get my desired solution working. I'll go over everyting here, first with a few answers to people should be aware of

  1. Do you need to go through the "oauth dance" to get an access token with the 2 legged approach for Jira? No
  2. Can the the OAuth handler that comes with the Jira Java Rest client be extended to work with the 2 legged approach? No
  3. Why not? The Jira Java Reset client uses Jersy internally, which puts the OAuth signature in the HTTP header, not a request paramater. Jira only accepts request paramaters for the signature as far as I can see.

Here is my solution:

For anyone else that wants to implement, you need to generate your public/private keys as linked to above and create the trusted application through the Jira gui. Make sure you enable 2 legged oauth. Next, create the following class:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthServiceProvider;
import net.oauth.http.HttpMessage;
import net.oauth.signature.RSA_SHA1;

import com.atlassian.jira.rest.client.AuthenticationHandler;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientRequest.Builder;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.sun.jersey.api.client.filter.Filterable;
import com.sun.jersey.api.uri.UriComponent;
import com.sun.jersey.client.apache.config.ApacheHttpClientConfig;

public class TrusedApplicationTwoLeggedOAuthAuthenticationHandler implements AuthenticationHandler
{
	private final String consumerKey;
	private final String userKey;
	private final String privateKey;
	private final String baseUri;

	public TrusedApplicationTwoLeggedOAuthAuthenticationHandler(final String consumerKey, final String userKey, final String privateKey, final String baseUri)
	{
		super();
		this.consumerKey = consumerKey;
		this.userKey = userKey;
		this.privateKey = privateKey;
		this.baseUri = baseUri;
	}

	@Override
	public void configure(final ApacheHttpClientConfig apacheHttpClientConfig)
	{
	}

	@Override
	public void configure(final Filterable filterable, final Client client)
	{
		final TrustedApplicationClientFilter filter = new TrustedApplicationClientFilter();
		filterable.addFilter(filter);
	}

	private class TrustedApplicationClientFilter extends ClientFilter
	{

		public TrustedApplicationClientFilter()
		{
			super();
		}

		@Override
		public ClientResponse handle(final ClientRequest request) throws ClientHandlerException
		{
			try
			{
				final OAuthServiceProvider serviceProvider = new OAuthServiceProvider(baseUri + "/plugins/servlet/oauth/request-token", baseUri
						+ "/plugins/servlet/oauth/authorize", baseUri + "/plugins/servlet/oauth/access-token");

				final OAuthConsumer consumer = new OAuthConsumer("oob", consumerKey, null, serviceProvider);
				consumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
				consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);

				final OAuthAccessor accessor = new OAuthAccessor(consumer);
				accessor.accessToken = "";

				final Map<String, String> parameters = new HashMap<String, String>();

				for (final Entry<String, List<String>> entry : UriComponent.decodeQuery(request.getURI(), true).entrySet())
				{
					parameters.put(entry.getKey(), entry.getValue().get(0));
				}

				parameters.put("user_id", userKey);
				final OAuthMessage message = accessor.newRequestMessage(request.getMethod(), request.getURI().toString(), parameters.entrySet(),
						new ByteArrayInputStream(request.getEntity().toString().getBytes()));

				final Builder requestBuilder = ClientRequest.create();

				// TODO This does not support multi value params
				for (final Entry<String, List<Object>> entry : request.getHeaders().entrySet())
				{
					requestBuilder.header(entry.getKey(), entry.getValue());
				}

				requestBuilder.entity(request.getEntity());

				final HttpMessage httpRequest = HttpMessage.newRequest(message, net.oauth.ParameterStyle.BODY);

				return getNext().handle(requestBuilder.build(httpRequest.url.toURI(), request.getMethod()));
			}
			catch (final URISyntaxException e)
			{
				throw new ClientHandlerException(e);
			}
			catch (final OAuthException e)
			{
				throw new ClientHandlerException(e);
			}
			catch (final IOException e)
			{
				throw new ClientHandlerException(e);
			}
		}

	}

}

Finally, use it the following way:

String uri = "http://my.jira.uri";
		final JerseyJiraRestClientFactory factory = new JerseyJiraRestClientFactory();
		final URI jiraServerUri = new URI(uri);
		return factory.create(jiraServerUri, new TrusedApplicationTwoLeggedOAuthAuthenticationHandler("ConsumerName", "JiraUserName", "private key", uri));

I hope this works for anyone else wanting to do this, and do reply if you have any problems/questions

Piyush Khera January 5, 2015

Hi Martin, Could you please let me know for which version of JRJC does this implementation work? I am using JRJC v2.0.0-m31 and this implementation does not work for me since the interface AuthenticationHandler only has one method configure(com.atlassian.httpclient.api.Request). Could you please provide an implementation for OAuthAuthenticationHandler for this version?

Martin Cassidy April 30, 2015

This was written for 1.2.

Mohamed Bushra April 26, 2018

Hi Martin, 

Are you using the below dependencies:

<dependency>
  <groupId>com.atlassian.jira</groupId>
  <artifactId>jira-rest-java-client-core</artifactId>
  <version>5.0.4</version>
</dependency>
<dependency>
  <groupId>net.oauth.core</groupId>
  <artifactId>oauth</artifactId>
  <version>20100527</version>
</dependency>

I'm not able to compile your code, would you please help.

Thanks!

2 votes
asarraf April 8, 2015

OK, this is my approach, it is very hacky but it is compatible with JiraRestClientFactory.

First follow JIRA REST API Tutorial to get your access token. Read their source code to understand exactly what they do, as my code is based on their code. Their code uses net.oauth library. Since this library is not under central maven repository, you need to add its repository to your maven file:

 

&lt;repository&gt;
			&lt;id&gt;oauth&lt;/id&gt;
			&lt;url&gt;http://oauth.googlecode.com/svn/code/maven/&lt;/url&gt;
		&lt;/repository&gt;
 
...
		&lt;dependency&gt;
			&lt;groupId&gt;net.oauth.core&lt;/groupId&gt;
			&lt;artifactId&gt;oauth&lt;/artifactId&gt;
			&lt;version&gt;20100527&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;net.oauth.core&lt;/groupId&gt;
			&lt;artifactId&gt;oauth-consumer&lt;/artifactId&gt;
			&lt;version&gt;20100527&lt;/version&gt;
		&lt;/dependency&gt;

		&lt;dependency&gt;
			&lt;groupId&gt;net.oauth.core&lt;/groupId&gt;
			&lt;artifactId&gt;oauth-httpclient4&lt;/artifactId&gt;
			&lt;version&gt;20090913&lt;/version&gt;
		&lt;/dependency&gt;

Now, we need to combine what the tutorial did to make authenticated request with the new AuthenticationHandler

@Test
	public void test() throws Exception {
	        final JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
	        final URI jiraServerUri = new URI("https://your.jira.instance");
	        JiraRestClient restClient = null;
//	        restClient = factory.createWithBasicHttpAuthentication(jiraServerUri, "user", "pass"); // old way of doing it
	        restClient = factory.create(jiraServerUri, new AuthenticationHandler() {
				@Override
				public void configure(Request request) {
			        try {
						OAuthAccessor accessor = getAccessor();
                        accessor.accessToken = access_token;
						OAuthMessage request2 = accessor.newRequestMessage(null, request.getUri().toString(), Collections.&lt;Map.Entry&lt;?, ?&gt;&gt;emptySet());
						Object accepted = accessor.consumer.getProperty(OAuthConsumer.ACCEPT_ENCODING);
						if (accepted != null) {
							request2.getHeaders().add(new OAuth.Parameter(HttpMessage.ACCEPT_ENCODING, accepted.toString()));
						}
						Object ps = accessor.consumer.getProperty(OAuthClient.PARAMETER_STYLE);
				        ParameterStyle style = (ps == null) ? ParameterStyle.BODY
				                : Enum.valueOf(ParameterStyle.class, ps.toString());
						HttpMessage httpRequest = HttpMessage.newRequest(request2, style);
						for ( Entry&lt;String, String&gt; ap : httpRequest.headers)
							request.setHeader(ap.getKey(), ap.getValue());
						request.setUri( httpRequest.url.toURI() );
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
	        
	        final int buildNumber = restClient.getMetadataClient().getServerInfo().claim().getBuildNumber();
	        
	        assertTrue(buildNumber &gt; 0);
	}
 
private final OAuthAccessor getAccessor()
    {
        if (accessor == null)
        {
            OAuthServiceProvider serviceProvider = new OAuthServiceProvider(getRequestTokenUrl(), getAuthorizeUrl(), getAccessTokenUrl());
            OAuthConsumer consumer = new OAuthConsumer(callback, consumerKey, null, serviceProvider);
            consumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
            consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
            accessor = new OAuthAccessor(consumer);
        }
        return accessor;
    }
    private String getAccessTokenUrl()
    {
        return baseUrl + SERVLET_BASE_URL + "/oauth/access-token";
    }
    private String getRequestTokenUrl()
    {
        return  baseUrl + SERVLET_BASE_URL + "/oauth/request-token";
    }
    public String getAuthorizeUrlForToken(String token)
    {
        return getAuthorizeUrl() + "?oauth_token=" + token;
    }
    private String getAuthorizeUrl() {return baseUrl + SERVLET_BASE_URL + "/oauth/authorize";}
Blanka Bouskova September 6, 2016

Hi Abbas,

your code seems to work good, thank you. But where do you impersonate a user?
I'm trying to set parameter like xoauth_requestor_id, user_id, oauth_user_id, xoauth_user_id but without any success.

Are you able to send request to JIRA with 2-legged authentication and user impersonation?
If yes could you let me know how to do please?

Harinath T January 4, 2018

Hi Blanka Bouskova 

@Blanka Bouskova

Are you able to send request with user impersonation now..?

Do you have any solution for this..?

if yes could you please provide me your solutiion.

Blanka Bouskova January 4, 2018

Hi @Harinath T,

I'm sorry I'm not able to do it. I'm not on the project anymore.

Good luck!

1 vote
Mohamed Mahmoud March 18, 2014


JerseyJiraRestClientFactory factory = new JerseyJiraRestClientFactory();

		OAuthParameters oAuthParameters = new OAuthParameters();

		oAuthParameters.setSignatureMethod(OAuth.RSA_SHA1);

		oAuthParameters.setVersion("1.0");

		oAuthParameters.setConsumerKey(consumerKey);

		oAuthParameters.setToken(accessToken);

		OAuthSecrets oAuthSecrets = new OAuthSecrets();

		oAuthSecrets.setConsumerSecret(privateKey);

			

		AuthenticationHandler authenticationHandler = new OAuthAuthenticationHandler(oAuthParameters, oAuthSecrets );

		bc =factory.create(jiraUri, authenticationHandler );

Martin Cassidy March 24, 2014

Whe does the access token come from? I'm wanting 2 legged oauth for any user.

Martin Cassidy March 26, 2014

I've look at that example already, it only shows the 3 legged approach.

Mohamed Mahmoud March 26, 2014

I think if from the only difference is that from Jira's side you should allow 2 legged and that you just ask for request token then ask for access token without waiting for user to authorize

OAuthServiceProvider serviceProvider = new OAuthServiceProvider(requestTokenURL, userAuthorizationURL, accessTokenURL);
	
		OAuthConsumer consumer = new OAuthConsumer(null, "consumerkey", null, serviceProvider );
		consumer.setProperty(RSA_SHA1.PRIVATE_KEY, privateKey);
		consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);
		
		OAuthAccessor accessor = new OAuthAccessor(consumer);
		OAuthClient client = new OAuthClient(new HttpClient4());
OAuthMessage m = client.getRequestTokenResponse(accessor, "POST", null);

		String requestToken = m.getParameter(OAuth.OAUTH_TOKEN);
OAuthMessage msg = client.getAccessToken(accessor, "POST",null);
		
		 accessToken= msg.getParameter(OAuth.OAUTH_TOKEN);

Martin Cassidy March 26, 2014

I've tried to implement that but am getting a 500 on the request token request. The following is from the jira log

Caused by: java.security.SignatureException: Signature length not correct: got 256 but was expecting 128
at sun.security.rsa.RSASignature.engineVerify(Unknown Source)
at java.security.Signature$Delegate.engineVerify(Unknown Source)
at java.security.Signature.verify(Unknown Source)
at net.oauth.signature.RSA_SHA1.verify(RSA_SHA1.java:240)
at net.oauth.signature.RSA_SHA1.isValid(RSA_SHA1.java:209)

Mohamed Mahmoud March 26, 2014

From the error it seems there is a problem with the RSA keys

You should put the your public key in Jira and the private key on the client side.

try using the public and private keys from here just for testing:

https://bitbucket.org/atlassian_tutorial/atlassian-oauth-examples/src

Martin Cassidy April 15, 2014

OK, getting as far as the access token request which is returning permission_unknown

net.oauth.OAuthProblemException: permission_unknown
at net.oauth.client.OAuthResponseMessage.toOAuthProblemException(OAuthResponseMessage.java:83)
at net.oauth.client.OAuthClient.invoke(OAuthClient.java:306)
at net.oauth.client.OAuthClient.invoke(OAuthClient.java:260)
at net.oauth.client.OAuthClient.getAccessToken(OAuthClient.java:229)

Mohamed Mahmoud April 15, 2014

so did u get the access token back?

if not then maybe you didnt use the request token you got back

this is how you should use it

http://JIRA_URI/plugins/servlet/oauth/authorize?oauth_token=request_Token_you_got

go to url and authorize the token then after it request the access token

Martin Cassidy April 15, 2014

I got the request token back ok. I am doing 2 legged oauth though as mentioned above. The permission error is coming back when sending the access token request.

Mohamed Mahmoud April 15, 2014

Can you put your code?

Martin Cassidy April 15, 2014

Here it is, the uri is the Jira base uri and the username is the username I'm trying to authenticate as:

private static String getAccessToken(final String uri, final String username) throws IOException, OAuthException, URISyntaxException
	{
		final OAuthServiceProvider serviceProvider = new OAuthServiceProvider(uri + "/plugins/servlet/oauth/request-token", uri
				+ "/plugins/servlet/oauth/authorize", uri + "/plugins/servlet/oauth/access-token");

		final OAuthConsumer consumer = new OAuthConsumer(null, "MuleBridge", null, serviceProvider);
		consumer.setProperty(RSA_SHA1.PRIVATE_KEY, CONSUMER_PRIVATE_KEY);
		consumer.setProperty(OAuth.OAUTH_SIGNATURE_METHOD, OAuth.RSA_SHA1);

		consumer.setProperty("user_id", username);

		final OAuthAccessor accessor = new OAuthAccessor(consumer);
		final OAuthClient client = new OAuthClient(new HttpClient4());
		final OAuthMessage message = client.getRequestTokenResponse(accessor, OAuthMessage.POST, null);

		accessor.requestToken = message.getParameter(OAuth.OAUTH_TOKEN);
		accessor.tokenSecret = message.getParameter(OAuth.OAUTH_TOKEN_SECRET);

		final OAuthMessage msg = client.getAccessToken(accessor, OAuthMessage.POST, null);

		return msg.getParameter(OAuth.OAUTH_TOKEN);
	}

Mohamed Mahmoud April 15, 2014

consumer.setProperty("user_id", username);

I dont think this is how you set the user, but I am not sure what is the right way either :)

Martin Cassidy April 15, 2014

Neither do I, I was guessing after I saw this: https://answers.atlassian.com/questions/250024/atlassian-connect-how-to-use-an-jira-authentication-header

Do you have any idea why I'm getting back the permission_unknown error?

Mohamed Mahmoud April 15, 2014

Can you try

"xoauth_requestor_id"

instead of user_id and tell me what you get?

source https://bitbucket.org/atlassian/atlassian-oauth/commits/2c4072a5dfe8ddb1b873dfcd8b378466e945b045

Martin Cassidy April 27, 2014

I've been able to work this out, and have posted the solution as a new answer below.

Srikanth BG September 3, 2014

Hi Martin,

Where is your solution for the JIRA authentication? Are you using the 2 legger trusted application approach?

Martin Cassidy September 3, 2014
0 votes
Srikanth BG September 3, 2014

Martin,

Can you please let me know how you worked this out? I am getting stuck in authentication big time

Martin Cassidy September 3, 2014

Hi Srikanth, I worked this out by debugging a locally running jira instance, obtaining the source code for the relevent classes from BitBucket https://bitbucket.org/atlassian/atlassian-oauth.gitso I was able to step through and see what was going on.

If the solution I posted doesn't help, debugging in the same way should help you find out why.

Hope this helps

Youssef ElGhareeb December 26, 2016

Hi Blanka,

I believe that Abbas's code can do user impersonation, you just need to use the correct access token. To do so, you will need to ask the users to give your 3rd party application the permission to read/write data on their behalf. After that you can swap the request tokens for access tokens and use them.

I'm currently having a problem using this code: The class AsynchronousJiraRestClientFactory is not included in the Jar I am using. Also there are 2 "configure" methods that I must override:

  1. public void configure(Filterable filterable, Client client);
  2. public void configure(ApacheHttpClientConfig apacheHttpClientConfig)

I would like to ask about the exact Jar versions that you are using. I am working on Gradle project and here are my dependencies:

compile 'net.oauth.core:oauth:20100527'
compile 'net.oauth.core:oauth-consumer:20100527'
compile 'net.oauth.core:oauth-httpclient4:20090913'

compile 'com.atlassian.jira:jira-rest-java-client:0.5'
compile group: 'com.atlassian.jira', name: 'jira-api', version: '7.0.0'

Blanka Bouskova December 29, 2016

Hi Youssef,
I'm sorry, I have already not been on the project so I can't have a look at it.
I just remember we created some hack on it.. because we didn't want to ask users for permission.

0 votes
Martin Cassidy April 15, 2014

OK, getting as far as the access token request which is returning permission_unknown

net.oauth.OAuthProblemException: permission_unknown
at net.oauth.client.OAuthResponseMessage.toOAuthProblemException(OAuthResponseMessage.java:83)
at net.oauth.client.OAuthClient.invoke(OAuthClient.java:306)
at net.oauth.client.OAuthClient.invoke(OAuthClient.java:260)
at net.oauth.client.OAuthClient.getAccessToken(OAuthClient.java:229)

0 votes
Martin Cassidy March 26, 2014

I've look at that example already, it only shows the 3 legged approach.

0 votes
Mohamed Mahmoud March 18, 2014

I have the same question did anyone manage to get it to work with OAuth ?

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events