5

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

Tom3652
  • 2,540
  • 3
  • 19
  • 45

2 Answers2

2

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!

Tom
  • 635
  • 7
  • 1
    Thanks for your detailed answer, be sure i will test soon. I need to make some changes for my in app products and i will be testing it ! I have already tested PyJWT in my lambda and it's working fine, i have no arch issues or anything so it's a good point. – Tom3652 Feb 28 '23 at 12:08
  • Sounds good, let me know if you run into any issues with this. – Tom Feb 28 '23 at 12:36
  • I have managed to test it ! I have got the following error : `Algorithm 'RS256' could not be found. Do you have cryptography installed?` but i have installed `PyJwt==2.6.0` in my Lambda – Tom3652 Mar 03 '23 at 10:42
  • I have run the flow on my local python machine with the correct tokens etc.. and it's working fine thank you very much ! I just need to solve my crypto issue from the AWS lambda, it would be very nice of you if you could help me out but i am already granting you the bounty because it's exactly what i wanted – Tom3652 Mar 03 '23 at 10:45
  • 1
    i needed to install `pyjwt[crypto]` :) – Tom3652 Mar 03 '23 at 10:52
  • 1
    Awesome, cheers mate! When I installed PyJwt on my machine it pulled the crypto functions automatically. Looks like that isn't always the case, see this [question](https://stackoverflow.com/questions/67602828/decoding-jwt-token-with-pyjwt-in-python-giving-error-algorithm-not-supported). `pip3 install pyjwt[crypto]` You need to install the cryptography library in your lamda instance. – Tom Mar 03 '23 at 10:56
  • Ah ha, you got there first, nice one! – Tom Mar 03 '23 at 10:57
0

After following the tutorial for Authorization here: https://developers.google.com/android-publisher/authorization

We will have the access_token, which we need to send in Authorization header like this:

headers = {
    'accept': 'application/json',
    'Authorization': f'Bearer {access_token}',
}
response = requests.get(url, headers=headers)
Divyessh
  • 2,540
  • 1
  • 7
  • 24
  • At the first step of the tutorial i am blocked because : `You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure.` – Tom3652 Feb 27 '23 at 16:05
  • Does this help @Tom3652? https://stackoverflow.com/questions/68764885/google-oauth-2-0-api-authentication-error-error-400-redirect-uri-mismatch-do – Divyessh Mar 03 '23 at 07:23
  • The below answer of @Tom is working very fine and is exactly what i wanted. – Tom3652 Mar 03 '23 at 10:45