kotlingoogle-cloud-functionsfirebase-app-check

Play Integrity API can't be completed, Error 403 when using Firebase App Check


I'm trying to utilise app check for my cloud function. After following the documentation, I am still unable to get it working properly. Here is the code I have for initialising app check from my application, the function code itself, as well code for invoking the function. At the end I will add screenshots for other non code things that I have done

Initialising App Check (In Main Activity)

FirebaseApp.initializeApp(this)
        Firebase.appCheck.installAppCheckProviderFactory(
            PlayIntegrityAppCheckProviderFactory.getInstance(),
        )

And in logcat I find this: Error getting App Check token; using placeholder token instead. Error: com.google.firebase.FirebaseException: Error returned from API. code: 403 body: App attestation failed.

Python Cloud Run Function code I'm assuming all I need to do is add is the enforce app check line.

from firebase_functions import https_fn
from google.auth import default
from googleapiclient.discovery import build
from firebase_admin import initialize_app

# Initialize the Firebase Admin SDK
initialize_app()


@https_fn.on_call(
    enforce_app_check = True
)
def verify_purchase(req: https_fn.CallableRequest) -> dict:
    """Callable Cloud Function to verify Android subscription purchases."""
    print("Request: ", req)
    try:
        # Use Application Default Credentials (ADC)
        credentials, _ = default(scopes=['https://www.googleapis.com/auth/androidpublisher'])
        androidpublisher = build('androidpublisher', 'v3', credentials=credentials)
    except Exception as e:
        raise https_fn.HttpsError(
            code=https_fn.FunctionsErrorCode.INTERNAL,
            message="Failed to initialize credentials or service",
            details=str(e)
        )

    # Extracting request data
    purchase_token = req.data.get('token')
    print("Purchase Token: ", purchase_token)
    subscription_id = req.data.get('subscription_id')
    print("Subscription ID: ", subscription_id)
    package_name = "" #Ive added this in my real code

    if not purchase_token:
        raise https_fn.HttpsError(
            code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
            message="Token is required"
        )

    if not subscription_id:
        raise https_fn.HttpsError(
            code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
            message="Subscription ID is required"
        )

    try:
        subscription = androidpublisher.purchases().subscriptions().get(
            packageName=package_name,
            subscriptionId=subscription_id,
            token=purchase_token
        ).execute()

        return {"data": subscription}
    except Exception as e:
        raise https_fn.HttpsError(
            code=https_fn.FunctionsErrorCode.UNKNOWN,
            message="Error verifying subscription",
            details=str(e)
        )

Invoking Function from App

private fun verifyPurchaseWithFirebase(purchaseToken: String, subscriptionId: String) {
        val data = hashMapOf(
            "token" to purchaseToken,
            "subscription_id" to subscriptionId
        )

        Log.d(TAG, "Verifying purchase with token: $purchaseToken")
        Log.d(TAG, "Data to send to Firebase: $data")

        functions
            .getHttpsCallable("verify_purchase")
            .call(data)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    Log.d(TAG, "Purchase verified successfully with Firebase")
                    val result = task.result?.data as? Map<String, Any>
                    if (result != null) {
                        updateUserSubscriptionStatus(result)
                    } else {
                        Log.e(TAG, "No data received from Firebase function")
                    }
                } else {
                    Log.e(TAG, "Failed to verify purchase with Firebase", task.exception)
                }
            }
    }

Set up app check on firebase with 256 SHA fingerprint. Attestation Providers: Play Integrity, Status: Registered Enabled Play Integrity API with 100% Error rate. Play Integrity API Enabled One thing I find weird... On my play console, it says my integration has only started, not complete. My cloud project is also already linked, I have tried unlinking and relinking to no avail, waited half a day as well. Integrate Integrity API started

If I am missing anything, do let me know, thank you.


Solution

  • Basically, I did not add this line

    implementation 'com.google.android.play:integrity:1.4.0'
    

    Into my build.gradle file. It mentions this in the documentation, and I guess I just overlooked it.