API Reference

This page will help you to authenticate to API

JOSS API requests must have authentication, or they fail with a 401 Unauthorized error response. Signature is a security parameter that needs to be generated on your Backend to verify the request authenticity. Before generating Signature, you need to prepare all the component required.

Generate Signature

Generate Digest

Hash of your JSON body request and as one of required component to generate Signature. To generate the digest, calculate SHA256 base64 hash from your JSON Body.

📘

For API that uses GET and DELETE method, you don't need to generate a Digest.

Set Request Target

Value of field Request-Target is JOSS resource-path minus domain. For instance, resource-path access by client: <https://sandbox.joss.kemnaker.go.id/api/v2/employers> , therefore the Request-Target value is /api/v2/employers

📘

JOSS also generate Signature when sending HTTP Notification. You need to verify the authenticity to ensure the Notification Request coming from JOSS or not. As for the Request-Target, JOSS will use your resource-path of your Notification URL.

For example, you set the Notification URL <https://yourdomain.com/api/employer/notifications>, therefore the Request-Target is /api/employer/notifications

Signature Components

ParameterDescription
Client-IdClient ID retrieved from Indonesia Ministry of Man Power.
Request-IdUnique random string generated from client side to protect duplicate request.
Request-TimestampTimestamp request on UTC time in ISO8601 UTC+0 format. It means to proceed with the transaction on UTC+7 (WIB), the client needs to subtract time with 7. Ex: to proceed with the transaction on September 22th 2022 at 08:51:00 WIB, the timestamp should be 2022-09-22T01:51:00Z
Request-TargetJOSS resource-path minus domain. e.g: /api/v2/employers
Digestbase64 encoded string from raw binary hashed JSON body using sha256 algorithm or skip if the request does not have a body.

Generate Hash for Signature Components

  1. Arrange the signature components to one component and its value by adding | character as separator. Don't add | at end of the string. Sample of the raw format:
20bd0244-7e6f-40c8-91a7-6a9c5b787f76|c6ad317b-f21e-43ac-9184-fff4ce087e3c|2022-05-10T22:10:37Z|/api/v2/employers|RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=
  1. Calculate HMAC-SHA256 from all the components above using the secret key from Indonesia Ministry of Man Power. Sample:
c91600a51c17cee10aa97c4361e61161ed3a1b88d0dcf9c05c0c25757a430918

❗️

The generated signature only valid for 5 minutes.

Authenticate Request

To Authenticate if the request is authorized and valid, the API must be able to verify whether the request is coming from a valid client. So what an API request has to do is add the parameters Client-Id, Request-Id, Request-Timestamp, and Signature to the request header.

HTTP Header Parameters

ParameterDescription
Client-IdWill contain Client ID retrieved from Indonesia Ministry of Manpower.
Request-IdWill contain unique random string generated from client side to protect duplicate request.
Request-TimestampWill contain timestamp request on UTC time in ISO8601 UTC+0 format. It means to proceed with the transaction on UTC+7 (WIB), the client needs to subtract time with 7. Ex: to proceed with the transaction on September 22th 2022 at 08:51:00 WIB, the timestamp should be 2022-09-22T01:51:00Z
SignatureHMACSHA256={signature} will contain the security parameter that needs to be generated on client Backend and placed to the header request to ensure that the request is coming from a valid client. The signature will be explained above.

Code Examples

$clientId = 'yourClientId';
$clientSecret = 'yourClientSecret';
$requestId = 'yourRequestId';
$requestTimestamp = '2021-05-10T22:10:37Z';
$requestTarget = '/request-path';
$requestBody = '{"name": "John Doe"}';

// Generate Digest
$digest = base64_encode(hash('sha256', $requestBody, true));

// Signature Component
$components = [
    $clientId,
    $requestId,
    $requestTimestamp,
    $requestTarget,
    $digest
];
$componentSignature = implode('|', $components);

// Calculate HMAC-SHA256 from all the components above
$signature = hash_hmac('sha256', $componentSignature, $clientSecret);
echo 'Signature: '.$signature;
echo "\r\n\n";

// Sample of Usage
$headerSignature =  'Client-Id:'.$clientId ."\n". 
                    'Request-Id:'.$requestId . "\n".
                    'Request-Timestamp:'.$requestTimestamp ."\n".
                    'Signature:HMACSHA256='.$signature;
echo "your header request look like: \n".$headerSignature;
echo "\r\n\n";
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class Signature {

    public static final String CLIENT_ID = "Client-Id";
    public static final String REQUEST_ID = "Request-Id";
    public static final String REQUEST_TIMESTAMP = "Request-Timestamp";
    public static final String REQUEST_TARGET = "Request-Target";
    public static final String DIGEST = "Digest";

    // Generate Digest
    public static String generateDigest(String myBodyJson) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(myBodyJson.getBytes(StandardCharsets.UTF_8));
        byte[] digest = md.digest();
        return Base64.getEncoder().encodeToString(digest);
    }

    private static String generateSignature(String clientId, String requestId, String requestTimestamp, String requestTarget, String digest, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
        // Signature Component
        String componentSignature = String.join("|", clientId, requestId, requestTimestamp, requestTarget, digest);

        // Calculate HMAC-SHA256 from all the components above
        byte[] decodedKey = secret.getBytes();
        SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        hmacSha256.init(originalKey);
        hmacSha256.update(componentSignature.toString().getBytes());
        byte[] HmacSha256DigestBytes = hmacSha256.doFinal();

        return bytesToHex(HmacSha256DigestBytes);
    }

    private static String bytesToHex(byte[] bytes) {   
        final char[] hexArray = "0123456789abcdef".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0, v; j < bytes.length; j++) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    // Sample of Usage
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        String jsonBody = "{\"name\": \"John Doe\"}";

        String digest = generateDigest(jsonBody);

        // Generate Signature
        String headerSignature = generateSignature(
                "yourClientId",
                "yourRequestId",
                "2021-05-10T22:10:37Z",
                "/request-path",
                digest,
                "yourClientSecret");

        System.out.println("----- Header Signature -----");
        System.out.println(headerSignature);
    }
}
import hashlib
import hmac
import base64

# Generate Digest
def generateDigest(jsonBody):  
    return base64.b64encode(hashlib.sha256(jsonBody.encode('utf-8')).digest()).decode("utf-8")

def generateSignature(clientId, requestId, requestTimestamp, requestTarget, digest, secret):
    # Signature Components
    componentSignature = '|'.join([clientId, requestId, requestTimestamp, requestTarget, digest])

    message = bytes(componentSignature, 'utf-8')
    secret = bytes(secret, 'utf-8')

    # Calculate HMAC-SHA256 from all the components above
    signature = hmac.new(secret, message, digestmod=hashlib.sha256).hexdigest()

    return signature

# Sample of usage

# Generate Digest from JSON Body
jsonBody = '{"name": "John Doe"}'
digest = generateDigest(jsonBody)

# Generate Signature
headerSignature = generateSignature(
        "yourClientId",
        "yourRequestId",
        "2021-05-10T22:10:37Z",
        "/request-path",
        digest,
        "yourClientSecret")
print("----- Header Signature -----")
print(headerSignature)
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "fmt"
    "strings"
)

const CLIENT_ID = "Client-Id"
const REQUEST_ID = "Request-Id"
const REQUEST_TIMESTAMP = "Request-Timestamp"
const REQUEST_TARGET = "Request-Target"
const DIGEST = "Digest"

// Generate Digest
func generateDigest(jsonBody string) string {
    converted := []byte(jsonBody)
    hasher := sha256.New()
    hasher.Write(converted)
    return (base64.StdEncoding.EncodeToString(hasher.Sum(nil)))     
}

func generateSignature(clientId string, requestId string, requestTimestamp string, requestTarget string, digest string, secret string) string {
    // Signature Components
    components := []string{clientId, requestId, requestTimestamp, requestTarget, digest}
    componentSignature := strings.Join(components, "|")

    // Calculate HMAC-SHA256 from all the components above
    key := []byte(secret)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(componentSignature))

    return hex.EncodeToString(h.Sum(nil))
}

// Sample of Usage
func main() {

    // Generate Digest from JSON Body
    var jsonBody = "{\"name\": \"John Doe\"}"
    digest := generateDigest(jsonBody)

    // Generate Signature
    headerSignature := generateSignature(
        "yourClientId",
        "yourRequestId",
        "2021-05-10T22:10:37Z",
        "/request-path",
        digest,
        "yourClientSecret")

    fmt.Println("----- Header Signature -----")    
    fmt.Println(headerSignature)
}