This guide will outline all necessary steps to form a proper JWS. This example uses the ACH v2 API, but please note that this approach to forming the JWS is the same across all of the latest versioned US Payments APIs.

 

Step 1 : JWS Signature 

All payment requests will need to be signed with a JWS signature. This step makes certain that no one tampered with the payload after the signature was generated.

SVB provides code samples in Python below to calculate the JWS signature. Please also see our Java example and GO example

Python screenshot

Python code sample 

Below Python and json files can be grouped and used to calculate the JWS signature token. When below files are grouped within a project, following command can be executed to retrieve the JWS signature:

  • -f /detached-jwt-utility/payload.json -s clientSecret
    • where the first parameter is the absolute path to the request payload (i.e. payload.json in this example) and the second parameter is the secret string received form the API support team (clientSecret).

detachedJWT.py

from jwcrypto import jwk, jwt
from InputOpt import InputOption
import uuid

def main():
    input = InputOption()
    InputOption.input()
    print(DetachedJWT.detached_jwt(input.get_filePath(), input.get_secret()))

class DetachedJWT:

    @staticmethod
    def detached_jwt(filePath, secret):
        key = jwk.JWK.from_password(secret)

        # Reading the input payload to be signed
        file = open(filePath, "r")
        payload = file.read()

        # Generating headers with necessary claims
        headers = {"alg": "HS256", "kid": str(uuid.uuid4()), "typ": "JOSE"}
        # Creating the jwstoken and sign
        jwstoken = jwt.JWT(header=headers, claims=payload)
        jwstoken.make_signed_token(key)
        sig = jwstoken.serialize()
        # Detaching JWT
        splittedJWS = sig.split(".")
        splittedJWS[1] = ""
        print("printing the jws token")
        return '.'.join(splittedJWS)

if __name__ == "__main__":
    main()

InputOpt.py

import getopt
import sys

class InputOption:

    @staticmethod
    def usage():
        print("ToCreateDetachJET: python detachedJWT.py -f <filePath.json> -s <Secret key>")

    @staticmethod
    def input():
        try:
            (opts, args) = getopt.getopt(sys.argv[1:], 'f:s:h',
                                         ['filePath=', 'secret=', 'help'])
        except getopt.GetoptError as err:
            print(err)
            sys.exit(2)

        if len(opts) != 0:
            for (o, a) in opts:
                if o in ('-h', '--help'):
                    InputOption.usage()
                    sys.exit()
                elif o in ('-f', '--filePath'):
                    global filePath
                    filePath = str(a)
                    InputOption.set_filePath(filePath)
                elif o in ('-s', '--secret'):
                    global secret
                    secret = str(a)
                    InputOption.set_secret(secret)
                else:
                    InputOption.usage()
                    sys.exit(2)
        else:
            InputOption.usage()
            sys.exit(2)

    def set_filePath(filePath):
        filePath = filePath

    def get_filePath(self):
        return filePath

    def set_secret(secret):
        secret = secret

    def get_secret(self):
        return secret

payload.json

{
    "buy": {
        "currency_code": "EUR",
        "value": "100.00"
    },
    "sell_currency": "USD",
    "tenor": "TOD"
}

Step 2 : oAuth 2.0 Access Token

ACH payment requests leverage two input parameters for security: JWS signature and oAuth 2.0 access token.

An API request to /v1/security/oauth/token is submitted to retrieve an access token.

/token endpoint uses Basic Authorization with a username/password pair which represent the credentials received by the API Support/Implementations team.

Postman auth screenshotPostman bearer token screenshot

 

Step 3 : Submitting an ACH payment request

API requests will use an authorization type: Bearer token : access token, where the access token represents the token returned from the /token endpoint in Step 2.

Postman ACH auth

JWS signature token from Step 1 will be included as a header parameter:

Postman ACH headersPostman ACH body

 

Java JWS Code Sample

package com.svb.api.utility;

import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.keys.HmacKey;

import java.util.UUID;

public class DetachedJwt {

    /**
     * This method uses HS256 algorithm for enryption to create a detached JWS
     * @param payload: the body of your request.
     * @param secret: this is the client secret which is shared after onboarding, also used as encyption key
     * @return: This method returns a detached JWS.
     * @throws Exception
     */
    public static String generateDetachedJwt(String payload, String secret) throws Exception {
        String detached_jwt = "";
        try {
            JsonWebSignature jws = new JsonWebSignature();

            // The body of your request.
            jws.setPayload(payload);

            // Setting random value for Kid
            jws.setKeyIdHeaderValue(UUID.randomUUID().toString());

            // The type for the JWT
            jws.setHeader("typ", "JOSE");

            // This is your client secret used to encrypt the payload
            jws.setKey(new HmacKey(secret.getBytes("UTF-8")));

            // Setting algorithm to HS256
            jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
            jws.setDoKeyValidation(false);

            // Get signed JWS
            String token = jws.getCompactSerialization();

            // Detaching the body for the JWS
            String parts = token.substring(0, token.indexOf("."));
            int posA = token.lastIndexOf(".");
            String part2 = token.substring(posA + 1);
            detached_jwt = (parts.concat("..")).concat(part2);

        } catch (Exception e) {
            throw new Exception("Unable to generate detached jwt at this time.");
        }

        return detached_jwt;
    }
}

GO JWS Code Sample

package main

import (
    jose "github.com/dvsekhvalnov/jose2go"
    "github.com/google/uuid"
    "strings"
)

func generateDetachedJwt(payload string, secret []byte) (string, error) {
    jwt, err := generateJwt(payload, secret)
    return detachJwt(jwt), err
}

func generateJwt(payload string, secret []byte) (string, error) {
    kid := jose.Header("kid", uuid.NewString())
    typ := jose.Header("typ", "JOSE")
    return jose.Sign(payload, jose.HS256, secret, kid, typ)
}

func detachJwt(jwt string) string {
    splitted := strings.Split(jwt, ".")
    jwtPart1 := splitted[0]
    jwtPart2 := splitted[len(splitted)-1]
    return jwtPart1 + ".." + jwtPart2
}