3

I am integrating federated login using firebase authentication and I have REST API's on google appengine endpoints and other servers that am accessing.

Conceptually, the user logs in via firebase google signin, the angular web client gets the id token using the firebase client libraries and sends it to the google endpoint. The google endpoint verifies the id token to grant access.

After reading and working through various articles I have finally got something working, however I have very little experience in this area of authorization and was wondering if there was a better way to do this.

Code to get the id token on the client side is

firebase.auth().currentUser.getToken(true).then(function(idToken) {
   // Send token to your backend via HTTPS
}

This code works and I get the correct id token.

For now I am using postman to send this id token to the app engine endpoint in the authorization header.

Google appengine endpoint code is

# only showing imports specifically required for cryptography 
import jwt
import jwt.algorithms

from jwt.contrib.algorithms.pycrypto import RSAAlgorithm
jwt.register_algorithm('RS256', RSAAlgorithm(RSAAlgorithm.SHA256))

logging.debug("algorithms = ", jwt.algorithms.get_default_algorithms())

#had to install pyjwt as a library in appengine https://cloud.google.com/appengine/docs/python/tools/using-libraries-python-27

import json
import base64

from Crypto.Util.asn1 import DerSequence
from Crypto.PublicKey import RSA
from binascii import a2b_base64

import httplib


""" Message to accept a JWT for access """
class UserJWTMsg(messages.Message):
    token = messages.StringField(1)

class UserMsg(messages.Message):
    name = messages.StringField(1)

# [START user_api]
@endpoints.api(name='user', version='v1')
class UserApi(remote.Service):

    """ Access with id Token """
    @endpoints.method(
        message_types.VoidMessage,
        UserMsg,
        path='access-with-idToken',
        http_method='GET',
        name='access-with-idToken')
    def access_with_id_token(self, request):
      id_token = self.request_state.headers.get('authorization')

      id_token_parts = id_token.split('.')

      #get key id from the token header
      #check if padding needs to be added to the id token header
      padding_count = 4 - len(id_token_parts[0])%4
      if padding_count:
        id_token_header = id_token_parts[0] + b'='*padding_count

      #decode base64 header
      decoded_id_token_header_json = base64.b64decode(id_token_header)
      decoded_id_token_header = json.loads(decoded_id_token_header_json)

      #get key id from the header
      key_id = decoded_id_token_header['kid']

      #get certificate for the given key id, #cert = CERTS.get(key_id)
      cert = getCertificate(key_id)

      #get the public key from certificate
      public_key = pubKeyFrmCert(cert)

      #decode / verify id token
      decoded_payload = jwt.decode(id_token, public_key, audience='audience')

      return UserMsg(name=json.dumps(decoded_payload));


""" get Certificate for key id """
def getCertificate(key_id):

    c =       httplib.HTTPSConnection("www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com")
    c.request("GET", "/")

    response = c.getresponse()

    cert_str = response.read()
    cert_obj = json.loads(cert_str)
    cert = cert_obj.get(key_id)

    return(cert)


""" return a public key from a X509 certificate """
""" http://stackoverflow.com/questions/12911373/how-do-i-use-a-x509-certificate-with-pycrypto """
def pubKeyFrmCert(cert):

    lines = cert.replace(" ",'').split()
    der = a2b_base64(''.join(lines[1:-1]))

    # Extract subjectPublicKeyInfo field from X.509 certificate (see RFC3280)
    cert = DerSequence()
    cert.decode(der)
    tbsCertificate = DerSequence()
    tbsCertificate.decode(cert[0])
    subjectPublicKeyInfo = tbsCertificate[6]

    # Initialize RSA key
    rsa_key = RSA.importKey(subjectPublicKeyInfo)

    return(rsa_key)
Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
Ryan
  • 73
  • 7

0 Answers0