Azure CosmosDB - 'Authorization Header' Generation to perform RestAPI Calls on Cosmos DB using Python
Aravind krishna Puduri
Senior Engineer at Siemens Healthineers Development Center | EX-HCL Software | NITK
The Authorization Header (Auth Header) generation for Cosmos DB to perform Rest calls is a tough job because of its Dynamic nature. Meaning, The Auth Header changes for every type of call(Get/Post etc.), resource Id(URL), resource type(Docs, Colls, Db etc..) you try to access. The timestamp of request, is also being used in Auth Header generation.
More importantly, the official documentation for CosmosDB REST API reference only has NodeJS, C# code snippets as examples for Auth Header generation.
In this Article, we will learn how to generate this Dynamic Auth header in Python using its native modules. We will also explore what is being done on each step so that we can replicate the same for other scripting platforms like Powershell.
Also, I'm assuming you already aware of Cosmos DB and Python at basic level.
Pre-Reqs
1. Python Modules
2. Cosmos DB Pre-Reqs
Code Part
if you look at the official documentation for Auth Generation as given below,
The Auth generator functions(written in C#, NodeJS) taking resource type, resource id, type of call and cosmos DB keys as arguments. Here, there are two operations are being performed.
1. type of rest call (POST/GET/PUT etc)
2. resourceType ( basically, the type of cosmos DB resource like db, colls, docs etc. )
3. resourceID (basically, the url of the resource)
4. time stamp in UTC string format.
5. you can find appropriate values and urls in official documentation or in below link also.
The format for payload string is :
'{type_of_call}\n+{resource_type}\n+{resource_id}\n+{timestamp_in_UTC}\n\n'
-->Notice the double newlines in the end :)
import hmac
import hashlib
import base64
from datetime import datetime
import urllib.parse
key = 'master_key_to_cosmos_DB'
# Generate the UTC time String
now = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:00 GMT')
print(now)
#--------------------------------------------------------------
'''
call- post
resourceType - colls
resourceID- dbs/{db_name}
'''
payload = ('post\ncolls\ndbs/' + db_name + '\n' + now + '\n\n').lower()
Note: This article focusses more on point 2. The Understanding and exploring how these values and URL's being worked in background is out of scope for this post :).
2. Generating Auth signature based on the point 1 using HMAC.
in simple terms, we are generating a HMAC SHA-256 digest based on payload in point 1 with cosmos DB key as the KEY. to do this, we need to convert the payload into bytes using UTF-8 encoding and the master key has to be converted into bytes using UTF-8 encoding and the same has to decoded into base64 as, usually the keys are in base64 encoded form. this can be done by,
payload = bytes(payload,encoding='utf-8')
key = base64.b64decode(key.encode('utf-8'))
now that both payload and key are in bytes form we can generate the HMAC digest using below line.
hmac_digest = hmac.new(key, msg = payload, digestmod = hashlib.sha256).digest()
Now, we have to encode the digest into base64 form.
hmac_digest_b64 = base64.b64encode(hmac_digest)
the same has to be converted into string form as b64encode method returns bytes by default. we can convert HMAC signature into string form using below line,
signature = hmac_digest_b64.decode()
now, we have to attach Cosmos DB key type and version to signature and parse it into URL format using below line.
authStr = urllib.parse.quote('type=master&ver=1.0&sig={}'.format(signature))
That's it. we are done with Auth header generation. Now, this can be used in RestAPI calls to Cosmos DB to perform ops like create DB, create container, add data in container etc.
Even in Postman this can be used. The process is same for all types of requests and resource types. This dynamic signature varies by the params mentioned in point 1.
To use the authstr in header we use,
headers = {
'Authorization': authStr,
"x-ms-date": now, # matched with timestamp used in authStr genration.
"x-ms-version": "2018-12-31", # static value - can be found in off doc
"Content-type": "application/json"
}
Conclusion
The same process can be followed in other scripting platforms as long as they support mentioned HMAC SHA-256 scheme.
--Queries/Feedback always welcome--
Thanks,
Aravind Krishna
+919703051678