CloudKit Server-to-Server auth: Продолжайте получать 401 Аутентификация не удалась

Недавно я изучал CloudKit и связанные с ним фреймворки. У меня работает связь с моим приложением, а также с моим веб-сайтом с помощью CloudKitJS. Где я борюсь, так это в обмене данными между серверами (который мне понадобится для экспорта данных из общедоступной базы данных в csv.

Я попробовал пакет Python requests-cloudkit, который предлагали другие. Я создал токен Server-to-Server и скопировал только ключ между строками START и END после создания файла eckey.pem. Затем я получил этот код:

from requests_cloudkit import CloudKitAuth
from restmapper import restmapper
import json
KEY_ID = '[my key ID from CK Dashboard]'
SECRET_FILE_KEY = 'eckey.pem'
AUTH = CloudKitAuth(KEY_ID, SECRET_FILE_KEY)
PARAMS = {
        'query':{
                'recordType': '[my record type]'
        },
}
CloudKit = restmapper.RestMapper("https://api.apple-cloudkit.com/database/1/[my container]/development/")
cloudkit = CloudKit(auth=AUTH)
response = cloudkit.POST.public.records.query(json.dumps(PARAMS))

Затем я получаю ответ 401 Authentication failed. Я застрял на этом в течение нескольких дней, поэтому я был бы признателен за любую помощь или совет. ????


person Matyáš Boháček    schedule 04.08.2020    source источник


Ответы (1)


Создание межсерверного ключа — важный первый шаг, но чтобы после этого делать HTTP-запросы, вы должны подписывать каждый запрос.

Найдите раздел Authenticate Web Service Requests в нижней части эта страница документации.

Это немного запутанно, но вы должны тщательно создавать подписанные заголовки, чтобы включать их в каждый запрос, который вы делаете. Я не знаком с тем, как это сделать в Python, но вот как я это делаю в NodeJS, что может помочь:

//Get the timestamp in a very specific format
let date = moment().utc().format('YYYY-MM-DD[T]HH:mm:ss[Z]')

//Construct the subpath
let endpoint = '/records/lookup'
let path = '/database/1/iCloud.*****/development/public'
let subpath = path+endpoint

//Get the key file
let privateKeyFile = fs.readFileSync('../../'+SECRET_FILE_KEY, 'utf8')

//Make a string out of your JSON query
let query = {
  recordType: '[my record type]'
}
let requestBody = JSON.stringify(query)

//Hash the query
let bodyHash = crypto.createHash('sha256').update(requestBody, 'utf8').digest('base64')

//Assemble the components you just generated in a special format
//[Current date]:[Request body]:[Web service URL subpath]
let message = date+':'+bodyHash+':'+subpath
  
//Sign it
let signature = crypto.createSign('RSA-SHA256').update(message).sign(privateKeyFile, 'base64')

//Assemble your headers and include them in your HTTP request
let headers = {
  'X-Apple-CloudKit-Request-KeyID': KEY_ID,
  'X-Apple-CloudKit-Request-ISO8601Date': date,
  'X-Apple-CloudKit-Request-SignatureV1': signature
}

Поначалу это немного сложно, но я просто поместил все это в функцию, которую повторно использую всякий раз, когда мне нужно сделать запрос.

Документация Apple в значительной степени заброшена, и в наши дни трудно найти хорошую помощь по веб-сервисам CloudKit.

person Clifton Labrum    schedule 04.08.2020