HTTPS SSH

atlassian-connect-spring-boot

Maven Central Javadoc codecov

This repository contains a Spring Boot starter for building Atlassian Connect add-ons for JIRA (Software, Service Desk and Core) and Confluence.

This is the officially supported Atlassian Connect Java framework. Please read our documentation to see our other supported and community provided Frameworks and Tools. The tools listed in that documentation will greatly aid you while writing your Atlassian Connect add-on; we highly recommend you make use of them.

Dependencies

Features

atlassian-connect-spring-boot-starter provides the following features:

  • Serving of the add-on descriptor (atlassian-connect.json) with configuration support
  • Automatic handling of installation and uninstallation lifecycle callbacks
  • JSON Web Token authentication for incoming requests
  • Signing of outbound requests made either as the add-on or as a user
  • Authentication of requests from iframe content back to the add-on
  • Persistence of hosts using Spring Data
  • Conversion of standard context parameters to Spring Web MVC model attributes

Additionally, atlassian-connect-spring-boot-jpa-starter provides bindings to Spring Data JPA and Liquibase.

API documentation

The Java API of the starter is documented in Javadoc.

Getting started

To get started with the framework, there are two options. You can generate a fresh project using a Maven archetype, which will set up the structure and dependencies for you. Alternatively if you have an existing Java project, you can manually add a few things to your project to turn it into an Atlassian Connect add-on.

Creating a project from the Maven archetype

Execute the following command:

mvn archetype:generate -DarchetypeGroupId=com.atlassian.connect -DarchetypeArtifactId=atlassian-connect-spring-boot-archetype -DarchetypeVersion=1.5.0

Maven will ask you to define the groupId, artifactId, and version for your new project. You will also be asked to specify the Java package for your source code, which by default is the same as your groupId. Once confirmed, Maven will generate the source of a skeleton Atlassian Connect add-on, including:

Modifying an existing project

  1. Set up your project using Spring Boot (but don't specify any @RequestMappings just yet - we'll get to that in a bit)
  2. Add an extra dependency to your POM:

    <dependency>
        <groupId>com.atlassian.connect</groupId>
        <artifactId>atlassian-connect-spring-boot-starter</artifactId>
        <version>${atlassian-connect-spring-boot.version}</version>
    </dependency>
    
  3. Choose a Spring Data implementation to use with AtlassianHostRepository, and enable Repository scanning with the appropriate @Enable${store}Repositories annotation. If you choose Spring Data JPA, consider using atlassian-connect-spring-boot-jpa-starter.

  4. Create an add-on descriptor file called atlassian-connect.json in the main resource directory of your application. The following is a minimal descriptor that is installable in both JIRA and Confluence (but does nothing):

    {
      "key": "my-addon",
      "baseUrl": "http://localhost:8080",
      "name": "My add-on",
      "authentication": {
        "type": "jwt"
      },
      "lifecycle": {
        "installed": "/installed",
        "uninstalled": "/uninstalled"
      }
    }
    

Running sample applications

atlassian-connect-spring-boot-samples contains a set of sample Spring Boot applications built using atlassian-connect-spring-boot, illustrating how to use different aspects of the starter, and how to use the starter along with other technologies supported by Spring Boot.

Running your application

Build your project, then run the following command:

mvn spring-boot:run

Your application should start up locally on port 8080. If you visit http://localhost:8080/atlassian-connect.json in your browser, you should see your add-on descriptor.

Responding to requests to an endpoint

To implement an endpoint in your add-on, create a spring Controller class with a RequestMapping method. For example:

@Controller
public class HelloWorld {

    @RequestMapping(value = "/hello-world", method = RequestMethod.GET)
    @ResponseBody
    public String helloWorld(@AuthenticationPrincipal AtlassianHostUser hostUser) {
        return "hello-world";
    }
}

This is mostly standard Spring. If you create a module in your add-on descriptor with url attribute "/hello-world", the product will hit this endpoint and you can return whatever you need to. You can also inject information about the host product instance that performed the request as a method parameter, as we've done in this example. The AtlassianHostUser class will also contain the Atlassian Account ID of the product user that triggered the request to your add-on, if any.

Endpoints specified in this way will automatically authenticate incoming requests using JSON Web Tokens. To disable JWT verification for an endpoint, you can annotate your RequestMapping method or your Controller class with @IgnoreJwt. You should only disable JWT verification for endpoints that will not be accessed by an Atlassian product.

Rendering views

Spring Boot supports a number of web template languages. Using a template language allows you to render dynamic content based on information that you receive in a request. For example, the following endpoint takes the request parameter "username" and passes it on to be rendered by a template called "hello".

@RequestMapping(value = "/hello-world", method = RequestMethod.GET)
public ModelAndView helloWorld(@RequestParam String username) {
    ModelAndView model = new ModelAndView();
    model.setViewName("hello");
    model.addObject("userName", username);
    return model;
}

You could use then use this value in your template, for example in a Thymeleaf template:

<body>
    Hi ${userName}!
    ...
</body>

atlassian-connect-spring-boot provides a number of model attributes by default that you can use in your templates.

Atlassian Connect JavaScript API

The Atlassian Connect JavaScript client library establishes a cross-domain messaging bridge between an Atlassian Connect iframe and the host product. All pages to be displayed within an Atlassian product must include a file called all.js, served from the product, in order to establish the bridge and be able to be displayed.

The URL to this file is provided by atlassian-connect-spring-boot for every request in the model attribute atlassianConnectAllJsUrl (also as deprecated atlassian-connect-all-js-url). For example, using Thymeleaf, you would only need to add the following to your pages:

<script th:src="@{${atlassianConnectAllJsUrl}}" type="text/javascript"></script>

Standard context parameters

The following standard context parameters are exposed as model attributes.

  • lic as atlassianConnectLicense (also as deprecated atlassian-connect-license)
  • (deprecated)loc as atlassianConnectLocale and atlassian-connect-locale
  • (deprecated) tz as atlassianConnectTimezone and atlassian-connect-timezone

Making API requests to the product as the add-on

atlassian-connect-spring-boot will automatically sign requests from your add-on to an installed host product with JSON Web Tokens. To make a request, just autowire an AtlassianHostRestClients object into your class.

When responding to an incoming request, outgoing requests to relative URL's will be made to the current authenticated host product.

@Autowired
private AtlassianHostRestClients atlassianHostRestClients;

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

Making API requests to the product as a user

If your add-on has the ACT_AS_USER scope, atlassian-connect-spring-boot also supports making requests as either the currently-authenticated host product user (if one is associated with the request)...

atlassianHostRestClients
    .authenticatedAsHostActor()
    .getForObject("/rest/api/example", Void.class);

or as a host user you specify.

AtlassianHostUser hostUser = new AtlassianHostUserBuilder(host)
    .withUserAccountId("123456:1234abcd-1234-abcd-1234-1234abcd1234")
    .build();
atlassianHostRestClients
    .authenticatedAs(hostUser)
    .getForObject("/rest/api/example", Void.class);

Note: Behind the scenes this feature uses the OAuth 2.0 JWT Bearer token flow.

Authenticating requests from iframe content back to the add-on

The initial request to load iframe content served by the add-on is secured by JWT, as described above. However, add-ons often need to make authenticated requests back to the add-on from within the iframe. Using sessions is not recommended, since some browsers block third-party cookies by default. Also, the JWT token issued by the Atlassian host cannot be used for other requests to the add-on since it contains the qsh (query-string hash) claim.

Instead, add-ons can use the JWT self-authentication token - provided by atlassian-connect-spring-boot with each request in the model attribute atlassianConnectToken (also as deprecated atlassian-connect-token). To authenticate requests with the token, the token must be included in the HTTP request back to the add-on. Whenever possible, e.g. for AJAX requests, the token should be sent in the Authorization HTTP header:

beforeSend: function (request) {
    request.setRequestHeader("Authorization", "JWT " + token);
}

You can also send the token in the jwt query parameter:

<a href="/protected-resource?jwt=...">See more</a>

For convenient access by a script, you can embed the token on the page, e.g. in a meta tag. Using Thymeleaf, that can be done using the following element:

<meta name="token" th:content="${atlassianConnectToken}"/>

Reacting to add-on lifecycle events

Upon successful completion of add-on installation or uninstallation, a Spring application event will be fired: AddonInstalledEvent or AddonUninstalledEvent. These events are fired asynchronously and cannot affect the HTTP response returned to the Atlassian host.

Configuration

You can use a Spring properties file to configure the behaviour of your application. If you define properties in your properties file, they will override the default values set by atlassian-connect-spring-boot.

  • atlassian.connect.allow-reinstall-missing-host

Atlassian hosts will sign all but the first installation request. If your add-on loses the host details during development, this flag enables installations to be accepted by your add-on.

  • atlassian.connect.debug-all-js

The client library for the Atlassian Connect JavaScript API comes in two versions: an obfuscated version for production use (all.js) and a plain version for development use (all-debug.js). This flag populates the atlassian-connect-all-js-url Spring Web MVC model attribute with the development version.

  • atlassian.connect.jwt-filter-order

The order of the servlet filter responsible for JWT authentication. The default is 100.

  • atlassian.connect.jwt-expiration-time

The expiration time of JWT tokens. The default is 3 minutes.

  • atlassian.connect.self-authentication-expiration-time

The expiration time of JWT self-authentication tokens. The default is 15 minutes.

Referencing configuration values in the add-on descriptor

Placeholders for Spring configuration properties (${...}) can be used in both keys and values in the add-on descriptor (atlassian-connect.json). This provides a convenient way to tie your add-on descriptor to your application configuration, and is even more powerful when used in conjunction with Spring profiles. The placeholders are replaced with actual values from configuration each time your add-on descriptor is requested.

atlassian-connect-spring-boot-archetype uses the configuration property addon.base-url to provide the value for the baseUrl add-on descriptor element. If you have made your add-on accessible to the public internet using e.g. ngrok, you can use the following command to start your add-on with the correct base URL:

mvn spring-boot:run -Drun.arguments="--addon.base-url=https://[my-subdomain].ngrok.io"

Making your add-on production ready

Some of the default configuration for atlassian-connect-spring-boot is only safe in a development environment - you should enable a Spring profile called production if deploying to a production environment. There are a number of ways to set the active profiles of a Spring Boot application. For example, if launching your application using the Spring Boot Maven plugin:

mvn spring-boot:run -Drun.profiles=production

By default, your application will use an in-memory database, which is useful for development but inappropriate in a production environment. You will need to point your application towards a production database in your properties file, e.g. to use PostgreSQL you should add something like the following to your properties file:

spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/my-database

For more information on working with SQL databases, see the Spring Boot documentation.

Useful Spring Boot dependencies

Spring Boot Actuator

The Spring Boot Actuator will add a number of monitoring and management endpoints to your application. By default, atlassian-connect-spring-boot moves these endpoints to /manage. You can change this behaviour by setting the value of management.context-path in your properties file.

Spring Boot Developer Tools

Spring Boot provides a number of useful developer tools you can use. The automatic restart capabilities are particularly useful for developing add-ons.

Getting help

If you need help using Spring Boot, see the Getting help section of the Spring Boot Reference Guide.

If you need help developing against Atlassian products, see the Atlassian Developer site.

If you need help using functionality provided by atlassian-connect-spring-boot, please ask in the Atlassian Developer Community.

If you want to report a problem, please raise a support request in Atlassian Ecosystem's Developer Service Desk.

Contributing

Pull requests are always welcome. Please follow the contribution guidelines.

Change log

See the change log for the contents of each release.

License

This project is licensed under the Apache License, Version 2.0.