4

As explained here, I am trying to verify a token that is passed, by an Android app, to a server running python3.

I want to verify the passed token. The trouble is that I am running python3 on the server which is not supported by the google-api-python-client library. I found the following workaround, using the pyjwt and requests libraries, from this site:

import json
import jwt
import requests

GOOGLE_CERTS_URI = 'https://www.googleapis.com/oauth2/v1/certs'


class GoogleIdToken(object):
    def __init__(self):
        self._certs = {}
        self._token = {}

    def getCerts(self):
        cert = requests.get(GOOGLE_CERTS_URI)
        if cert.status_code == 200:
            return json.loads(cert.content)

    def isValid(self, token, audience, clientId=None):
        self._certs = self.getCerts()
        for key in self._certs:
            try:
                token = jwt.decode(token, key=self._certs[key], verify=False)
                if 'email' in token and 'aud' in token:
                    if token['aud'] == audience and (clientId == token['cid'] if clientId is not None else True):
                        self._token = token
                        return True
            except Exception, e:
                print("Error decoding: %s" % e.message)
        return False

My two questions are:

  1. Does anyone know of a different and/or better existing solution that works in python3?
  2. Is the solution above complete?
Alex
  • 18,484
  • 8
  • 60
  • 80
  • Have you found a better solution? – drfence Apr 15 '15 at 17:43
  • No, I never did find a good solution. – Alex Apr 16 '15 at 20:15
  • Does this still work for you or has Google changed anything? Looking for a native / own implementation myself. The Google libraries are horribly bloated. – Simon Steinberger Aug 02 '17 at 22:46
  • @SimonSteinberger They are... I've since switched to firebase auth using firebase ui. Much much easier: https://github.com/firebase/FirebaseUI-Android/blob/master/auth/README.md – Alex Aug 03 '17 at 14:37
  • Thanks for your answer. I've tried it in the meantime and realized, there's no need for pyjwt if you use `verify=False`. Also the certificate pulling has no function here. Basically, you can decode the JWT from Google using Python's base64 decoder. You can then verify everything manually, except the signature - for which the certificates are needed. And validating the signature is extremely complex without third party libs. So, your recommendation is certainly of great help. – Simon Steinberger Aug 08 '17 at 11:27

2 Answers2

2

After a few hours of googling around and some trial and error, this is how I ended up doing it.

Dependencies

pip install cryptography PyJWT requests

Code

import jwt, requests
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

GOOGLE_CERTS_URI = 'https://www.googleapis.com/oauth2/v1/certs'

def verify_id_token(id_token, audience):
    certs = requests.get(GOOGLE_CERTS_URI).json()

    for cert in certs.values():
        cert = str.encode(cert)
        cert_obj = load_pem_x509_certificate(cert, default_backend())
        pub_key = cert_obj.public_key()
        try:
            return jwt.decode(id_token, pub_key, algorithm='RS256',
                              audience=audience)
        except (jwt.exceptions.DecodeError,
                jwt.exceptions.ExpiredSignatureError) as e:
            pass

EDIT

I just realized Google provides an example in python for that, using their oauth2client library.

simlmx
  • 999
  • 12
  • 17
0

The Google API was recently ported to Python 3x and includes jwt verification. You can access it here.

As for your work around the only thing I'd highlight is the same point that Stefan made on the link you posted. That being the security vulnerability you're introducing as a result of having verify=False in your jwt decode call.

skyjacks
  • 1,154
  • 9
  • 7
  • Thanks - I found the port, but am hoping to avoid using an unofficial library that is so bloated (for my purposes I only need a couple methods). The only reason I was considering the google-api-python-client library at all was because I assumed it would have LTS. Also, although I haven't tried it, it looks like verify kwarg in the workaround above can be True value since PyJWT now supports RS256. – Alex May 07 '14 at 18:19