Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

External API Authentication and Authorization Using Service Accounts

Introduction

Conga systems support server-to-server interactions such as those between a web application and a Conga application. For these scenarios, a service account is needed for your applications to call Conga application APIs, because no users are directly involved in those workflows. For scenarios where the user is involved in making the request to Conga applications, users can generate API tokens through the Application Portal UI, if they have permissions to do so. This document focuses on service-to-service scenarios wherein the user makes no direct requests to Conga applications.

Conga applications use JWT (JSON Web Token) and industry-standard OAuth 2.0 protocol for service-to-service authentication, encoding JWT information in the access_token payload, containing information about the authorization request. This document describes how an application can complete the service-to-service OAuth 2.0 flow using JWT payloads and a series of REST API calls.

Note: The remainder of these instructions will use the values for the US1 realm. Substitute or adjust the values for another realm where necessary.

Preparing to Make an Authorized API Call

Note: The following authentication process uses OAuth 2.0.
  1. Obtain a service account from the Application Portal.
    Note: Please refer to the Service Accounts documentation for instructions on creating one.
  2. Create a signed JSON Web Token (JWS).
  3. Request an access token from the Application Portal.
  4. Make an authorized API call.

Create a Signed JSON Web Token (JWT)

UNDERSTANDING THE JWT STRUCTURE

A JSON Web Token (JWT) is a compact and secure way to transfer information between two parties. It is commonly used for authentication in web applications. When a user successfully logs in with their credentials, a JSON Web Token is issued.

A JWT is composed of three parts:

  • Header
  • Claim set
  • Signature

The header and claim set are JSON objects, which are serialized to UTF-8 bytes, then encoded using Base64url encoding.

Follow the steps below to construct the JWT.

  • THE JWT HEADER

The header consists of two fields that indicate the signing algorithm and the format of the assertion. The algorithm field is mandatory, but JWT token format is optional, and each field has only one value. Service accounts rely on the RSA SHA-256 algorithm and JWT token format.

JWT Header value (formatted for readability):

{
	"typ":"JWT",
	"alg":"RS256"
}

Base64 encoded value of the header: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9

  • THE JWT CLAIM SET

The JWT claim set contains information about the JWT, including the application roles being requested, the issuer is the service account, the scope is the URN of the Conga application, the token request generated time, and token request expiration time.

Required claims

NAMEDESCRIPTION
issThe issuer URN. Issuer is the service account (Must be in lowercase)
audThe URL of the Application Portal
scopeURN of the resource owner (target Conga application)
iatThe time the assertion was issued, specified as seconds since 00:00:00 UTC, January 1, 1970
expThe expiration time of the assertion, specified as seconds since 00:00:00 UTC, January 1, 1970 (Max 1 hour after the issued time)
Service account ID (blue below) is the iss and the application ID (yellow) is the scope.

The aud claim should be the Application Portal URL:

Conga Cloud Realm Application Portal URL
US REALM 1https://login.us1s1.congacloud.com
US1 Previewhttps://login.us0s1.congacloud.com
EU REALM 1https://login.eu1s1.congacloud.com
EU1 Previewhttps://login.eu0s1.congacloud.com
AU REALM 1https://login.au1s1.congacloud.com
EU REALM 2https://login.eu1s2.congacloud.com
US REALM 2https://login.us1s2.congacloud.com

EXAMPLE

Claim set (formatted for readability):

{
	"aud":"https://login.us1s1.congacloud.com",
	"scope":"urn:pros:portal:app:d4ae9f99-dd05-4a84-bf48-6b76db921e98",
	"iss":"urn:pros:portal:acct:50b84e69-1209-43bb-ac56-4533104c1464",
	"exp":1518757838,
	"iat":1518739838
}

Additional Claims

NAMEDESCRIPTION
rolesList of application roles to include in the access token. If omitted, the token contains all allowed application roles for the service account.

EXAMPLE

Claim set with roles claim:

{
	"aud":"https://login.us1s1.congacloud.com",
	"scope":"urn:pros:portal:app:6801b597-ee80-4ef3-988f-c970670da78b",
	"roles":["ANALYST","USER"],
	"iss":"urn:pros:portal:acct:39777bb4-2c8f-40be-b662-9d4184707c20",
	"exp":1518759048,
	"iat":1518741048
}

Base64url encoded value of the claim set:

eyJhdWQiOiJodHRwczpcL1wvbG9naW4udXMxLnByb3NjbG91ZC5jb20iLCJzY29wZSI6InVybjpw cm9zOnBvcnRhbDphcHA6NjgwMWI1OTctZWU4MC00ZWYzLTk4OGYtYzk3MDY3MGRhNzhiIiwicm9s ZXMiOlsiQU5BTFlTVCIsIlVTRVIiXSwiaXNzIjoidXJuOnByb3M6cG9ydGFsOmFjY3Q6Mzk3Nzdi YjQtMmM4Zi00MGJlLWI2NjItOWQ0MTg0NzA3YzIwIiwiZXhwIjoxNTE4NzU5MDQ4LCJpYXQiOjE1 MTg3NDEwNDgsInRpZCI6IjJiNjQ4NDZmLTA5YzMtNDc0MC05MTJjLTdjZGVhZmE3N2JlZSJ9

Encoding the JWT

Concatenate the Base64url-safe encoded values of JWT header and the claim set with a period (.). Below is an example of the above JWT header and JWT claim set concatenated:

Example concatenated header and claim set

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4udXMxLnB yb3NjbG91ZC5jb20iLCJzY29wZSI6InVybjpwcm9zOnBvcnRhbDphcHA6NjgwMWI1OTctZWU4MC0 0ZWYzLTk4OGYtYzk3MDY3MGRhNzhiIiwicm9sZXMiOlsiQU5BTFlTVCIsIlVTRVIiXSwiaXNzIjo idXJuOnByb3M6cG9ydGFsOmFjY3Q6Mzk3NzdiYjQtMmM4Zi00MGJlLWI2NjItOWQ0MTg0NzA3YzI wIiwiZXhwIjoxNTE4NzU5MDQ4LCJpYXQiOjE1MTg3NDEwNDgsInRpZCI6IjJiNjQ4NDZmLTA5YzM
tNDc0MC05MTJjLTdjZGVhZmE3N2JlZSJ9
  • COMPUTING THE SIGNATURE

A JSON Web Signature (JWS) is the specification that guides the mechanics of generating the signature of the JWT. The input for the signature is the byte array of the concatenated header and claim set. The signing algorithm of the JWT header must be used when computing the signature. The only signing algorithm supported by the Application Portal is RSA using SHA-256 hashing algorithm. This is expressed as RS256 in the alg field in the JWT header.

Sign the UTF-8 representation of the input using SHA256withRSA algorithm using the private key of the service account. The output will be a byte array. The signature and resulting byte array must be Base64url encoded. Then the header, claim set, and signature are concatenated together with a period (.) character. The result is the JWT. It should be the following:

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

Example JWT with signature:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwczpcL1wvbG9naW4udXMxLnB yb3NjbG91ZC5jb20iLCJzY29wZSI6InVybjpwcm9zOnBvcnRhbDphcHA6NjgwMWI1OTctZWU4MC0 0ZWYzLTk4OGYtYzk3MDY3MGRhNzhiIiwicm9sZXMiOlsiQU5BTFlTVCIsIlVTRVIiXSwiaXNzIjo idXJuOnByb3M6cG9ydGFsOmFjY3Q6Mzk3NzdiYjQtMmM4Zi00MGJlLWI2NjItOWQ0MTg0NzA3YzI wIiwiZXhwIjoxNTE4NzU5MDQ4LCJpYXQiOjE1MTg3NDEwNDgsInRpZCI6IjJiNjQ4NDZmLTA5YzM tNDc0MC05MTJjLTdjZGVhZmE3N2JlZSJ9.FSVE2VS6uku9yY1JN74pF6mokZd6E9w9Urk02WsMpV 6P4fVxHcgz66jhx1yHUb5ytD0eoNbSkLP5drVXftZM2Vu3fuj5LkUPIQvJWgp6dtU56netb3qKMX kFH39leVGYuwHMc_f-wSuv-DWodm9gAkiDZ5wguPw2pQKeqxloICBW_pKxP8YxaxEDsRJqx2aIoH7GCNto_CHbp2z0woJVUZksTDG_q 54kVX_DUIGSrYUo6- mbmkATic8KeqQLSNYN6H4cwcaikYUho1WZrEvFCEEHJBZD1TlwoxlnQh5c6LPCvf_3s1mQJXlttx Q6qGhxkPmHFjCPe0Xfkt5tbu29Q

Java example of signature computation

Below is an example of JWT signature computation in Java using the nimbus library.

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;

....    
  /**
   * Returns the signed JWT for the service to service token request from 
   * Home using service account credentials.
   *
   * @param audience         Home URL
   * @param scope            The target application
   * @param issuer           The service account
   * @param privateKeyBase64 Base 64 value of the PKCS8 formatted private key of
   *                         the service account (exclude start/end comments and
   *                         new line characters from the key)
   *
   * @return String value of the signed JWT
   *
   * @throws Throwable
   */
  public String generateJwtUsingServiceAccountCredentials(
      String audience, String scope, String issuer, String privateKeyBase64) throws Throwable {
    JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
    builder.issuer(issuer);
    builder.audience(audience);
    builder.claim("scope", scope);
    builder.issueTime(Date.from(Instant.now()));
    builder.expirationTime(Date.from(Instant.now().plusSeconds(60 * 5)));
    JWTClaimsSet jwtClaimsSet = builder.build();

    SignedJWT signedJWT = rsaSign(jwtClaimsSet, new Base64(privateKeyBase64).decode());

    return signedJWT.serialize();
  }

  private SignedJWT rsaSign(JWTClaimsSet claimsSet, byte[] privateKeyBytes) throws JOSEException {
    try {
      PrivateKey privateKey = KeyFactory.getInstance("RSA")
          .generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));

      RSASSASigner signer = new RSASSASigner(privateKey);
      SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet);
      signedJWT.sign(signer);
      return signedJWT;
    } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
      throw new JOSEException(e.getMessage(), e);
    }
  }

Request an Access Token from the Application Portal

The application can use the signed JWT to request an access token by making a REST call to the token endpoint of the Application Portal. Be sure to use the token endpoint URL of the correct realm below:

Conga Cloud RealmApplication Portal URL
US REALM 1https://login.us1s1.congacloud.com/api/login/oauth2/token
US1 Previewhttps://login.us0s1.congacloud.com/api/login/oauth2/token
EU REALM 1https://login.eu1s1.congacloud.com/api/login/oauth2/token
EU1 Previewhttps://login.eu0s1.congacloud.com/api/login/oauth2/token
AU REALM 1https://login.au1s1.congacloud.com/api/login/oauth2/token
EU REALM 2https://login.eu1s2.congacloud.com/api/login/oauth2/token
US REALM 2https://login.us1s2.congacloud.com/api/login/oauth2/token

The following parameters are required in the POST request:

NAMEDESCRIPTION
grant_typeurn:ietf:params:oauth:grant-type:jwt-bearer
assertionThe JWT, including the signature
Note: Tokens can be re-used within the period of their validity. It is not necessary to request a new token for every API call request.

Example dump of the HTTP POST request:

POST /api/login/oauth2/token
Host: https://login.us1s1.congacloud.com
Content-Type: application/x-www-form-urlencoded
Body:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwczpc
L1wvbG9naW4udXMxLnByb3NjbG91ZC5jb20iLCJzY29wZSI6InVybjpwcm9zOnBvcnRhbDphcH
A6NjgwMWI1OTctZWU4MC00ZWYzLTk4OGYtYzk3MDY3MGRhNzhiIiwicm9sZXMiOlsiQU5BTFlT
VCIsIlVTRVIiXSwiaXNzIjoidXJuOnByb3M6cG9ydGFsOmFjY3Q6Mzk3NzdiYjQtMmM4Zi00MG
JlLWI2NjItOWQ0MTg0NzA3YzIwIiwiZXhwIjoxNTE4NzU5MDQ4LCJpYXQiOjE1MTg3NDEwNDgs
InRpZCI6IjJiNjQ4NDZmLTA5YzMtNDc0MC05MTJjLTdjZGVhZmE3N2JlZSJ9.FSVE2VS6uku9y
Y1JN74pF6mokZd6E9w9Urk02WsMpV6P4fVxHcgz66jhx1yHUb5ytD0eoNbSkLP5drVXftZM2Vu
3fuj5LkUPIQvJWgp6dtU56netb3qKMXkFH39leVGYuwHMc_f-wSuv-DWodm9gAkiDZ5wguPw2p
QKeqxloICBW_pKxP8YxaxEDsRJqx2aIoH7GCNto_CHbp2z0woJVUZksTDG_q54kVX_DUIG SrY
Uo6-mbmkATic8KeqQLSNYN6H4cwcaikYUho1WZrEvFCEEHJBZD1TlwoxlnQh5c6LPCvf_3s1mQ
JXlttxQ6qGhxkPmHFjCPe0Xfkt5tbu29Q

HANDLING THE RESPONSE

If the JWT and the access token request are properly formed and the service account has access to the target application, then the successful JSON response from the Application Portal includes an access token.

Example Response:

{
  "access_token":"eyJwMnMiOiJoNE9rd2ZqN2w5MmEZffQ.iK5FTFA-5lGbsoRSpmoW0DTyD_3N342WnqOw0y584sIIiDs9JWSCAN5NpKwDty5uBxd-PmXYm230z4IMUqK72hQg3HzJvEK0K8PsB0beXM7UA.3XBbHW1ZuLHLnTYH06gJ1g",
  "token_type":"Bearer",
  "expires_in":3600
}

The access_token field contains the access token value and expires_in field indicates the token lifetime in seconds.

Making an Authorized API Call

The above access token needs to be included in the Authorization HTTP header of the API call to the Conga Application. See the example below:

GET /api/price Authorization: Bearer

eyJwMnMiOiJoNE9rd2ZqN2w5MmEZffQ.iK5FTFA-5lGbsoRSpmoW0DTyD_3N342WnqOw0y584sIIiDs9JWSCA N5NpKwDty5uBxd-PmXYm230z4IMUqK72hQg3HzJvEK0K8PsB0beXM7UA.3XBbHW1ZuLHLnTYH06gJ1g

Troubleshooting

If you are not receiving the access token, be sure to verify that the claimset fields above (aud, iss, and scope) are correct and are all lower-case.

Couldn't unwrap AES key: javax.crypto.IllegalBlockSizeException: Integrity check failed

This indicates that there a public-private key mismatch between the system making the call and want Conga is expecting. Be sure that the private key associated with the public key that you uploaded into the service account configuration is loaded in to the service responsible for the API calls (Java Keystore, etc). You may have to verify that you uploaded to the version of the runtime that is actually responsible for the calls, as there can often be several options running simultaneously.

Invalid request: Signature verification failed

This indicates an issue with one more more of the fields in the payload. Verify that the aud, iss, and scope fields are the correct values, iat should be the current time, and exp should not be more than one hour from the current time. Note that iat and exp are represented in seconds, not minutes or hours.