We have a Python code that tries to query an API deployed on Microsoft Azure. The Code first requests an access token from the API using azure-identity
library and then sends the token in the Authorization
header of the request like the following:
import requests
from azure.identity import ClientSecretCredential
TENANT_ID = 'my-tenant-id'
CLIENT_ID = 'my-client-id'
CLIENT_SECRET = "my_client-secret"
SCOPES = ['api://my-client-id/.default']
identity_client = ClientSecretCredential(tenant_id=TENANT_ID,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
authority='https://login.microsoftonline.com')
access_token = identity_client.get_token(SCOPES[0])
#Request the API endpoint
json = {
"function_name": "function_name",
"param1": "param1_value",
"param2": "param2_value",
}
headers = {
"Authorization": f"Bearer {access_token.token}",
"Content-Type": "application/json"
}
response = requests.get('https://myapi.whatever/myendpoint',
json=json, headers=headers)
if response.status_code == 200:
print(response.json()["result"])
else:
print(response)
However, also we get an access token (with valid signature on jwt.io
); we get the following error/response when we query the endpoint:
{'_content': b'missing_claim',
'_content_consumed': True,
'_next': None,
'status_code': 401,
'headers': {'Date': 'Fri, 12 May 2023 15:25:27 GMT', 'Content-Type': 'text/plain', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Request-Context': 'appId=cid-v1:752b04bc-08aa-4002-a618-d3e7be07a371', 'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'sameorigin', 'X-Permitted-Cross-Domain-Policies': 'none', 'Referrer-Policy': 'no-referrer'},
'raw': <urllib3.response.HTTPResponse at 0x2967109e3a0>,
'url': 'https://myapi.whatever/myendpoint',
'encoding': 'ISO-8859-1',
'history': [],
'reason': 'Unauthorized',
'cookies': <RequestsCookieJar[]>,
'elapsed': datetime.timedelta(microseconds=306335),
'request': <PreparedRequest [GET]>,
'connection': <requests.adapters.HTTPAdapter at 0x296710856a0>}
I am not sure what is causing this also we configured the permissions for the API correctly... could anyone have any idea of what is causing this error and how to fix it? Thanks.
Also note that we tried using other libraries like msal
for example:
app = msal.ConfidentialClientApplication(
client_id=CLIENT_ID,
client_credential=[CLIENT_SECRET],
authority='https://login.microsoftonline.com/my-tenant-id',
token_cache=cache,
)
result = None
result = app.acquire_token_silent(scopes=SCOPES, account=None)
if not result:
print('Here')
result = app.acquire_token_for_client(scopes=SCOPES)
but still the same error...
I agree with @Gaurav Mantri, the error 401 Unauthorized
usually occurs if your service principal does not have permissions to call the API.
To confirm that, decode the access token and check whether it has scp
claim with API permission or not.
If you added scope in Expose an API
tab, it creates delegated permissions like below:
I generated access token using client credentials flow via Postman like this:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <clientappID>
client_secret: <secret>
scope: api://<webapi_appID>/.default
Response:
When I used above access token to call API, I too got 401 Unauthorized
error like below:
GET https://myapiendpoint/weatherforecast
Response:
Note that, client credentials flow won't work with delegated permissions. So,
scp
claim would be missing in token generated with client credentials flow.
To resolve the error, you have to use delegated flow like authorization code flow, interactive flow or username password flow etc...
In my case, I generated access token using authorization code flow via Postman like this:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:authorization_code
client_id: <clientappID>
client_secret: <secret>
scope: api://<webapi_appID>/.default
redirect_uri: https://jwt.ms
code: code
Response:
When I decoded this token in jwt.ms, it has scp
claim with API permission like below:
I can make API call successfully with above access token like this:
GET https://myapiendpoint/weatherforecast
Response:
You can check this SO thread to generate access token using auth code flow via Python.