Last active
August 28, 2023 12:04
-
-
Save spllr/4bf3fadb7f6168f67698 to your computer and use it in GitHub Desktop.
Setting up an authenticated CloudKit server-to-server request
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Demonstrates how to use Apple's CloudKit server-to-server authentication | |
* | |
* Create private key with: `openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem` | |
* Generate the public key to register at the CloudKit dashboard: `openssl ec -in eckey.pem -pubout` | |
* | |
* @see https://developer.apple.com/library/prerelease/ios/documentation/DataManagement/Conceptual/CloutKitWebServicesReference/SettingUpWebServices/SettingUpWebServices.html#//apple_ref/doc/uid/TP40015240-CH24-SW6 | |
* | |
* @author @spllr | |
*/ | |
var crypto = require("crypto"), | |
https = require("https"), | |
fs = require("fs") | |
var CloudKitRequest = function(payload) { | |
this.payload = payload | |
this.requestOptions = { // Used with `https.request` | |
hostname: "api.apple-cloudkit.com", | |
port: 443, | |
path: "/database/1/iCloud.your.container.id/development/public/records/modify", // Update container ID | |
method: "POST", | |
headers: { // We will add more headers in the sign methods | |
"X-Apple-CloudKit-Request-KeyID": "your-cloudkit-request-key-id" | |
} | |
} | |
} | |
CloudKitRequest.prototype.sign = function(privateKey) { | |
var dateString = new Date().toISOString().replace(/\.[0-9]+?Z/, "Z"), // NOTE: No milliseconds | |
hash = crypto.createHash("sha256"), | |
sign = crypto.createSign("RSA-SHA256") | |
// Create the hash of the payload | |
hash.update(this.payload, "utf8") | |
var payloadHash = hash.digest("base64") | |
// Create the signature string to sign | |
var signatureData = [ | |
dateString, | |
payloadHash, | |
this.requestOptions.path | |
].join(":") // [Date]:[Request body]:[Web Service URL] | |
// Construct the signature | |
sign.update(signatureData) | |
var signature = sign.sign(privateKey, "base64") | |
// Update the request headers | |
this.requestOptions.headers["X-Apple-CloudKit-Request-ISO8601Date"] = dateString | |
this.requestOptions.headers["X-Apple-CloudKit-Request-SignatureV1"] = signature | |
return signature // This might be useful to keep around | |
} | |
CloudKitRequest.prototype.send = function(cb) { | |
var request = https.request(this.requestOptions, function(response) { | |
var responseBody = "" | |
response.on("data", function(chunk) { | |
responseBody += chunk.toString("utf8") | |
}) | |
response.on("end", function() { | |
cb(null, JSON.parse(responseBody)) | |
}) | |
}) | |
request.on("error", function(err) { | |
cb(err, null) | |
}) | |
request.end(this.payload) | |
} | |
var privateKey = fs.readFileSync("./cloudkit/eckey.pem"), | |
creationPayload = JSON.stringify({ | |
"operations": [{ | |
"operationType" : "create", | |
"record" : { | |
"recordType" : "Post", | |
"fields" : { | |
"title" : { "value" : "A Post From The Server" } | |
} | |
} | |
}] | |
}) | |
var creationRequest = new CloudKitRequest(creationPayload) | |
creationRequest.sign(privateKey) | |
creationRequest.send(function(err, response) { | |
console.log("Created a new entry with error", err, "and respone", response) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment