From 680031ae2c9fbbf97aa90b014bffcfc4337e046e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Sat, 2 Mar 2024 06:00:51 +0000 Subject: [PATCH] Fix IAP receipt validation crash on receipt with invalid transaction ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During testing I noticed that sending receipt payloads that have a valid format but invalid transaction ID would crash the server. This commit fixes that and adds test coverage for this edge case. Testing: - All tests passing, both new and old - Ran an iOS simulator containing an IAP signed locally by Xcode (the one that originally caused the crash) and verified the crash no longer occurs Signed-off-by: Daniel D’Aquino --- src/app_store_receipt_verifier.js | 13 +++++++---- test/controllers/mock_iap_controller.js | 13 ++++++++--- test/iap_flow.test.js | 31 ++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/app_store_receipt_verifier.js b/src/app_store_receipt_verifier.js index 7fe77d0..fd64057 100644 --- a/src/app_store_receipt_verifier.js +++ b/src/app_store_receipt_verifier.js @@ -146,10 +146,15 @@ async function fetchTransactionHistory(client, transactionId) { let transactions = []; do { const revisionToken = response !== null && response.revision !== null ? response.revision : null; - response = await client.getTransactionHistory(transactionId, revisionToken, transactionHistoryRequest); - if (response.signedTransactions) { - transactions = transactions.concat(response.signedTransactions); - continue; + try { + response = await client.getTransactionHistory(transactionId, revisionToken, transactionHistoryRequest); + if (response.signedTransactions) { + transactions = transactions.concat(response.signedTransactions); + continue; + } + } catch (error) { + debug("Error fetching IAP transaction history due to an error: %o", error); + break; } } while (response.hasMore); return transactions; diff --git a/test/controllers/mock_iap_controller.js b/test/controllers/mock_iap_controller.js index 8e1c5e5..a3ff9ec 100644 --- a/test/controllers/mock_iap_controller.js +++ b/test/controllers/mock_iap_controller.js @@ -6,6 +6,7 @@ */ const current_time = require('../../src/utils.js').current_time; +const { APIException } = require('@apple/app-store-server-library'); const { v4: uuidv4 } = require('uuid') /** @@ -63,7 +64,10 @@ class MockIAPController { getTransactionHistory(transactionId, revision, transactionHistoryRequest) { // TODO: Make this function more realistic return new Promise((resolve, reject) => { - setTimeout(() => { + setTimeout(() => { + if (!MOCK_TRANSACTION_HISTORY_DATA[transactionId]) { + reject(new APIException(400, 4000006)) + } resolve({ signedTransactions: MOCK_TRANSACTION_HISTORY_DATA[transactionId], hasMore: false @@ -79,7 +83,8 @@ class MockIAPController { // Do not change this UUID or rearrange the order of the UUIDs const MOCK_ACCOUNT_UUIDS = [ - "AC3ED301-4B13-40C8-BE33-C6C2A47B8F1F" // User 0. + "AC3ED301-4B13-40C8-BE33-C6C2A47B8F1F", // User 0. + "84F48FAA-69B5-4BA9-8EFC-E343D4EF54AE", // User 1. ] const MOCK_IAP_DATES = { @@ -100,8 +105,10 @@ const MOCK_TRANSACTION_HISTORY_DATA = { const MOCK_RECEIPT_DATA = { // Belongs to user 0 [MOCK_ACCOUNT_UUIDS[0]]: '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', + // Belongs to user 1. Valid receipt format, but the transaction ID in it is invalid + [MOCK_ACCOUNT_UUIDS[1]]: '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' } module.exports = { - MockIAPController, MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES + MockIAPController, MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES, MOCK_RECEIPT_DATA } diff --git a/test/iap_flow.test.js b/test/iap_flow.test.js index 9bcd6df..521db71 100644 --- a/test/iap_flow.test.js +++ b/test/iap_flow.test.js @@ -4,7 +4,7 @@ const test = require('tap').test; const { PurpleTestController } = require('./controllers/purple_test_controller.js'); const { PURPLE_ONE_MONTH } = require('../src/invoicing.js'); -const { MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES } = require('./controllers/mock_iap_controller.js'); +const { MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES, MOCK_RECEIPT_DATA } = require('./controllers/mock_iap_controller.js'); test('IAP Flow — Expected flow', async (t) => { // Initialize the PurpleTestController @@ -70,3 +70,32 @@ test('IAP Flow — Invalid receipt should not be authorized or crash the server' t.end(); }); + +test('IAP Flow — Invalid receipt should not be authorized or crash the server', async (t) => { + // Initialize the PurpleTestController + const purple_api_controller = await PurpleTestController.new(t); + + const user_uuid = MOCK_ACCOUNT_UUIDS[1] + + // Instantiate a new client + const user_pubkey_1 = purple_api_controller.new_client(); + + // Try to get the account info + const response = await purple_api_controller.clients[user_pubkey_1].get_account(); + t.same(response.statusCode, 404); + + // Simulate IAP purchase on the iOS side + + purple_api_controller.set_account_uuid(user_pubkey_1, user_uuid); // Associate the pubkey with the user_uuid on the server + const receipt_base64 = MOCK_RECEIPT_DATA[user_uuid]; // Receipt with valid format but invalid transaction ID + + // Send the receipt to the server to activate the account + const iap_response = await purple_api_controller.clients[user_pubkey_1].send_iap_receipt(user_uuid, receipt_base64); + t.same(iap_response.statusCode, 401); + + // Read the account info now + const account_info_response = await purple_api_controller.clients[user_pubkey_1].get_account(); + t.same(account_info_response.statusCode, 404); + + t.end(); +});