DEV Community

Cover image for OAuth Bearer Token Using Python
ICCHA Technologies
ICCHA Technologies

Posted on • Edited on

OAuth Bearer Token Using Python

As you build your own APIs, examining your use cases will help you decide which security methods to implement for them. For some use cases, API keys are sufficient; in others, youโ€™ll want the additional protection and flexibility of tokens.

Solution

A bearer Token would include an embedded digital signature and be encrypted. The digital signature confirms that the token came from a trusted source. The use of encryption means only systems meant to be able to read the token can read it.

Recipe

  1. We are going to use the API from https://developer.here.com/, so please follow these steps to get your OAuth token.

    You will get a 'credentials.properties' file created from above, it contains two values of interest, the Access Key ID and the Access Key Secret. We will be needing them going forward.

    It looks like this:

    here.user.id = HERE-USER_ID
    here.client.id = YOUR_CLIENT_ID
    here.access.key.id = YOUR_ACCESS_KEY
    here.access.key.secret = YOUR_ACCESS_KEY_SECRET
    here.token.endpoint.url = https://account.api.here.com/oauth2/token
    
  2. Install Python and the environment required to run the file.

      pip install virtualenv
    

    Then create your main folder, and create the environment.

        cd your_folder
        virtualenv venv -p python3
    

    before starting the environment, create at the main folder your "app.py"
    to put your code.

    You will now have a venv folder, go to quotes/venv/Scripts and run "activate" to active the virtual environment.

  3. Add the imports required to run the request.

        import requests #Needed for making HTTP requests
        import time #Needed to generate the OAuth timestamp
        import urllib.parse #Needed to URLencode the parameter string
        from base64 import b64encode  #Needed for create_signature function
        import hmac  #Needed for create_signature function
        import hashlib #Needed for create_signature functionx
        import binascii#Needed for create_signature function
    

    Be sure to install the request modules in your environment

        pip install requests
    
  4. Creating the Oauth Signature

    For requesting a token, we need to pass the OAuth signature in the Authorization Header of a request. Signature plays an important role as it is used for authenticating a user or application. It requires us to create a base string containing various parameters and then pass it into an HMAC-SHA256 hashing algorithm.

    Create the Parameter String

    We need 6 key-value pair for generating a signature

    1. grant_type - Value always remains same, "client_credentials"
    2. oauth_consumer_key - The Access Key ID value we acquired from credentials.properties file
    3. oauth_nonce - A unique string which never repeats
    4. oauth_signature_method - Always use "HMAC-SHA256"
    5. oauth_timestamp - The number of seconds since the Unix epoch, in simple words, the current time
    6. oauth_version - Always use "1.0"

    The values of the parameter looks like this.

        grant_type = 'client_credentials'
        oauth_consumer_key = 'HERE.ACCESS.KEY.ID'  # replace  your credentials.properties file
        access_key_secret = 'HERE.ACCESS.KEY.SECRET'  # replace credentials.properties file
        oauth_nonce = str(int(time.time()*1000))
        oauth_timestamp = str(int(time.time()))
        oauth_signature_method = 'HMAC-SHA256'
        oauth_version = '1.0'
    

    The important thing here is that the type of all 6 parameters has to be string. For calculating nonce and timestamp we have used the time module of Python.

    Next, we alphabetically combine all the parameters as a single string, separating each key value pair with an ampersand character ("&") and then URL-encoding it.

    
        def create_parameter_string(grant_type, oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version):
            parameter_string = ''
            parameter_string = parameter_string + 'grant_type=' + grant_type
            parameter_string = parameter_string + '&oauth_consumer_key=' + oauth_consumer_key
            parameter_string = parameter_string + '&oauth_nonce=' + oauth_nonce
            parameter_string = parameter_string + \
                '&oauth_signature_method=' + oauth_signature_method
            parameter_string = parameter_string + '&oauth_timestamp=' + oauth_timestamp
            parameter_string = parameter_string + '&oauth_version=' + oauth_version
            return parameter_string
    
        parameter_string = create_parameter_string(grant_type, oauth_consumer_key,oauth_nonce,oauth_signature_method,oauth_timestamp,oauth_version)
    
        encoded_parameter_string = urllib.parse.quote(parameter_string, safe='')
    

    The parameter_string is a simple concatenated output containing key-value pair separated by an ampersand character. With the help of the urllib python library, we got our URL-encoded output in encoded_parameter_string. It looks like this:

    parameter_string

        grant_type=client_credentials&oauth_consumer_key=XXXXXXXXXX&oauth_nonce=1585745318447&oauth_signature_method=HMAC-SHA256&oauth_timestamp=1585745318&oauth_version=1.0
    

    encoded_parameter_string

        grant_type%3Dclient_credentials%26oauth_consumer_key%3DXXXXXXXXXX%26oauth_nonce%3D1585745318447%26oauth_signature_method%3DHMAC-SHA256%26oauth_timestamp%3D1585745318%26oauth_version%3D1.0
    
  5. Create Signature Base String

    Next we need to add the HTTP method (POST), base URL and encoded parameter string into a single string called base string.

        url = 'https://account.api.here.com/oauth2/token'
    
        encoded_base_string = 'POST' + '&' + urllib.parse.quote(url, safe='')
        encoded_base_string = encoded_base_string + '&' + encoded_parameter_string
    

    Base string looks like this.

        POST&https%3A%2F%2Faccount.api.here.com%2Foauth2%2Ftoken&grant_type%3Dclient_credentials%26oauth_consumer_key%3DXXXXXXXXXX%26oauth_nonce%3D1585747084344%26oauth_signature_method%3DHMAC-SHA256%26oauth_timestamp%3D1585747084%26oauth_version%3D1.0
    

    POST, URL and encoded parameter string separated by ampersand ("&")

  6. Create Signgin Key

    The signing key is the URL-encoded access key secret, followed by an ampersand ("&"). The access key secret is the value of "here.access.key.secret" property in credentials.properties file.

        access_key_secret = 'HERE.ACCESS.KEY.SECRET'#From credentials.properties file
        signing_key = access_key_secret + '&'
    

    Combine all to create OAuth Signature

    The signature base string and the signing key created above, are passed to the HMAC-SHA256 Hashing Algorithm and the output is converted to a base64 string. Finally, we have our OAuth Signature.

        def create_signature(secret_key, signature_base_string):
        encoded_string = signature_base_string.encode()
        encoded_key = secret_key.encode()
        temp = hmac.new(encoded_key, encoded_string, hashlib.sha256).hexdigest()
        byte_array = b64encode(binascii.unhexlify(temp))
        return byte_array.decode()
    
        oauth_signature = create_signature(signing_key, encoded_base_string)
    
        encoded_oauth_signature = urllib.parse.quote(oauth_signature, safe='')
    

    The create_signature method takes secret key and base string as an input, performs hashing, converts the output into a base64 string. Then we URL-encode the output for further usage.

  7. Request Token

    Once we have our signature, the rest of the process is straight forward. All we require now is to create an Authorization header for the request and then make a request.

    For this, first we will combine -

    1. oauth_consumer_key - The value of "here.access.key.id" from credentials.properties file
    2. oauth_nonce - Already have
    3. oauth_signature - The value of encoded_oauth_signature from above
    4. oauth_signature_method - "HMAC-SHA256"
    5. oauth_timestamp - Already have
    6. oauth_version - "1.0"

    and append them to a string beginning with โ€œOAuthโ€.

        body = {'grant_type' : '{}'.format(grant_type)}
    
        headers = {
                    'Content-Type' : 'application/x-www-form-urlencoded',
                    'Authorization' : 'OAuth oauth_consumer_key="{0}",oauth_nonce="{1}",oauth_signature="{2}",oauth_signature_method="HMAC-SHA256",oauth_timestamp="{3}",oauth_version="1.0"'.format(oauth_consumer_key,oauth_nonce,encoded_oauth_signature,oauth_timestamp)
                  }
    
        response = requests.post(url, data=body, headers=headers)
    
        print(response.text)    
    

    The request body must contain grant_type value as 'client_credentials', always.

    The output of the code looks like this-

        {
            "access_token":"eyJhbGci...",
            "token_type":"bearer",
            "expires_in":86399
        }
    
  8. Add requirements file

        (venv) pip freeze > requirements.txt
    

    be sure to install requests module

  9. The Complete Code

    import requests #Needed for making HTTP requests
    import time #Needed to generate the OAuth timestamp
    import urllib.parse #Needed to URLencode the parameter string
    from base64 import b64encode  #Needed for create_signature function
    import hmac  #Needed for create_signature function
    import hashlib #Needed for create_signature functionx
    import binascii#Needed for create_signature function
    
    grant_type = 'client_credentials'
    oauth_consumer_key = 'HERE.ACCESS.KEY.ID' #From credentials.properties file
    access_key_secret = 'HERE.ACCESS.KEY.SECRET'#From credentials.properties file
    oauth_nonce = str(int(time.time()*1000))
    oauth_timestamp = str(int(time.time()))
    oauth_signature_method = 'HMAC-SHA256'
    oauth_version = '1.0'
    url = 'https://account.api.here.com/oauth2/token'
    
    # HMAC-SHA256 hashing algorithm to generate the OAuth signature
    def create_signature(secret_key, signature_base_string):
        encoded_string = signature_base_string.encode()
        encoded_key = secret_key.encode()
        temp = hmac.new(encoded_key, encoded_string, hashlib.sha256).hexdigest()
        byte_array = b64encode(binascii.unhexlify(temp))
        return byte_array.decode()
    
    # concatenate the six Oauth parameters, plus the request parameters from above, sorted alphabetically by the key and separated by "&"
    def create_parameter_string(grant_type, oauth_consumer_key,oauth_nonce,oauth_signature_method,oauth_timestamp,oauth_version):
        parameter_string = ''
        parameter_string = parameter_string + 'grant_type=' + grant_type
        parameter_string = parameter_string + '&oauth_consumer_key=' + oauth_consumer_key
        parameter_string = parameter_string + '&oauth_nonce=' + oauth_nonce
        parameter_string = parameter_string + '&oauth_signature_method=' + oauth_signature_method
        parameter_string = parameter_string + '&oauth_timestamp=' + oauth_timestamp
        parameter_string = parameter_string + '&oauth_version=' + oauth_version
        return parameter_string
    
    parameter_string = create_parameter_string(grant_type, oauth_consumer_key,oauth_nonce,oauth_signature_method,oauth_timestamp,oauth_version)
    encoded_parameter_string = urllib.parse.quote(parameter_string, safe='')
    encoded_base_string = 'POST' + '&' + urllib.parse.quote(url, safe='')
    encoded_base_string = encoded_base_string + '&' + encoded_parameter_string
    
    # create the signing key
    signing_key = access_key_secret + '&'
    
    oauth_signature = create_signature(signing_key, encoded_base_string)
    encoded_oauth_signature = urllib.parse.quote(oauth_signature, safe='')
    
    #---------------------Requesting Token---------------------
    body = {'grant_type' : '{}'.format(grant_type)}
    
    headers = {
                'Content-Type' : 'application/x-www-form-urlencoded',
                'Authorization' : 'OAuth oauth_consumer_key="{0}",oauth_nonce="{1}",oauth_signature="{2}",oauth_signature_method="HMAC-SHA256",oauth_timestamp="{3}",oauth_version="1.0"'.format(oauth_consumer_key,oauth_nonce,encoded_oauth_signature,oauth_timestamp)
            }
    
    response = requests.post(url, data=body, headers=headers)
    
    print(response.text)
    

Feel free to see this git project as reference.

Top comments (0)