Knox Webhook Notification for Knox Device Management Service
Last updated October 15th, 2024
The following tutorials will help you get started with using the Knox Webhook Notification API for Knox Device Management Service.
Currently, the Knox Webhook Notification API supports the following Knox Device Management Service events:
Event |
Action |
Description |
KDMS_DEVICE_UPLOAD |
Upload devices |
Upload a list of devices through Knox Device Management Service. |
KDMS_DEVICE_DELETE |
Delete devices |
Delete a list of devices through Knox Device Management Service. |
Prerequisites
Authentication
You need an authentication token to use Knox cloud services APIs. For more information, see how to get an OAuth 2.0 access token. Make sure you include the kdms.devices
scope when you create your OAuth 2.0 app. If you want to provide more granular access permissions, you can grant permission specifically for upload or delete operations with kdms.devices:upload
or kdms.devices:delete
scopes.
Certificate
The Samsung Knox validation certificate is required to validate the response you’ll receive from Knox Webhook Notification. Download the certificate using the GET /downloadCertificate operation.
Alternatively, you can download the certificate by clicking the following button.
Download certificate
Use the Knox Webhook Notification API
Upload devices
This tutorial demonstrates how you can register the event from uploading 10,000 devices to Knox Device Management Service, and register the event with Knox Webhook Notification to receive change notifications when the asynchronous upload operation is complete.
Step 1: Subscribe an event
Subscribe a particular event to Knox Webhook Notification through the Create Subscription operation — POST /kwn/v1/subscriptions
. You’ll need to:
-
Provide a subscription URL — known as a “callback” — that you’ll register to receive asynchronous API operation results once the event is complete.
-
Register the KDMS_DEVICE_UPLOAD
event to asynchronously receive a result notification once the device upload operation is complete.
Step 2: Upload devices
Upload devices with the Knox Device Management Service API.
Step 3: Handle response message
After the Knox Device Management Service finishes executing the asynchronous operation to upload 10,000 devices, you’ll receive a message in the body of the subscribed URL call as the response payload. You can see the payload schema under Callbacks in the Create subscription operation on the API reference.
Here is an example payload:
"payload": {
"operationId": "221b7088-4baa-4b3a-8682-868cbead2434",
"successCount": 1,
"failureCount": 1,
"successfulDevices": [
{
"imei": "345678901234567",
"serialNumber": null,
}
],
"failedDevices": [
{
"imeiOrSerial": "347654356789012",
"errorCode": "DUPLICATED_DEVICES",
"errorMessage": "This device is already uploaded to your account."
}
]
}
One URL for multiple subscriptions
You can also configure one URL across multiple subscriptions. However, you must meet the following conditions:
Verify the response
To verify the Knox Webhook Notification callback response:
- Get the String value of
HttpRequestPayload
byte[] inputStreamBytes = StreamUtils.copyToByteArray(request.getInputStream());
Map jsonBody = objectMapper.readValue(inputStreamBytes, HashMap.class);
String requestBody = objectMapper.writeValueAsString(jsonBody);
- Parse the encoded JoseHeader and signature from X-WSM-SIGNATURE
String[] jwsParts = jwsSignature.split("\\.");
if (jwsParts.length != 3) {
// Invalid JWS signature
return new ResponseEntity<>("Invalid JWS signature: X-WSM-SIGNATURE=" + jwsSignature, HttpStatus.BAD_REQUEST);
}
String encodedHeaders = jwsParts[0];
String encodedRequestBody = Base64.getUrlEncoder().encodeToString(requestBody.getBytes(StandardCharsets.UTF_8));
String signature = jwsParts[2];
- Prepare the data to verify:
DataToVerify = encodedJoseHeader.Base64UrlEncode(HttpRequestPayload)
String dataToVerify = encodedHeaders + "." + encodedRequestBody;
- Decode the signature with
Base64Url
decoder and verify the data above by using SHA256withRSA
byte[] signatureByte = Base64.getUrlDecoder().decode(signature);
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
// Pre-download Samsung certificate and store it locally with your application. Load public key from locally stored cert file.
rsaSignature.initVerify(publicKey);
rsaSignature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));
boolean verified = rsaSignature.verify(signatureByte);
if (verified) {
// Process the result
// ADD BUSINESS LOGIC, return OK
return new ResponseEntity<> ("Result is processed successfully", HttpStatus.OK);
} else {
return new ResponseEntity<>("Signature validation failed: X-WSM-SIGNATURE=" + jwsSignature, HttpStatus.INTERNAL_SERVER_ERROR);
}
Full code sample
public ResponseEntity receiveResult(HttpServletRequest request,
@Valid @NotBlank @RequestHeader(value="X-WSM-SIGNATURE") String jwsSignature,
@Valid @NotBlank @RequestHeader(value="X-WSM-TRACEID") String transId) {
try {
// 1. Get the request body
byte[] inputStreamBytes = StreamUtils.copyToByteArray(request.getInputStream());
Map jsonBody = objectMapper.readValue(inputStreamBytes, HashMap.class);
String requestBody = objectMapper.writeValueAsString(jsonBody);
// 2. Verify the data and signature
String[] jwsParts = jwsSignature.split("\\.");
if (jwsParts.length != 3) {
// Invalid JWS signature
return new ResponseEntity<>("Invalid JWS signature: X-WSM-SIGNATURE=" + jwsSignature, HttpStatus.BAD_REQUEST);
}
String encodedHeaders = jwsParts[0];
String encodedRequestBody = Base64.getUrlEncoder().encodeToString(requestBody.getBytes(StandardCharsets.UTF_8));
String signature = jwsParts[2];
String dataToVerify = encodedHeaders + "." + encodedRequestBody;
byte[] signatureByte = Base64.getUrlDecoder().decode(signature);
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
// Pre-download Samsung certificate and store it locally with your application. Load public key from locally stored cert file.
rsaSignature.initVerify(publicKey);
rsaSignature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));
boolean verified = rsaSignature.verify(signatureByte);
if (verified) {
// 3. Process the result
// ADD YOUR BUSINESS LOGIC, return OK
return new ResponseEntity<> ("Result is processed successfully", HttpStatus.OK);
} else {
return new ResponseEntity<>("Signature validation failed: X-WSM-SIGNATURE=" + jwsSignature, HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (Exception e) {
log.error("receiveResult: failed to verify or parse request body", e);
return new ResponseEntity<>("Internal error: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}