JIRA Add-on, AtlassianHostRestClients

Hi to all!
I have an issue with implementation of AtlassianHostRestClients, RestTemplate and authentication (JWT) in Java.

I’ve tried, I think, almost everything but without a success.
Add-on is simple and it is properly registered at my development Jira host (https://{my_name}.atlassian.net/).

Technical points:

  • Java8
  • Maven project configuration (default definition from Atlassian repository)
  • Atlassian Connect Spring Boot 1.4.3
  • Spring boot 1.5.3 (as newer versions are not working properly with 1.4.3 Atlassian)
  • PostgreSQL db (correctly configured and Atlassian data is properly written in a database)

Plugin’s JSON:

{
  "key": "${addon.key}",
  "baseUrl": "${addon.base-url}",
  "name": "Jira Addon (Spring Boot) Template",
  "authentication": {
    "type": "jwt"
  },
  "lifecycle": {
    "installed": "/installed",
    "uninstalled": "/uninstalled"
  },
  "scopes": [
    "READ",
    "WRITE",
    "DELETE",
    "ADMIN",
    "ACT_AS_USER"
  ],
  "modules": {
    "generalPages": [
      {
        "key": "home-page-jira",
        "location": "system.top.navigation.bar",
        "name": {
          "value": "Jira Addon template"
        },
        "url": "/home"
      }
    ]
  }
}

Controller class:

@Controller
public class HomeController {

    @Autowired
    private AtlassianHostRestClients atlassianHostRestClients;

    @Autowired
    private AtlassianHostRepository atlassianHostRepository;

    @GetMapping("/home")
    public String getHome(@AuthenticationPrincipal AtlassianHostUser hostUser, Model model) {
        AtlassianHost host = atlassianHostRepository.findAll().iterator().next();
        System.out.println("-------------- URL " + host.getBaseUrl()+"/rest/api/2/application-properties");
        RestTemplate restTemplate = atlassianHostRestClients.authenticatedAsAddon();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(host.getBaseUrl()+"/rest/api/2/application-properties",String.class);
        //atlassianHostRestClients.authenticatedAsAddon().getForObject(host.getBaseUrl() + "/rest/api/2/application-properties", String.class);

        model.addAttribute("userKey", hostUser.getUserKey());

        return "home";
    }
}

Everything is working fine until line where “restTemplate.getForEntity” can be found.
Issue is that request is not forbidden (org.springframework.web.client.HttpClientErrorException: 403 Forbidden)

I’ve tried to use authenticatedAsHostActor() method but again the same situation, to be more specific in this case I’m getting “not authorized” error message.

Beside error messages about authentication, before requesting Jira’s REST API I can see in a log that authentication should be fine.

.request.jwt.JwtGenerator       : Generating JWT with canonical request: CanonicalHttpUriComponentsRequest[method=GET,relativePath=/rest/api/2/application-properties,parameterMap=[]]

I’ve already read all posts and comments here and on the Internet, and I didn’t found a solution.
Am I doing something wrong here?

If any of you have some snippet or example which is working fine with this set of versions, please post it here.

Best regards!
Alex

@aleksandar.cajic, the API documentation for GET /rest/api/2/application-properties includes the line:

Apps cannot access this REST resource.

Connect apps cannot access all REST endpoints, only whitelisted ones. This endpoint has not been whitelisted.

Aside from that, you have another problem in your code, since you are taking the Jira base URL from the first installed host you find in your database. As long as you only have one host, that should work. But you really should be calling AtlassianHostUser#getHost() to obtain the host for the current authenticated user.

@epehrson
Thank you for your reply!
Your comment led me to think little bit different about implementation.
Now I have a solution about Jira API calls.

Best regards!
Alex

Hello @aleksandar.cajic.

If possible, could you please tell how you resolved this?

You used some other authorization approach ?

Thank you

Hi @quadr988,

You can use API token to call Jira cloud rest API. If resource has “Apps cannot access this REST resource” permission then API token will use to consume REST API.

JAVA Code:

// This code sample uses the  'Unirest' library:
// http://unirest.io/java.html
HttpResponse<JsonNode> response = Unirest.get("/rest/api/3/application-properties")
  .basicAuth("email@example.com", "<api_token>")
  .header("Accept", "application/json")
  .asJson();

System.out.println(response.getBody());

Reference:
https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-application-properties-get

Generate API Token:
https://confluence.atlassian.com/cloud/api-tokens-938839638.html

1 Like

@dchouksey89 Thanks a lot for clarifying this!

The only problem is that while using Basic Auth the mail and token are specified statically for definite user, but if it is necessary to make REST call from user behalf from any host dynamically, no matter what host is used and using AuthenticatedAsUser or AuthenticatedAsHost - the 400 Bad Request error is reproduced.

  • authenticatedAsAddon() --> only access to not restricted REST API

and if using restricted REST API:

  • authenticatedAs(hostUser) --> (+ “ACT_AS_USER” in app descriptor) - 400 error
  • authenticatedAsHostActor() --> (+ “ACT_AS_USER” in app descriptor) - 400 error

Is there any additional configuration needed for using authenticatedAsHostActor()/authenticatedAs(hostUser) except “ACT_AS_USER” in app descriptor ?

Thank you!

Hi @quadr988,

So if you are trying to access restricted resource for app then do not use library client methods as It works on JWT token which strictly follows the app scopes .

Solution:
Add this Unirest library in your pom.xml file and use the below code to call the api.

POM.xml

<!-- https://mvnrepository.com/artifact/com.mashape.unirest/unirest-java -->
<dependency>
    <groupId>com.mashape.unirest</groupId>
    <artifactId>unirest-java</artifactId>
    <version>1.4.9</version>
</dependency>

Calling API:

/ This code sample uses the  'Unirest' library:
// http://unirest.io/java.html
HttpResponse<JsonNode> response = Unirest.get("/rest/api/3/application-properties")
  .basicAuth("email@example.com", "<api_token>")
  .header("Accept", "application/json")
  .asJson();

System.out.println(response.getBody());

Add scope as ADMIN in descriptor file.

"scopes": ["ADMIN"],
1 Like

Hello @dchouksey89 .

Thank you! This really seems to be the right path for implementing what is needed for me.

Thank you again for your time and help :grinning: