Last updated Mar 10, 2025

Using Forge Remote in Connect frameworks

You can re-use functions you have built using the Connect framework as Forge Remote endpoints, by adapting that code to work with Forge Remote. From a Forge app developer's perspective, Forge Remote is a layer on top of HTTPS which sends Forge-specific metadata along with the HTTPS request that your remote function can use to make Atlassian-authenticated requests back into the platform and access context information related to the app.

This page includes information on how to do the following, in the frameworks supported by Connect:

  • Authenticate incoming requests to your external function from the Atlassian Forge platform
  • Retrieve information such as tokens for making API calls back into the Atlassian platform as the logged in user, or the app account, or the base URL to use, when making API calls back into the Atlassian platform
  • Access Connect and Forge context data in your external function

How to adapt functions in your Atlassian Connect Express (ACE) app

The information in this section is adapted from content in the Atlassian Connect Express framework Bitbucket repo.

For more information on the Connect javascript functions mentioned in this section, see the source code in the repo.

Your ACE app can act as a remote backend for Forge.

To do so, it must have an appId set in the ./config.json. The value should be the app.id as it appears in the Forge manifest. For example:

1
2
{
  "appId": "ari:cloud:ecosystem::app/406d303d-0393-4ec4-ad7c-1435be94583a"
}

If you are using the DynamoDB adapter for storage, you will also need to add a couple of tables for Forge installation related data. There is code in the Atlassian Connect Express framework Bitbucket repo that will attempt to create these tables here but this may not succeed if your app does not have the necessary permissions. In that case please manually create the following:

  • An InstallationClientKeys table with installationId as a string partition key.

  • A ForgeSettings table with installationId as a string partition key, and key as a string sort key.

Authenticating requests from Forge

The addon.authenticateForge() middleware will verify remote requests from Forge in accordance with the Remote contract and set the following properties on request.context.forge. Refer to the remote contract reference for more information about these properties.

  • request.context.forge.tokens.app: A ready-to-use access token representing the app
  • request.context.forge.tokens.user: A ready-to-use access token representing the current user
  • request.context.forge.apiBaseUrl: The API base URL where all product API requests should be routed.  Example: https://api.atlassian.com/ex/confluence/4c822e2f-510f-48b9-b8d2-8419d0932949
  • request.context.forge.app: Information about the app and installation context
  • request.context.forge.context: Product- and extension-specific data including configuration.

Making requests

There is no pre-configured http client for Forge akin to the Connect addon.httpClient, but making a Forge-authenticated request in your http client of choice is as simple as prepending the apiBaseUrl and providing one of the ready-to-use tokens in the Authorization header, prefixed by Bearer . For example:

1
2
app.get("/forge/backend/resource", addon.authenticateForge(), async (req, res) => {
  const response = await fetch(`${req.context.forge.apiBaseUrl}/wiki/api/content`, {
    headers: {
      Authorization: `Bearer ${req.context.forge.tokens.app}`
    }
  });
});

The addon.authenticateForge() middleware caches the app token and API base URL against the installation ID for later use. If you are making a request in response to an invocation which did not come from Forge, have the appropriate installation ID, and would like to make a request to Atlassian APIs, you can retieve the app token using addon.getForgeAppToken(installationId). For example:

1
2
  app.get("/external-event/{installationId}", async (req, res) => {
    const { appToken, apiBaseUrl } = await addon.getForgeAppToken(req.params.installationId);
    const response = await fetch(`${apiBaseUrl}/wiki/api/content`, {
      headers: {
        Authorization: `Bearer ${appToken}`
      }
    });
  });

Associating Connect and Forge data

To have access to Connect context variables and http client as well as Forge context variables, the addon.associateConnect() middleware can be used in tandem with addon.authenticateForge():

1
2
app.get("/forge/backend/resource", [addon.authenticateForge(), addon.associateConnect()], async (req, res) => {
  /* Both Connect and Forge context are available here, for example:
  addon.httpClient(req);
  addon.settings.set("key", { value: "val" }, req.context.clientKey);
  req.context.forge;
  */
});

How to adapt functions in your Atlassian Connect Spring Boot (ACSB) app

The information in this section is adapted from content in the Atlassian Connect Spring Boot framework Bitbucket repo.

For more information on the Connect Java functions mentioned in this section, see the source code in the repo.

Your app can act as a remote backend for Forge.

To do this, you must set app.id in application.properties or application.yml. The value should be the app.id as it appears in the Forge manifest. For example (YAML):

1
2
app:
  id: ari:cloud:ecosystem::app/406d303d-0393-4ec4-ad7c-1435be94583a

Authenticating requests from Forge

The @ForgeRemote annotation will verify remote requests from Forge in accordance with the Remote contract.

Making requests

AtlassianForgeRestClients is a pre-configured HTTP client for Forge akin to the Connect AtlassianHostRestClients.

We provide the following public methods to make requests to the products:

  • asUser(): send a request authenticated as the current user
  • asApp(): send a request authenticated as the app
  • asApp(String installationId): send a request authenticated as the app. This method can be used outside the @ForgeRemote context by using the stored access token.
  • request(): send a request to the Atlassian API gateway (use /graphql as the path)
  • requestConfluence(): send a request to the Confluence API
  • requestJira(): send a request to the Jira API

You can call either asUser() or asApp() and then chain it with the product you are targeting. For example, to make a request to the Confluence API as the current user, you would do the following:

1
2
@ForgeRemote
@RequestMapping(value = "/frc", method = RequestMethod.POST)
public String testMethod() {
    final HttpEntity<String> entity = new HttpEntity<>(null, null);
    final ResponseEntity<String> response =
            atlassianForgeRestClients
                    .asUser()
                    .requestConfluence()
                    .exchange(
                            "/api/v2/pages",
                            HttpMethod.GET,
                            entity,
                            String.class);
    return response.getBody();
}

Accessing Forge context data

When you receive a request from Forge, it includes a Forge Invocation Token containing context about the request.

You can access this token in your request handler using the ForgeContextRetriever component, like so:

1
2
@ForgeRemote
@RequestMapping(value = "/frc", method = RequestMethod.POST)
public String testMethod() {
    Optional<ForgeApiContext> forgeContext = forgeContextRetriever.getForgeApiContext();
    var installationId = forgeContext.map(ForgeApiContext::getForgeInvocationToken)
            .map(ForgeInvocationToken::getApp)
            .map(ForgeApp::getInstallationId);
}

Associating Connect and Forge data

To enable injecting an AtlassianHostUser with information about the host product instance and making Connect JWT or ACT_AS_USER requests, add associateConnect = true to the @ForgeRemote annotation.

1
2
@ForgeRemote(associateConnect = true)
@RequestMapping(value = "/frc-associate-connect", method = RequestMethod.POST)
public String testMethod(@AuthenticationPrincipal AtlassianHostUser hostUser) {
  /* Connect context is available here via the AtlassianHostUser */
  var clientKey = hostUser.getHost().getClientKey();
}

Rate this page: