Accessing remote services

Most applications will have to communicate with another service other than a Deskpro instance. The SDK does not enforce a particular way of how to implement the communication, leaving this choice to the developer.

The SDK comes with a http client which aims to solve only advanced use cases for accessing remote services:

The browsers security model prevents a page from making requests to another page in a different domain. CORS is a mechanism that allows this which requires the page in the different domain to whitelist the domain of the page making the request. Not all API's are CORS enabled and to get around this issue a server-side proxy ships with Deskpro as part of the Apps SDK. This means you do not have to run your own server-side proxy implementation if you do not have a really compelling reason for it

  • making secure requests

Many of the requests made by apps will involve some sort of authorization, like an OAuth token or API Key. These credentials should be stored securely, meaning your application will not have direct access to them. Instead, by using request placeholders, the server-side proxy will inject those credentials into the outbound request.

Some type of secure requests will require a signature to be computed based on some secure storage data, such is the case for OAuth1 requests. Make sure to read the chapter Signing Requests to learn more.

Whitelisting remote services

If you use the server-side proxy, your application manifest must declare which external URLs can be accessed. This is a security measure to stop abusing this feature.

You specify which URL can be accessed by supplying a list of regex patterns in your application manifest:

{
  "deskpro" : {
      "domainWhitelist": [
        "/^https?://([^.]+\\.)*myjetbrains.com/?.*$/",
        "/^https?://([^.]+\\.)*atlassian.net/?.*$/"
    ]
  }
}

The patterns above allow the server-side proxy to access any url on the myjetbrains.com and atlassian.net domains via http or https.

Using request placeholders

Request placeholders is a technique that allows the server-side proxy to inject settings or other secure storage data into a request sent from an application.

The syntax for a placeholder is:

    {{name}}

where name is the name of the setting or a secure storage data. Placeholders can be used in the URL of the request or in a Header

For example, let's assume that your application is an integration between Deskpro and ServiceX and that an API Key is required to make authorized requests to ServiceX.

Your application manifest will probably contain these entries which secure the API Key:

{
  "deskpro": {
    "settings" : [
      {
        "name": "apiKey",
        "defaultValue": "",
        "title": "Mailchimp API Key",
        "required": true,
        "type": "text"
      }
    ],
    "storage": [
      {
        "name": "apiKey",
        "isBackendOnly": true,
        "permRead": "EVERYBODY",
        "permWrite": "OWNER"
      }
    ]
  }
}

When the application is installed, the administrator will configure it and input an API key and then it will be stored under the apiKey storage key.

To make a request using the API key you would invoke the server-side proxy like this:

  /**
   * @param {string} email the subscriber email
   * @returns {Promise.<DeskproAPIResponse, Error>}
   */
  getData()
  {
    // reference to the deskpro api client, @see https://deskpro.github.io/apps-sdk-core/reference/DeskproAPIClient.html
    const { restApi } = this.props.dpapp;

    return restApi.fetchCORS(
      `https://{serviceX-resource-url}>`,
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json' ,
          'Accept': 'application/json' ,
          'Authorization': 'api_key {{apiKey}}' 
        }
      })
    ;
  }

As you can see, invoking the server-side proxy is done through the DeskproAPIClient.fetchCors method. Notice the Authorization header and how we are using the request placeholder syntax {{apiKey}} to securely inject the ServiceX API key which is stored server-side under the apiKey key.

Signing requests

In some cases you will need to compute a signature based on secure settings or other secure data. Because your application does not have direct access to those values, you must delegate this task to the server-side proxy.

To instruct the proxy to sign your request you must add a X-Proxy-SignWith header to your request configuration. The value of this header tells the proxy how the request should be signed. The proxy will compute the signature, discard the X-Proxy-SignWith header such that it is not added to the final request and in most cases an Authorization header will be added in the final request.

Read the following chapters for an overview of the existing signature types.

Basic Auth signature

This type of signature adds a Authorization: Basic <VALUE> header to the final request. The syntax for this header is:

  HEADER = X-Proxy-SignWith: auth_basic VALUE:VALUE
  VALUE = name | {{name}}

which allows injection of secure data via the request placeholder syntax

To enable this, let's elaborate on the previous example:

  /**
   * @param {string} email the subscriber email
   * @returns {Promise.<DeskproAPIResponse, Error>}
   */
  getData()
  {
    // reference to the deskpro api client, @see https://deskpro.github.io/apps-sdk-core/reference/DeskproAPIClient.html
    const { restApi } = this.props.dpapp;

    return restApi.fetchCORS(
      `https://{serviceX-resource-url}>`,
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json' ,
          'Accept': 'application/json' ,
          'X-Proxy-SignWith': 'auth_basic anystring:{{apiKey}}' 
        }
      })
    ;
  }

As you can see the signature is enabled by adding this entry 'X-Proxy-SignWith': 'auth_basic anystring:{{apiKey}}' to the list of headers.

Oauth1 Signature

This type of signature adds an OAuth1 authorization header to the final request:

Authorization: OAuth
               oauth_consumer_key=<KEY>
               oauth_token=<TOKEN>
               oauth_signature_method="HMAC-SHA1"
               oauth_timestamp=<TIMESTAMP>,
               oauth_nonce="nonce",
               oauth_signature=<VALUE>

The syntax for this header is:

  HEADER = X-Proxy-SignWith: oauth1 CONNECTION_KEY TOKENS_KEY
  CONNECTION_KEY = name
  TOKENS_KEY = name

For example, if your application would use OAuth1 to connect to JIRA your manifest would contain among others the following keys:

{
  "deskpro": {
    "storage": [
     {
        "name": "oauth:jira",
        "isBackendOnly": true,
        "permRead": "EVERYBODY",
        "permWrite": "OWNER"
      },
      {
        "name": "oauth:jira:tokens",
        "isBackendOnly": true,
        "permRead": "OWNER",
        "permWrite": "OWNER"
      }
    ]
  }
}

You would recall that if your application requires OAuth you must declare a storage item name oauth:<PROVIDER NAME> where the information used to create a connection to PROVIDER NAME is stored. The other key,oauth:jira:tokens, is only used to store individual tokens and it can have any name you deem fit, but we recommend the oauth:<PROVIDER NAME>:tokens convention. The most important thing about the tokens key is that it must be readable only be its owner.

Building on the previous example, this is how we would make an OAuth1 request:

To enable this, let's elaborate on the previous example:

  /**
   * @param {string} email the subscriber email
   * @returns {Promise.<DeskproAPIResponse, Error>}
   */
  getData()
  {
    // reference to the deskpro api client, @see https://deskpro.github.io/apps-sdk-core/reference/DeskproAPIClient.html
    const { restApi } = this.props.dpapp;

    return restApi.fetchCORS(
      `https://{serviceX-resource-url}>`,
      {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json' ,
          'Accept': 'application/json' ,
          'X-Proxy-SignWith': 'oauth1 oauth:jira oauth:jira:tokens' 
        }
      })
    ;
  }

As you can see the signature is enabled by adding this entry 'X-Proxy-SignWith': 'oauth1 oauth:jira oauth:jira:tokens' to the list of headers.

Last updated