How to authenticate Java PUT request to Cloud REST api

I’m trying to assign an issue in JSD with a Java server.

This Curl command works great:

curl -D- -u admin:password -X PUT --data '{ "fields": { "assignee": {"name":"admin"} }}' -H "Content-Type: application/json" https://ac-demo.atlassian.net/rest/api/2/issue/HELP-3

First question, how should I authenticate the admin:password aspect of this? I understand that the plugin has its “own” user account that it will be using on each Cloud instance, but I am not sure how to accept that.

Second question, what’s a good starting point for writing Java that accomplishes this?

I’ve found samples such as this and this, though there is a lot of conflict.

For the sake of completeness, here’s an isolated sample of the sort of thing I’d like to do:

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.IOException;
import java.net.MalformedURLException;
import java.io.*;

public class HelloWorld{

     public static void main(String []args){
        String data = "{ \"fields\": { \"assignee\":{\"name\":\"admin\"} }}";
        
        try{
            URL url = new URL("https://ac-demo.atlassian.net/rest/api/2/issue/HELP-3");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("PUT");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Accept", "application/json");
            OutputStreamWriter osw = new OutputStreamWriter(connection.getOutputStream());
            osw.write(data);
            osw.flush();
            osw.close();
            System.err.println(connection.getResponseCode());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(data);
     }
}
1 Like

I like that you start with basic authentication to check your REST API requests. That means you know the right headers, HTTP method, and payload to send. I highly recommend this technique for isolating REST problems from the implementation in a specific language.

The authentication for Atlassian Connect requires JWT. The documentation I just linked also provides sample code for creating a JWT token in Java, which you can combine with your URL construction. That said, your add-on must be a web application so that it can receive information from JIRA, specifically the lifecycle callback for installed.

Since it appears you are starting from scratch, you might find it easier to use Atlassian Connect Spring Boot. It is a lot easier than starting from scratch! Especially because it has built-in features for dealing with the lifecycle and JWT authentication.

1 Like

Thanks for the reply. The addon is using Atlassian Connect Spring Boot, which is imported through Maven under com.atlassian.connect.

Following those examples, I’ve added another dependency as follows: (reference)

    <dependency>
      <groupId>com.atlassian.jwt</groupId>
      <artifactId>jwt-api</artifactId>
      <version>1.6.2</version>
    </dependency>

And in repositories;

  <repositories>
    <repository>
      <id>atlassian</id>
      <url>https://maven.atlassian.com/content/repositories/atlassian-public</url>
    </repository>
  </repositories>

At present, it looks like the necessary packages are being downloaded and installed fine, but I am still seeing errors along these lines:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project example: Compilation failure: Compilation failure:
[ERROR] /.../cloud/src/main/java/com/controller/AssignRespondServlet.java:[39,1] package com.atlassian.jwt.core.writer does not exist

I’m not quite sure why this wouldn’t exist! Is there an error with my use of the dependency?

Still struggling on through. I added a dependency for jwt-core and the Maven build errors have gone.

However, I’m having issue with one method from the sample; CanonicalHttpUriRequest, which appears to not have everything required for the sample? I’m struggling to find more information about it in the docs.

My stack trace starts with:

java.lang.NoSuchMethodError: org.apache.commons.lang.StringUtils.defaultIfBlank(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
	at com.atlassian.jwt.httpclient.CanonicalHttpUriRequest.<init>(CanonicalHttpUriRequest.java:25) ~[jwt-core-1.5.11-0002.jar!/:na]
	at com.example.controller.AssignRespondServlet.createUriWithJwt(AssignRespondServlet.java:146) ~[classes!/:1.1.0]

You need to include Apache Commons Lang in your dependencies; it appears to be missing from Atlassian’s JWT library.

<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
</dependency>
1 Like

David, you’re a hero. That did the trick.

@thomas.knight, if you use Atlassian Connect Spring Boot, you don’t need to (and ideally shouldn’t) create JSON Web Tokens using another library.

Please see the Javadoc for AtlassianHostRestClients for examples of how to obtain a Spring RestTemplate instance preconfigured for JWT authentication.

If you would prefer to use a different HTTP client, atlassian-connect-spring-boot-samples contains sample code for using Jersey and Retrofit.

1 Like

@epehrson How is that used? I understand that the principle is this aspect:

@Autowired
private AtlassianHostRestClients atlassianHostRestClients;

public void doSomething() {
    atlassianHostRestClients.authenticatedAsAddon().getForObject("/rest/api/example", Void.class);

However, I’m not sure how to use it in a live scenario to actually read the JSON received in a GET request. Could you elaborate?

@thomas.knight, the guide Consuming a RESTful Web Service explains that better than I could. (Note the jackson-databind Maven dependency.)

@epehrson Is it possible to use the Connect Spring Boot libraries to receive the GET response as a String with the whole JSON output?

Absolutely, @thomas.knight. Just pass String.class as the responseType to any of the getFor...() methods in RestTemplate.

For another time, I think you’ll find many questions around basic Spring usage answered on Stack Overflow, and you’d surely get new questions answered quicker there. For example, How to extract property from JSON embedded within JSON? shows how to retrieve data using RestTemplate as a String to deserialize yourself using Gson, as a Jackson ObjectNode or directly as a POJO (the recommended way).