5

I've been trying to get the server to server request for quite some time, but unfortunately

I keep getting this error:

2025-11-19T03:19:29Z:"mybody":/database/1/"mycontainer"/development/public/records/createHTTP/1.1

401 Unauthorized Server: AppleHttpServer/a3fb6e96e80a Date: Wed, 19 Nov 2025 03:19:29 GMT Content-Type: application/json; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive X-Responding-Instance: ckdatabasews:2140985586:prod_p50_ckdatabasews_50percent_664bb489df_4bnnl:8080:2544B223:ab717150a32767045aee83acd2d4b446ed35230f Strict-Transport-Security: max-age=31536000; includeSubDomains; x-apple-user-partition: 50 via: xrail:icloud-xrail-group114-ext-587cfdb7b4-rmkzv:8301:25R361:grp114,631194250daa17e24277dea86cf30319:25ca0aea36465bd9182b564819c4da18:defra2 X-Apple-Request-UUID: 818df8d6-f3f1-4325-b8b9-b15da6475fd0 access-control-expose-headers: X-Apple-Request-UUID,X-Responding-Instance,Via X-Apple-Edge-Response-Time: 157 { "uuid" : "818df8d6-f3f1-4325-b8b9-b15da6475fd0", "serverErrorCode" : "AUTHENTICATION_FAILED", "reason" : "Authentication failed" }

My POST looks like this in PHP:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<form action="" method="POST">
    <input type="submit" name="submit">
</form>
</body>
</html>
<?php
if (isset($_POST['submit'])) {
    newOrderCK();
}

    function newOrderCK()
    {
        $keyID = 'mykeyID';
        $path = "https://api.apple-cloudkit.com/database/1/"my container"/development/public/records/create";
        $pa = "/database/1/"mycontainer"/development/public/records/create";
        $fdate = date("Y-m-d\TH:i:sp");

        $privateKey = 'myprivateKey';
        $privatKey = "-----BEGIN EC PRIVATE KEY-----\n$privateKey\n-----END EC PRIVATE KEY-----";
        $body = array(
            "operations" => array("operationType" => "create",
                "record" => array("recordType" => "value",
                    "fields" => array("CD_art" => array("value" => "value")),
                    "recordName" => "value"
                )
            )
        );
        $method = "POST";
        $encoded = json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        $data = $fdate .':'. $encoded .':'. $pa;


        $binary_signature = "";
        openssl_sign($data, $binary_signature, $privatKey, OPENSSL_ALGO_SHA256);
        $valid_signature = base64_encode($binary_signature);

        $headers = array(
            "X-Apple-CloudKit-Request-KeyID: $keyID",
            "X-Apple-Cloudkit-Request-ISO8601Date: $fdate",
            "X-Apple-CloudKit-Request-SignatureV1: $valid_signature"
        );


        $curl = curl_init($path);

        curl_setopt($curl, CURLOPT_URL, $path);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_HEADER, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS,$encoded);


        $response = curl_exec($curl);
        echo $response;


}
?>

Unfortunately, the documentation from Apple about CloudKit Web Services is very poor for me.

Can someone here tell me where my mistake is?

New contributor
valentine is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • 1
    Even if this does not directly help your problem: isn't there any library that could help you to wrote these requests? Crafting arrays like this by hand, generating signatures, writing cURL requests - there are a lot of points where this can go wrong by simple mistakes, while a proper library (tested by lots of people) could make your work so much easier Commented 2 days ago

1 Answer 1

3

First thing is format of private key should be like this:

$privateKey = <<<EOD

-----BEGIN EC PRIVATE KEY-----

<your key here>

-----END PRIVATE KEY-----

EOD;

Second thing wrong string being signed correct one:

$bodyJson        = json_encode(...);
$bodyHashBinary  = hash('sha256', $bodyJson, true);
$bodyHashBase64  = base64_encode($bodyHashBinary);
$messageToSign   = $date . ':' . $bodyHashBase64 . ':' . $urlSubpath;

Third thing is, that you used a wrong date format, try this:

$date = gmdate("Y-m-d\TH:i:s\Z");
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I took the changes into my code and it worked immediately. My URL didn't match yet, but that wasn't the problem. Again: Thank you, thank you!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.