skip to Main Content

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…

2

Answers


  1. 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:

    enter image description here

    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:

    enter image description here

    When I used above access token to call API, I too got 401 Unauthorized error like below:

    GET https://myapiendpoint/weatherforecast
    

    Response:

    enter image description here

    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:

    enter image description here

    When I decoded this token in jwt.ms, it has scp claim with API permission like below:

    enter image description here

    I can make API call successfully with above access token like this:

    GET https://myapiendpoint/weatherforecast
    

    Response:

    enter image description here

    You can check this SO thread to generate access token using auth code flow via Python.

    Login or Signup to reply.
  2. I wanted to ask what is the scope of the token generated by the first method, where can we use that token?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search