iCloud Cloudkit CKFetchWebAuthTokenOperation

527 views Asked by At

My iOS app checks the iCloud account status and then requests an iCloud WebToken using the following method:

@objc static func fetchWebAuthToken ( _ apiToken : String, _ callback : @escaping CCallbackFunctionWithBoolAndString )
{
    let fetchAuthorization = CKFetchWebAuthTokenOperation(apiToken: apiToken)

    fetchAuthorization.fetchWebAuthTokenCompletionBlock = { webToken, error in
        guard let webToken = webToken, error == nil else {
            callback ( false, "[SWIFT] fetchWebAuthToken() error. " + (error?.localizedDescription ?? ""));
            return;
        }

        let encodedWebToken = token.addingPercentEncoding (
                withAllowedCharacters: CharacterSet(charactersIn: "+/=").inverted
            ) ?? token

        callback ( true, encodedWebToken );
        return;
    }

    CKContainer.default().privateCloudDatabase.add(fetchAuthorization);
}

Everything works correctly and a properly formatted web token is returned.

I then take that web token and, using Postman, I form the request (with exact values removed):

https://api.apple-cloudkit.com/database/1/iCloud.com.[my container]/development/private/users/caller?ckAPIToken=[development container token]&ckWebAuthToken=[web token]

The response is:

{
  "uuid": "[abc]",
  "serverErrorCode": "ACCESS_DENIED",
  "reason": "private db access disabled for this account"
}

If I request to the public database instead, I get a valid and correct response:

https://api.apple-cloudkit.com/database/1/iCloud.com.[my container]/development/public/users/caller?ckAPIToken=[development container token]&ckWebAuthToken=[web token]

{
  "userRecordName": "_[user id]",
  "nameComponents": {
    "givenName": "[First Name]",
    "familyName": "[Surname]"
  }
}

So, there's two questions here.

1) If I'm requesting a web token in code for the private database, why is it only allowing me to interact with the public database? It feels like it's providing a web token that's only valid for the public database, regardless of the database I add the action to.

2) What are the security implications of validating a user against the public database like this? The token should expire in 30 minutes, which helps from that front.

To prove that a web token works against the private database, I updated "Sign In Callback" int the Dashboard, copied the resulting ckWebAuthToken and was able to get access to the private database through PostMan, so there's no issue from that end. It seems as if the issue lies entirely with the web token returned from the iOS code.

1

There are 1 answers

1
Clifton Labrum On

My guess is that it's because the Users record type in CloudKit is always stored in the public database in every CloudKit container.

There shouldn't be any security risks with this validation against the public databse. In my opinion, Apple shouldn't have ever named it "public" because it's not really public. It's just generally available to the users of the app, but only the application and authenticated users can transact with the database as defined by the developer. It's not available to the public.

I'm going to assume you are doing something fancy with this authentication flow, since authenticating a user on an iOS device doesn't require passing around the ckWebAuthToken. :)