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
Parameter | Description |
---|---|
Client-Id | Client ID retrieved from Indonesia Ministry of Man Power. |
Request-Id | Unique random string generated from client side to protect duplicate request. |
Request-Timestamp | 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 |
Request-Target | JOSS resource-path minus domain. e.g: /api/v2/employers |
Digest | base64 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
- 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=
- 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
Parameter | Description |
---|---|
Client-Id | Will contain Client ID retrieved from Indonesia Ministry of Manpower. |
Request-Id | Will contain unique random string generated from client side to protect duplicate request. |
Request-Timestamp | Will 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 |
Signature | HMACSHA256={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)
}