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
-
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
-
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.
-
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
-
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
- 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' # 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
-
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 ("&")
-
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.
-
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 -
- 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 }
-
Add requirements file
(venv) pip freeze > requirements.txt
be sure to install requests module
-
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)