OPA API Authorization (1.0)

Introduction

OPA api uses HMAC(Hash-based message authentication code) authentication and merchant needs to call these apis using HMAC authentication. HMAC is a message authentication code that uses a key in conjunction with a hash function. For the implementation of HMAC based authentication, entire request is taken into account for building authentication object. The client(merchant) creates HMAC authentication code based on the below documentation and sets it in the HTTP "Authorization" request header. On receiving the request, the server(OPA) recreates this authentication code and request is executed successfully if both codes are matched otherwise unauthorized response will be returned.

HMAC Authentication

Everything related to HMAC authentication flow

Parameters considered for authentication object

Value Description Example
API Key API key generated APIKeyGenerated
API Key secret API Key secret generated APIKeySecretGenerated
Request URI Service request URL path /v2/codes
Request method HTTP method POST
Epoch Current epoch seconds,
Note : Validated whether less than 2mins on server side
1579843452
Nonce Random generated string
Note : String of any length, but recommended to use of length 8
acd028
Request body Value of request body passed in request

{"sampleRequestBodyKey1":"sampleRequestBodyValue1","sampleRequestBodyKey2":"sampleRequestBodyValue2"}

Request content type Content type passed in request header application/json;charset=UTF-8;
hash (MD5(Request body, Request content type)) Hash of request body and content type. This will be the output from step 1 of genererating HMAC auth header added below 1j0FnY4flNp5CtIKa7x9MQ==

Sample HMAC Auth header generated from above example values is as followed

hmac OPA-Auth:APIKeyGenerated:NW1jKIMnzR7tEhMWtcJcaef+nFVBt7jjAGcVuxHhchc=:acd028:1579843452:1j0FnY4flNp5CtIKa7x9MQ==

Steps to genererate HMAC auth header

Step 1: Hash the body and content-type with MD5 algorithm

Code sample for hashing body and content type

private String requestBody;
private String contentType;

MessageDigest md = MessageDigest.getInstance("MD5");
md.update(contentType.getBytes(StandardCharsets.UTF_8));
md.update(requestBody.getBytes(StandardCharsets.UTF_8));
String hash = new String(
          Base64.getEncoder().encode(md.digest()),
          StandardCharsets.UTF_8);

Note : If there is no request body, for instance, the HTTP GET method case, no need of generating MD5. Instead hash value is set as "empty".

Step 2 : Build string to be hashed with HMAC-SHA256.

We will consider following parameters for signing the request

private String requestUrl; //Only the request URI Example: "/v2/codes/payments/dynamic-qr-test-00002"
private String httpMethod;
private String nonce; //Random string
private String epoch;
private String contentType;
private String hash; //Output of step 1
private static final String DELIMITER = "\n";

byte[] hmacData = new StringBuilder()
                    .append(requestUrl)
                    .append(DELIMITER)
                    .append(httpMethod)
                    .append(DELIMITER)
                    .append(nonce)
                    .append(DELIMITER)
                    .append(epoch)
                    .append(DELIMITER)
                    .append(contentType)
                    .append(DELIMITER)
                    .append(hash != null ? hash : "")
                    .toString()
                    .getBytes(StandardCharsets.UTF_8);

Note : If there is no request body, for instance, the HTTP GET method case, content-type and hash will be set as "empty".

Step 3 : Generating HMAC object Generate HMAC based on output from Step 2 and Key secret of API key secret pair.

public final String toBase64HmacString() {
    private String apiKeySecret;
    private byte[] dataToSign; //Output from step 2
    try {
      SecretKeySpec signingKey = new SecretKeySpec(apiKeySecret.getBytes(StandardCharsets.UTF_8),
          "HmacSHA256");
      Mac sha256HMAC = Mac.getInstance("HmacSHA256");
      sha256HMAC.init(signingKey);
      byte[] rawHmac = sha256HMAC.doFinal(dataToSign);
      return java.util.Base64.getEncoder().encodeToString(rawHmac);
    } catch (GeneralSecurityException e) {
      LOGGER.error("Unexpected error while creating hash: " + e.getMessage());
      throw new IllegalArgumentException(e);
    }
  }

Step 4 : Build header object with the parameters of syntax. //We will be using password as the key to sign the hmacData

String authHeader = "hmac OPA-Auth:" + api-key +
":" + macData + ":" + nonce + ":" + epoch + ":" + hash;

Note: macData is output from step 3 and
For nonce and epoch, same value that has been used in step 2 should be passed.

If there is no body of the request such as in the case of HTTP GET method, the hash value will be "empty", so the header object will be as follows.

String authHeader = "hmac OPA-Auth:" + api-key +
":" + macData + ":" + nonce + ":" + epoch + ":empty";

The value of authHeader is passed in HttpHeader.AUTHORIZATION. With the authHeader will decode back the data added and with the HTTP request object and based on data available for api-key in the system, we will recreate the SHA256("key", requestParams) which gives macData. This macData is verified against the value passed in the header.

Changelog

Date of Change Date of Release Type of Change Section Updates
2023.03.24 Released Documentation Fix Parameters considered for authentication object Fixed the description of Request URI.