'403 forbidden error when accessing API with X-API-KEY authentication
I stumbled upon an error when trying to access “EDQM Standard Terms” dictionary via API — this is a dictionary of various pharmaceutical formulations used in the EU, and I wanted to use a Python script to fetch the data for further processing. The API documentation published by EDQM is really scarce: Standard Terms API documentation
I tried accessing one of the endpoints listed in API documentation — namely, a list of languages — and no matter what I try, I get 403 FORBIDDEN error as a reply. I checked this directly from Python, as well as with Postman, and the result is the same. I double-checked and I am using the correct API key, and the Web Service is active for my EDQM account.
I wrote the following functions:
- assemble_string
def assemble_sig(verb: str, uri: str, timestamp: str):
string_to_sign = verb + "&" + uri + "&" + HOST + "&" + timestamp
return string_to_sign
This function generates the signature mentioned in API docs based on the URI, ‘GET’ verb and current time/date.
- sign_string
def sign_string(key: str, msg: str):
signature = hmac.new(
key.encode(),
msg.encode(),
digestmod=hashlib.sha512,
).hexdigest()
signature_b64 = base64.b64encode(signature.encode())
signature_final = signature_b64.decode()
return signature_final[-22:]
This function takes my API key and the assembled string, encodes it and returns the 22 last characters.
- api_header
def api_header(uri: str):
d = dt.datetime.now()
edqm_timestamp = format_date_time(d.timestamp())
api_signature = assemble_sig(HTTP_VERB, uri, edqm_timestamp)
signed_string = sign_string(API_KEY, api_signature)
api_hdr = {
"Date": edqm_timestamp,
"X-STAPI-KEY": f"{LOGIN}|{signed_string}",
}
return api_hdr
This function, using the previous two ones, assembles the header for the API request. Generated header looks like this:
{'Date': 'Sun, 06 Feb 2022 16:43:06 GMT', 'X-STAPI-KEY': 'mylogin|FhYzRiMTllNjhlNzQ0MWI='}
where mylogin is replaced with my real login.
The entire code for the API request:
edqm_req_url = f"{HOST}{URI['languages']}"
edqm_req_header = api_header(URI["languages"])
edqm_session = requests.Session()
edqm_req = edqm_session.get(
edqm_req_url,
headers=edqm_req_header,
)
print(edqm_req.raise_for_status())
I already tried the following:
- different encoding in sign_string function — when encode() or decode() are used with UTF-8 or ASCII encoding parameter, I get 400 BAD REQUEST;
- using Authorization header {"Authorization": "X-STAPI-KEY = mylogin|signed_string”}, but I also got 400 BAD REQUEST.
I will really appreciate any help with getting this thing working!
Thanks :-)
Kuba
Solution 1:[1]
I've successfully accessed the edqm api with authentication. Let's describe the variable of the signature.
- StringToSign = HTTP-Verb + "&" + URI + "&" + HOST + "&" + Date
- HTTP-Verb
- http request method
- usually like GET?POST
- URI
- http request uri
- exclude scheme?host from url
- for example if the url is **https://standardterms.edqm.eu:443/standardterms/api/v1/domains**, the true uri is **/standardterms/api/v1/domains**
- HOST
- http request host
- constant, should be **standardterms.edqm.eu**
- Date
- same with the Date header as above
- &
- just a string value not operator
- 22 last characters of Base64( HMAC-SHA512( YourSecretAccessKey, StringToSign ) )
- you need to sign **StringToSign** with **YourSecretAccessKey** using HMAC-SHA512
- then encoded with Base64
- finally keep the last 22 characters of the base64 result
You could access my github https://github.com/asd1245dss/edqm-sync/edit/main/README.md to learn more
Solution 2:[2]
I also managed to get the API working, although I had to contact their IT support. Below I am posting the working solution:
String to sign should be as specified in the help page, as shown below: StringToSign = HTTP-Verb (=GET) + "&" + URI (=/standardterms/api/v1/languages) + "&" + HOST (=standardterms.edqm.eu) + "&" + Date; For this request, try to use: 'GET&/standardterms/api/v1/languages&standardterms.edqm.eu&Mon, 07 Feb 2022 12:35:02 GMT'
If using this, you still do not get the expected result, you could check your signature generation script. Here are the testing parameter:
String to sign: "GET&/standardterms/api/v1/languages&standardterms.edqm.eu&Mon, 07 Feb 2022 14:20:00 GMT" Secret key: "mysecret" Generated signature: "Z3DZ5tAtcmHGjeA4MUQw=="
It should also be noted that the HOST parameter should not include 'http://' or 'https://'.
Cheers, Kuba
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | SummerXY |
Solution 2 | jkosciel |