OAuth Bearer Token Using Python
Tags: python, oauth, bearertoken
Published: 2022-07-11 19:50:00

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](https://developer.here.com/documentation/identity-access-management/dev_guide/topics/dev-token.html) 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:

    ```json
    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.

    ```console
      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.

    ```python
        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

    ```console
        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.
    
    ```python
        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.

    ```python
        
        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

    ```python
        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

    ```python
        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.

    ```python
        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.

    ```python
        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.

    ```python
        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.

    ```python
        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”. 

    ```python
        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-

    ```json
        {
            "access_token":"eyJhbGci...",
            "token_type":"bearer",
            "expires_in":86399
        }
    ```
9. Add requirements file

    ```console
        (venv) pip freeze > requirements.txt
    ```

    > be sure to install requests module

8. The Complete Code 

    ```python
    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](https://github.com/ICCHA-Technologies/Python-Bearer-Token-Cookbook) as reference.