I'm trying to connect to ck web services using Vapor.
I keep getting code 401 (authentication failed). I read and reread the docs(https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/SettingUpWebServices.html#//apple_ref/doc/uid/TP40015240-CH24-SW1) a hundred times but still no luck
Here is my code:
let body = [
"records":
[
["recordName": "email@gmail.com"]
]
]
let bodyData = try? JSONSerialization.data(withJSONObject: body)
let requestBody = bodyData!
let body64 = requestBody.base64EncodedString()
let calendar = Calendar(identifier: .gregorian)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
dateFormatter.timeZone = calendar.timeZone
let date = dateFormatter.string(from: Date())
let webServiceURLPath = "/database/1/MYCONTAINERID/development/public/records/lookup"
let message = date + ":" + "\(body64)" + ":" + webServiceURLPath
let privateKeyPem =
"""
-----BEGIN EC PRIVATE KEY-----
MY PRIVATE HIDDEN PRIVATE KEY
-----END EC PRIVATE KEY-----
"""
let privateKey = try? P256.Signing.PrivateKey(pemRepresentation: privateKeyPem)
let sign = try? privateKey?.signature(for: SHA256.hash(data: message.data(using: .utf8)!))
let signatureBase64 = sign!.derRepresentation.base64EncodedString()
let keyID = "MYKEYID"
let url = URI(string: "https://api.apple-cloudkit.com/database/1/MYCONTAINERID/development/public/records/lookup")
let headers = HTTPHeaders([
("X-Apple-CloudKit-Request-KeyID", keyID),
("X-Apple-CloudKit-Request-ISO8601Date", date),
("X-Apple-CloudKit-Request-SignatureV1", signatureBase64)
])
let response = try app.client.post(url, headers: headers) { request in
try request.content.encode(body)
}
response.flatMapThrowing({ response in
print(response)
})
Here is one way to sign a CloudKit web services request
// Swift example for Apple CloudKit server-to-server token authentication
// See: https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/SettingUpWebServices.html#//apple_ref/doc/uid/TP40015240-CH24-SW6
import Foundation
import CryptoKit
import Network
// Set up your body JSON
let body = ["": ""]
let bodyData = try! JSONSerialization.data(withJSONObject: body)
// hash then base64-encode the body
let bodyHash = SHA256.hash(data: bodyData)
let body64 = Data(bodyHash).base64EncodedString()
// set up ISO8601 date string, at UTC
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let date = dateFormatter.string(from: Date())
// endpoint path you want to query
let path = "/database/1/iCloud.SceneMapper/development/public/zones/list"
// create the concatenated date, encoded body and subpath
let message = date + ":" + body64 + ":" + path
// Your private key (as generated following the Apple docs above)
let keyPem =
"""
-----BEGIN EC PRIVATE KEY-----
YOUR KEY HERE
-----END EC PRIVATE KEY-----
"""
// Set up the key and get ECDSA signature
let privateKey = try? P256.Signing.PrivateKey(pemRepresentation: keyPem)
let sign = try? privateKey?.signature(for: SHA256.hash(data: message.data(using: .utf8)!))
//let sign = try? privateKey?.signature(for: message.data(using: .utf8)!)
let signatureBase64 = sign!.derRepresentation.base64EncodedString()
// Your server-to-server key from the CloudKit dashboard
let keyID = "your_key_here"
// Set up the full URI
let url = URL(string: "https://api.apple-cloudkit.com" + path)!
var request = URLRequest(url: url)
// Set CloudKit-required headers
request.setValue(keyID, forHTTPHeaderField: "X-Apple-CloudKit-Request-KeyID")
request.setValue(date, forHTTPHeaderField: "X-Apple-CloudKit-Request-ISO8601Date")
request.setValue(signatureBase64, forHTTPHeaderField: "X-Apple-CloudKit-Request-SignatureV1")
// Request method
request.httpMethod = "POST"
// Our original body data for the request
request.httpBody = bodyData
// Send the request
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONSerialization.jsonObject(with: data
// Yada yada, do stuff with response