Handle Google OAuth 2.0 API to use AndroidPublisher from a WebServer (Python)
The scope of this article
- We want to use the AndroidPublisher API
- The goal isn’t to handle user login but to have access to google api
🕵🏾♂️ Why ?
An easy access to information.
And above all, by restricting the scope of data that we are allowed to use we enhance the:
- Security for users: they only give access to some information that they choose, they can easily monitor, restrict and stop access to their data.
- Security for Companies: You have to keep in mind that every users trusts you with their data so you have to be really careful about your process and a mistake could still occurs. By only asking a limited set of access your are sure to not be hold responsible for an error that could occurred outside of your scope.
🧙🏻♀ How ?
Few steps :
- Create credentials from your google play console
- From a front instance use those credential to set a Google Authorization screen and get a temporary token
- Use this temporary token to get an access and refresh token
- Use this API
- Refresh the access token to avoid to loose connexion
👮🏼♂️ Create Google Credentials
Step 0 and Step 1: Go here
Step 2: Then Click on Create OAuth Client And View in Google Developers Console
Step 3: Now by editing your ID Clients OAuth 2.0 line you can get your client_id and your client_secret
Step 4: We recommend to change the name in order to be sure to know what this client is used for.
WARNING If you want to use the Google AndroidPublisher API from an app that isn’t associated to your project (here on the picture step 2, it is called Google Play Android Developer) you will need the app owner to do the same step and provide you with his client_id and client_secret to continue. Otherwise you will get an error from google: The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.
👩🏽💻 Front
Step 1 Ask for permission and get a temp token to exchange
For this operation you will need the previous client ID that we’ll call GOOGLE_OAUTH_CLIENT_ID.
And we have to set some variables:
- REDIRECT_URI: where you want google to respond after the user has successfully logged in. If you want to copy paste manually the temp token at the end of the process you could set redirect_uri to:
urn:ietf:wg:oauth:2.0:oob - SCOPE: what you want to ask from the user (could be several scope but they must be joined by a space for instance
https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fandroidpublisher) - RESPONSE_TYPE: (=
code) we want google to send back to REDIRECT_URI a temporary code that will be used by our backend to get the final token. - ACCESS_TYPE: here two choices:
- online (default) that require that the user is still logged in to be able to refresh the token generated and set in your backend
- offline, when your backend will ask for the final token, google will give also a refresh token that will be used to refresh the final token without asking to the user in his browser. That’s what we want.
- STATE(Optional): You can put in session or cookie a random generated string STATE, your application could later be sure that the redirection from google come initially from you (Cross-site forgery). Google advices to read this. Or you can put in STATE extra infos to be sent back to your REDIRECT_URI.
(See Optional params in docs: OAuth 2 for WebServer by Google)
Step 2: Google prompts user for consent
Now you need in your front a way (for instance a button) to send to this adapted url:
https://accounts.google.com/o/oauth2/v2/auth?
&scope=${SCOPE}
&access_type=${ACCESS_TYPE}
&include_granted_scopes=${INCLUDE_GRANTED_SCOPES}
&state=${STATE}
&redirect_uri=${REDIRECT_URI}
&response_type=${RESPONSE_TYPE}
&client_id=${GOOGLE_OAUTH_CLIENT_ID}Step 3: handle the return authorization code
Once your user finished the accept permissions process.
It will be redirected to this address
${REDIRECT_URI}?state=${STATE}&code=${GOOGLE_CODE_TOKEN}&scope=${SCOPE}Now your are back in your front.
(OPTIONAL) Check the state in the URL and the state in your user session
1- Now you get your GOOGLE_CODE_TOKEN
2- Send it to your backend.
WARNING You also have to handle the case when the user refuse those permissions.
🤖 Back
Setup if you want to use the Python API
You will need those python packages
google-api-python-client
google-auth
google-auth-httplib2
oauth2client
google-auth-oauthlib
requests
pyOpenSSL
ndg-httpsclient
pyasn1Step 1: Ask for the access_token and refresh_token
With the GOOGLE_CODE_TOKEN you can now ask for the real token.
HTTP POST Request
https://accounts.google.com/o/oauth2/token?
grant_type=authorization_code
&code=${GOOGLE_CODE_TOKEN}
&client_id=${client_id}
&client_secret=${client_secret}
&redirect_uri=${REDIRECT_URI}or with python
client_config = {
"web": {
"client_id": ${client_id},
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": ${client_secret},
"redirect_uris": [
...YOUR_URIS
],
"javascript_origins": [
...YOUR_ORIGINS
]
}
}
session, client_config = (
google_auth_oauthlib.helpers.session_from_client_config(
client_config, scopes=cls.SCOPES, redirect_uri=cls.REDIRECT_URI))
flow = google_auth_oauthlib.flow.Flow.from_client_config(
client_config,
scopes=cls.SCOPES,
redirect_uri=cls.REDIRECT_URI
)
# Or you could directly download the client_secret.json from google and use
# flow = google_auth_oauthlib.flow.Flow.from_client_secret(
# client_secret_path,
# scopes=cls.SCOPES,
# redirect_uri=cls.REDIRECT_URI
#)flow.fetch_token(code=temp_token)# Object with attribute token, refresh_token, token_uri, client_id, client_secret, scopes
credentials = flow.credentialsprint(credentials.token)
print(credentials.refresh_token)
WARNING if you have already ask for a token you won’t be able to ask for a new one and you will see a “(invalid_grant) Bad Request”
So you will have to send your users to https://myaccount.google.com/permissions and ask them to remove access to your app.
I haven’t found yet a way for the application to remove the granted access so if you have any idea please share :)
Step 2: Test the android publisher api
Now we will try to get the list of the reviews of the app linked to this project with the package name PACKAGE_NAME.
HTTP GET
https://www.googleapis.com/androidpublisher/v3/applications/${PACKAGE_NAME}/reviewsDon’t forget to set the header Authorization: Bearer ${YOUR_ACCESS_TOKEN}
Or you could use the python api
token_uri = "https://oauth2.googleapis.com/token"credentials = google.oauth2.credentials.Credentials(token=${YOUR_ACCESS_TOKEN}, refresh_token=${YOUR_REFRESH_TOKEN}, token_uri=token_uri, client_id=${client_id},
client_secret=${client_secret}, scopes=${SCOPES})service = build('androidpublisher', 'v3', credentials=credentials, cache_discovery=False)print(service.reviews().list(packageName=${PACKAGE_NAME}))
Step 3: Token refresh
You could do it by an Http POST Request like this
https://oauth2.googleapis.com/token?
grant_type=refresh_token
&client_id=${GOOGLE_OAUTH_CLIENT_ID}
&client_secret=${GOOGLE_OAUTH_CLIENT_SECRET}
&refresh_token=${YOUR_REFRESH_TOKEN}Or you could use the python api
import httplib2
from oauth2client import client, GOOGLE_REVOKE_URI, GOOGLE_TOKEN_URIcredentials = client.OAuth2Credentials(
access_token=None, # set access_token to None since we use a refresh token
client_id=self.client_id,
client_secret=self.client_secret,
refresh_token=self.refresh_token,
token_expiry=None,
token_uri=GOOGLE_TOKEN_URI,
user_agent=None,
revoke_uri=GOOGLE_REVOKE_URI)
credentials.refresh(httplib2.Http()) # refresh the access token
return credentials.access_token, credentials.refresh_token
🌜Final point
Don’t forget to write a process to revoke this token in order to be compliant to GDPR.
Sources
OAuth 2 Simplified by Aaron Parecki Well explained and tackle every kind of implementation of OAuth
OAuth 2 for WebServer by Google
OAuth 2 for WebServer by Google
