Search code examples
pythonandroidin-app-purchasegoogle-api-python-clientgoogle-play-developer-api

Authenticate a GET request to Google Play Purchase API with service account python


I need to verify purchases of my android App from my AWS lambda in python.

I have seen many posts of how to do so and the documentation and here is the code I have written :

url = f"{google_verify_purchase_endpoint}/{product_id}/tokens/{token}"
response = requests.get(url=url)
data = response.json()
logging.info(f"Response from Google Play API : {data}")

When I do so, it throws a 401 status code not allowed. Alright, I have created a service account to allow the request with OAuth, but how can I use it to allow the request ?

Unfortunately I can't use the google-api-python-client as mentioned here which is too big for my AWS lambda maximum size of 250Mb unzipped package.

So my question is to use the service account with a simple GET requests or how can I authenticate automatically without the google-api-python-client ?

Thanks in advance


Solution

  • Pre-Requisites and Assumptions

    It looks like you have already set-up a service account but need a hand with obtaining a JSON Web Token (JWT) before going after the verify_purchase endpoint. Generating a JWT is documented here. You should read this to understand what the following code is doing.

    I note that you have a storage-constraint, but you are almost-definitely going to need an additional library to deal with the cryptographic aspect of the token generation. PyJwt is reasonably small (including its requirements).

    Let's install this first:

    pip3 install PyJwt
    

    Obtaining a Private Key

    Next, let's grab our Service Account Private Key from Google Cloud.

    Open your project in Google Cloud.
    
    Go to "APIs & Services".
    
    Go to "Credentials".
    
    Click "Service Account".
    
    Find your Service Account and Select "Manage Keys".
    
    Select "Create new key" from the "ADD KEY" drop down.
    
    Select JSON.
    
    Save this JSON file to a secure location accessible by your script.
    

    Putting it to Use

    Now we can make a start on a Python Script. Here is an example to get you started (you should review this before putting it into production):

    import time
    import json
    import requests
    import jwt
    
    claim_start = int(time.time())
    
    # Generate a claim header, this will always be the same.
    header = {"alg":"RS256","typ":"JWT"}
    
    # This is a claim for 1hr access to the android publisher API.
    # Replace <<EMAIL ADDRESS OF YOUR SERVICE ACCOUNT>> as appropriate.
    claims = {
      "iss": "<<EMAIL ADDRESS OF YOUR SERVICE ACCOUNT>>",
      "scope": "https://www.googleapis.com/auth/androidpublisher",
      "aud": "https://oauth2.googleapis.com/token",
      "exp": claim_start + 3600,
      "iat": claim_start
    }
    
    with open("<<PATH TO JSON FILE>>", 'r') as f:
        json_key = json.load(f)
        key = json_key.get('private_key')
    
    # Generate a signed jwt
    token = jwt.encode(claims, key, headers=header, algorithm="RS256")
    
    # Mandatory parameters required by GCloud.
    params = {
      "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
      "assertion": token
    }
    
    r = requests.post("https://www.googleapis.com/oauth2/v4/token", data=params)
    r.raise_for_status()
    access_token = r.json()['access_token']
    

    Using our Access Token

    The access_token can then be used as a JWT bearer for your project. Please note, I have changed the token variable to subscription_token from your original post to make it clear that it has nothing to do with this authentication mechanism. (It refers to "the token provided to the user's device when the subscription was purchased." as per the documentation that you provided.)

    headers = {
      "Host": "www.googleapis.com",
      "Authorization": "Bearer " + access_token,
      "Content-Type": "application/json"
    }
    
    google_verify_purchase_endpoint=""
    product_id=""
    subscription_token=""
    
    url = f"{google_verify_purchase_endpoint}/{product_id}/tokens/{subscription_token}"
    response = requests.get(url=url, headers=headers)
    data = response.json()
    logging.info(f"Response from Google Play API : {data}")
    
    

    Closing Remarks

    This is merely meant to serve as an introduction into authenticating against the Google Cloud APIs without the SDK. Ultimately, you are responsible for the security of your project and anyone else reading this should probably use the SDK where possible. I also suggest that you neaten the above code up into subsequent functions to call upon where appropriate.

    Good luck with the rest of your project!