Forum Discussion

arunkumar2891's avatar
arunkumar2891
New Contributor
2 months ago

JWT Configuration for SnapLogic Public API

This document details the process of configuring JWT authentication for the SnapLogic Public API using self-generated keys without the use of any third party JWT providers. It covers key generation, JWKS creation, SnapLogic configuration.

1. Key Generation and JWKS Creation

1.1 Setup the CMD
  • Open CMD
  • Mount the OpenSSL bin folder
 1.2 Generate the Private Key

Use the following command to generate a 2048-bit RSA private key in the PEM format.

BASH openssl genpkey -algorithm RSA -out jwt_private_key.pem -pkeyopt rsa_keygen_bits:2048

  • Result:A file named jwt_private_key.pem will be created. This key must be kept secret and secure.
1.3 Convert to PKCS#8 Format

The JWT generation requires the private key to be in the PKCS#8 format for proper decoding. So, convert the jwt_private_key.pem into PKCS8 format.

BASH openssl pkcs8 -topk8 -in jwt_private_key.pem -out jwt_private_key_pkcs8.pem -nocrypt

  • Result:A new file, jwt_private_key_pkcs8.pem, will be created. Use this key in your application for signing JWTs.
1.4 Extract the Public Key

The public key is required for the JWKS document.

BASH openssl rsa -in jwt_private_key_pkcs8.pem -pubout -out jwt_public_key_pkcs8.pem

  • Result:A file named jwt_public_key.pem will be created.
1.5 Extract Public Key Components for JWKS:
  • Extract the Modulus and Exponent from the CA-signed public key. These are the core components of your JWKS.

BASH openssl rsa -pubin -in jwt_public_key_pkcs8.pem -text -noout The output will look like this:Public-Key: (2048 bit)Modulus:    00:d2:e3:23:2c:15:a6:5b:54:c1:89:f7:5f:41:bf:...Exponent: 65537 (0x10001)

2. JWKS Creation and JWT Endpoint Configuration

2.1. The below steps explain how to create the JWKS JSON within Snaplogic.

2.1.1 Create a new project sapce and a project "JWKS" or even an API with name "JWKS" - (This step is just for access control and the API policy to be applied only for this purpose)
2.1.2 Create the pipeline CreateJWKS
2.1.3 Update the Modulus and Exponent values in the mapper copied from the step 1.5 in the section Key Generation, JWKS Creation, and Certificate Signing.
2.1.4 Select the language as Python and replace the default script in the script snap with

# Import the interface required by the Script snap.
from com.snaplogic.scripting.language import ScriptHook
import base64
import hashlib


class TransformScript(ScriptHook):
    def __init__(self, input, output, error, log):
        self.input = input
        self.output = output
        self.error = error
        self.log = log

    # Helper function to convert an integer to a big-endian byte string
    # This is a manual implementation of int.to_bytes() for Python 2.7
    def int_to_bytes(self, n):
        if n == 0:
            return '\x00'
       
        hex_string = "%x" % n
        if len(hex_string) % 2 == 1:
            hex_string = '0' + hex_string
       
        return hex_string.decode("hex")

    def execute(self):
        self.log.info("Executing Transform script")

        while self.input.hasNext():
            try:
                inDoc = self.input.next()
               
                # Modulus conversion logic
                hex_input = inDoc['hex_string_field']
                clean_hex_string = hex_input.replace('\n', '').replace(' ', '').replace(':', '')
                modulus_bytes = clean_hex_string.decode("hex")
                modulus_base64url = base64.urlsafe_b64encode(modulus_bytes).rstrip('=')

                # Exponent conversion logic
                exponent_input_str = inDoc['exponent_field']
                import re
                match = re.search(r'^\d+', exponent_input_str)
                if match:
                    exponent_int = int(match.group(0))
                else:
                    raise ValueError("Could not parse exponent value from string.")

                exponent_bytes = self.int_to_bytes(exponent_int)
                exponent_base64url = base64.urlsafe_b64encode(exponent_bytes).rstrip('=')
               
                # Dynamic Key ID (kid) generation logic
                # Concatenate the Base64url-encoded modulus and exponent
                jwk_string = modulus_base64url + exponent_base64url
               
                # Compute the SHA-256 hash
                kid_hash = hashlib.sha256(jwk_string).digest()
               
                # Base64url encode the hash to create the kid
                kid = base64.urlsafe_b64encode(kid_hash).rstrip('=')

                # Prepare the output document with all values
                outDoc = {
                    'modulus_base64url': modulus_base64url,
                    'exponent_base64url': exponent_base64url,
                    'kid': kid
                }

                self.output.write(inDoc, outDoc)
               
            except Exception as e:
                errDoc = { 'error' : str(e) }
                self.log.error("Error in python script: " + str(e))
                self.error.write(errDoc)

        self.log.info("Script executed")

    def cleanup(self):
        self.log.info("Cleaning up")

hook = TransformScript(input, output, error, log)

2.1.5 Replace the default value in the JSON generator with

{
    "keys": [
        {
            "kty": "RSA",
            "alg": "RS256",
            "kid": $kid,
            "use": "sig",
            "e": $exponent_base64url,
            "n": $modulus_base64url
        }
    ]
}

This will return us the JWKS JSON.

2.2. The below step creates the public endpoint for the JWKS JSON.

The below steps can be done as a standalone API as well as a separate project for this JWKS authentication.

2.2.1 Create the pipeline getJWKS

 

2.2.2 Paste the JWKS generated in step 2.1.5 above in the JSON Generator:

{
    "keys": [
        {
            "kty": "RSA",
            "alg": "RS256",
            "kid": "vTfx70NbtVbarHnBetDHNqLXsWVr4Ue5oC32TFNSMlc",
            "use": "sig",
            "e": "AQAB",
            "n": "ANLjIywVpltUwYn3X0G_********_3JmpnSh419wDZC_8-Ts"
        }
    ]
}

2.2.3 Follow the config as shown for JSON Formatter: 
2.2.4 Create a Task named jwks.json and follow the task config as shown and copy the Ultra Task HTTP Endpoint:

Select the Snaplex as Cloud, as the endpoint have to be truly public.

 

2.2.5 Create an API Policy - Anonymous Authenticator and key in the details as shown:



2.2.6 Create an API Policy - Authorize By Role and key in the details as shown:

 

3. SnapLogic JWT Configuration

This step links SnapLogic to your JWKS.

Configure Admin Manager:

3.1 In the SnapLogic Admin Manager, navigate to Authentication > JWT.

3.1.1 Issuer ID: Enter a unique identifier for your issuer. This can be a custom string.
3.1.2 JWKS Endpoint: Enter the full HTTPS URL where you have hosted the JWKS JSON file, HTTP Endpoint copied from step B.4 in the Section JWKS Creation and JWT Endpoint Configuration.

3.2 In the SnapLogic Admin Manager, navigate to Allowlists > CORS allowlist

3.2.1 Add domain: Key in the domain https://*.snaplogic.com in the Domain text box, click on Add Domain and click on Save.

4. JWT Generation and Structure

The JWT must be created with a header that references your custom kid and a payload with claims that match SnapLogic's requirements.

4.1 Header:

JSON

{  "alg": "RS256",  "typ": "JWT",  "kid": "use the key id generated in step 2.1.5 from the section JWKS Creation and JWT Endpoint Configuration"}

4.2 Payload:

JSON

{

  "iat": {{timestampIAT}},

  "exp": {{timestampEXP}},

  "sub": "youremail@yourcompany.com",

  "aud": "https://elastic.snaplogic.com/api/1/rest/public",

  "iss": "issuer id given in section 3.1.1.1",

  "org": "Your Snaplogic Org"

}

4.3 Sign the JWT: Use the jwt_private_key_pkcs8.pem to sign the token with your application's JWT library.

4.4 Postman Pre-Request script to automatically generate epoch timestamps for iat and exp claims

let now = new Date().getTime();

    let iat = (now/1000)

    let futureTime = now + (3600 * 1000);

    let exp = (futureTime/1000)

    // Set the collection variable

    pm.collectionVariables.set("timestampIAT", iat);

    pm.collectionVariables.set("timestampEXP", exp);

No RepliesBe the first to reply