Hands On

Requesting HERE OAuth Bearer Token Using Python

By Vidhan Bhonsle | 07 April 2020

Try HERE Maps

Create a free API key to build location-aware apps and services.

Get Started

As you may have noticed, we have recently introduced two new options for authentication. You can now use either an API Key or an OAuth 2.0 Bearer Token to access the HERE Location APIs. While using an API Key is straightforward, using OAuth can be bit more complicated. However, it decouples authentication from authorization, meaning that applications can access resources without exposing their credentials. In this blog post we will learn how to request a HERE OAuth Bearer Token using Python.

If you're not familiar with how OAuth works, you can think of it as a hotel key card system. If you have a card, you can use it to access your room. How do you get it? First, you have to get yourself authenticated at the hotel reception using your passport and your reservation. Then the hotel gives you a key card to access your room. However you can only access your room for the duration of your stay and access can be revoked at any time. The hotel key card in our case is a token, generated using OAuth. This token will allow us to access all HERE APIs. Lets get our token then! 

Generating OAuth Token Credentials

The first step for acquiring a token is to generate OAuth credentials from developer.here.com. You can get the complete steps for generating credentials here.

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

A 'credentials.properties' file 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

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.

Creating the Parameter String 

We need 6 key-value pair for generating a signature 

  • grant_type - Value always remains same, "client_credentials"
  • oauth_consumer_key - The Access Key ID value we acquired from credentials.properties file
  • oauth_nonce - A unique string which never repeats
  • oauth_signature_method - Always use "HMAC-SHA256"
  • oauth_timestamp - The number of seconds since the Unix epoch, in simple words, the current time 
  • 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' #From credentials.properties file
oauth_nonce = str(int(time.time()*1000))
oauth_signature_method = 'HMAC-SHA256'
oauth_timestamp = str(int(time.time()))
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

Creating 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 ("&")

Creating Signing 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 + '&'

Combining 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.

Requesting 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 - 

  • oauth_consumer_key - The value of "here.access.key.id" from credentials.properties file
  • oauth_nonce - Already have
  • oauth_signature - The value of encoded_oauth_signature from above
  • oauth_signature_method - "HMAC-SHA256"
  • oauth_timestamp - Already have
  • 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}

The Complete Code 

A lot of libraries are needed to be imported which are not mentioned in the above steps. So lets see how the complete code looks like.

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)

Conclusion

The value of access token is our actual token, and it is valid for 24 Hour. You can use token to make all HERE API calls.  Although you can continue to use API Key if you like, but if you want to go for a production environment, OAuth is the right way.