I tried to send it on the nodejs server I built. No success received 200
My work steps are as follows:
The app executes “DCAppAttestService.shared.attestKey” to get receiptData from the acquired attestation.
The app sends "receiptData.base64EncodedString()" to the server (code-1)
Nodejs code (code-2)
Because the app has been uploaded to TestFlight, I set the server IP to "data.appattest.apple.com"
Is there something wrong with my steps?
code-1
public func attestData(receipt:Data) {
if DCDevice.current.isSupported {
let sesh = URLSession(configuration: .default)
var req = URLRequest(url: URL(string: "http://10.254.239.27:3000/attestationData")!)
print(req)
req.addValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpMethod = "POST"
let data = try! JSONSerialization.data(withJSONObject: ["receipt": receipt.base64EncodedString()], options: [])
req.httpBody = data
let task = sesh.dataTask(with: req, completionHandler: { (data, response, error) in
if let data = data, let jsonString = String(data: data, encoding: .utf8) {
print(jsonString)
}
})
task.resume()
} else {
print("Platform is not supported. Make sure you aren't running in an emulator.")
//self.stopActivity()
}
}
code-2
versionRouter.post('/attestationData', function(req, response) {
console.log("\n\n\n\n\n");
console.log("receiptApi");
var receiptBase64 = req.body.receipt;
if (!receiptBase64) {
return response.status(400).send({ error: 'Missing receipt data' });
}
let binaryReceipt;
if (typeof receiptBase64 === 'string') {
const cleaned = receiptBase64.trim();
binaryReceipt = Buffer.from(cleaned, 'base64');
}
if (Buffer.isBuffer(binaryReceipt)) {
//binaryReceipt = receiptBase64;
console.log("receipt is base64 或 Buffer: "+ Buffer.isBuffer(binaryReceipt));
} else {
console.error('⚠️ receipt is not base64 or Buffer');
response.status(400).send("Receipt format error");
return;
}
var jwToken = jwt.sign({}, cert, { algorithm: 'ES256',header: { typ: undefined }, issuer: teamId, keyid: keyId});
var post_options = {
host: 'data.appattest.apple.com',
port: '443',
path: '/v1/attestationData',
method: 'POST',
headers: {
'Authorization': jwToken,
'Content-Type': 'application/octet-stream',
'Content-Length': binaryReceipt.length
}
};
var post_req = https.request(post_options, function(res) {
res.setEncoding('utf8');
console.log("📨 Apple Response Header:", res.headers);
console.log("📨 Apple StatusCode:", res.statusCode);
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function() {
console.log(data);
response.send({"status": res.statusCode, data: data});
});
});
post_req.on('error', function(e) {
console.error('error:', e);
response.status(500).send({ error: e.message });
});
post_req.write(binaryReceipt);
post_req.end();
});
App Attest
RSS for tagValidate the integrity of your app before your server provides access to sensitive data.
Posts under App Attest tag
25 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello, Preparing to use the app attest service documentation suggests we call attestKey(_:clientDataHash:completionHandler:) fewer than 100 requests per second.
I had a question - Our app might theoretically hit this limit on launch. Would Apple be able to relax these limits or is this a hard limit?
Hi everyone,
We are using the App Attest API to securely transition users to our new system. As part of this, we store the Key ID of the attestation key for each user to verify their identity later.
However, we’ve noticed that some users are encountering the error “DCErrorInvalidKey 3” when calling generateAssertion. Importantly, the key was previously successfully attested, and generateAssertion has worked before for these users.
Our questions:
Could this error be caused by an app or iOS update?
Is it problematic to link an attestation key's Key ID directly to a user, or are there scenarios where the key might change or become invalid?
If there’s a way to mitigate this issue or recover affected users, what best practices would you recommend?
Any help or shared experiences would be greatly appreciated! Thanks in advance.
Hi,
When calling generateAssertion on DCAppAttestService.shared, it gives invalidKey error when there was an update for an offloaded app.
The offloading and reinstall always works fine if it is the same version on app store that was offloaded from device,
but if there is an update and the app tries to reuse the keyID from previous installation for generateAssertion, attestation service rejects the key with error code 3 (invalid key) for a significant portion of our user.
In our internal testing it failed for more than a third of the update attempts.
STEPS TO REPRODUCE:
install v1 from app store
generate key using DCAppAttestService.shared.generateKey
Attest this key using DCAppAttestService.shared.attestKey
Send the attestation objection to our server and verify with apple servers
Generate assertions for network calls to backend using DCAppAttestService.shared.generateAssertion with keyID from step 2
Device offloads the app (manually triggered by user, or automatically by iOS)
A new version v2 is published to App Store
Use tries to open the app
Latest version is download from the App Store
App tries to use the keyID from step 2 to generate assertions
DCAppAttestService throws invalidKey error (Error Domain=com.apple.devicecheck.error Code=3)
Step 7 is critical here, if there is no new version of the app, the reinstalled v1 can reuse the key from step 2 without any issues
Is this behaviour expected?
Is there any way we can make sure the key is preserved between offloaded app updates?
Thanks
If an app has a text filtering extension and associated server that the iPhone OS communicates with, then how can that communication be authenticated?
In other words, how can the server verify that the request is valid and coming from the iPhone and not from some spoofer?
If somebody reverse engineers the associated domain urls our of the app's info.plist or entitlement files and calls the server url directly, then how can the server detect this has occurred and the request is not coming from the iPhone OS of a handset on which the app is installed?