Principal does not have access to API/Operation error returned trying to access Text Analytics when using access token generated through an authorization code

Dushyant Godse 11 Reputation points
2021-09-05T08:06:58.207+00:00

I have tried following the process similar to as noted in this article - https://learn.microsoft.com/en-us/graph/auth-v2-user

  1. First I create an app using Azure AD App registrations. With this app, I created a secret password (Under certificates and secrets)
  2. Then go to Azure AD, Authentication tab and add a new platform configuration of type web with redirect URL as https://localhost:5555/azuredemo. This is the return URL where you will receive the authorization code.
  3. Next, goto API permissions, Add a permission, select from "APIs my organization uses" and choose Microsoft Cognitive Services. Selected Delegated Permissions and add added user_impersonations
    4.Create a sample html page to generate an authorization code request using authorize url
    <!DOCTYPE html>
    <html>
    <title> Client Authorization page </title>
    <body>
    <form action="https://login.microsoftonline.com/{my-tenant-id}/oauth2/v2.0/authorize" action="post">
    Client ID: <input type="field" name="client_id" value="xxx"/> <br/>
    Client Secret: <input type="field" name="client_secret" value="yyy"/> <br/>
    Scope: <input type="field" name="scope" value="https://cognitiveservices.azure.us/user_impersonation"/> <br/>
    Response Type: <input type="field" name="response_type" value="code"/> <br/>
    Redirect URL: <input type="field" name="redirect_url" value="https://localhost:5555/azuredemo"/> <br/>
    <input type="submit" value="Authorize client"/> <br/>
    </form>
    </body>
    </html>
  4. authorization code was returned.
  5. Take this authorization code to get access token
    https://login.microsoftonline.com/{my-tenant-id}/oauth2/v2.0/token
  6. Usin the returned access token generated in the previous get token response, i added it to the authorization header. However I get the Principal does not have access to API/Operation error error Here is the sample POSTMAN request/response
    POST https://my-cs-resource.cognitiveservices.azure.com/text/analytics/v3.0/languages: {
    "Network": {
    "addresses": {
    "local": {
    "address": "masked",
    "family": "IPv4",
    "port": 51447
    },
    "remote": {
    "address": "masked",
    "family": "IPv4",
    "port": 443
    }
    },
    "tls": {
    "reused": false,
    "authorized": true,
    "authorizationError": null,
    "cipher": {
    "name": "masked",
    "standardName": "masked",
    "version": "TLSv1/SSLv3"
    },
    "protocol": "TLSv1.2",
    "ephemeralKeyInfo": {},
    "peerCertificate": {
    "subject": {
    "country": "US",
    "stateOrProvince": "WA",
    "locality": "Redmond",
    "organization": "Microsoft Corporation",
    "commonName": ".cognitive.microsoft.com",
    "alternativeNames": "DNS:
    .api.cognitive.microsoft.com, DNS:.cognitiveservices.azure.com, DNS:.dev.cognitive.microsoft.com, DNS:.cognitive.microsoft.com"
    },
    "issuer": {
    "country": "US",
    "organization": "Microsoft Corporation",
    "commonName": "Microsoft Azure TLS Issuing CA 02"
    },
    "validFrom": "Jul 27 22:28:10 2021 GMT",
    "validTo": "Jul 22 22:28:10 2022 GMT",
    "fingerprint": "masked",
    "serialNumber": "masked"
    }
    }
    },
    "Request Headers": {
    "authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCIsImtpZCI6Imwzc1EtNTBjQ0g0eEJWWkxIVEd3blNSNzY4MCJ9.eyJhdWQiOiJodHRwczovL2NvZ25pdGl2ZXNlcnZpY2VzLmF6dXJlLmNvbS8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9mYzhkNzZlNC0xMTIyLTRmZTgtYjUxOS00NzIyYjMxNGNmZmMvIiwiaWF0IjoxNjMwODE4NTkyLCJuYmYiOjE2MzA4MTg1OTIsImV4cCI6MTYzMDgyMjQ5MiwiYWNyIjoiMSIsImFpbyI6IkFXUUFtLzhUQUFBQXYxclF1eUtxWld0d3hDcGRZQWtHQXk4UVVMM1d4VGJuZTNqeVFDOUNXNDdxWVhRV1c3cVhPU3FNcis5NUJnZzh3T3cySTI0Y0FwSnd6cW9XSXFhaUYzVDhGWWpROE9tcEdsQ2tPL2dZYmJ4R3FGeG16SW5pVWMrMUdleWdxZGlrIiwiYWx0c2VjaWQiOiIxOmxpdmUuY29tOjAwMDExQkE3NEMzMTJBMTgiLCJhbXIiOlsicHdkIiwibWZhIl0sImFwcGlkIjoiYzczMzJiYmItNGU0Mi00MmY3LTg5NDMtMjc1NWI5M2Y5MTUyIiwiYXBwaWRhY3IiOiIxIiwiZW1haWwiOiJkdXNoeWFudGdAaG90bWFpbC5jb20iLCJmYW1pbHlfbmFtZSI6IkdvZHNlIiwiZ2l2ZW5fbmFtZSI6IkR1c2h5YW50IiwiZ3JvdXBzIjpbImFlNDNiNGQyLTVhYmMtNDdlOC04YTU4LTcwMzYxYWIxZmE2YyJdLCJpZHAiOiJsaXZlLmNvbSIsImlwYWRkciI6IjczLjIyMy4yNS43MiIsIm5hbWUiOiJEdXNoeWFudCBHb2RzZSIsIm9pZCI6IjcyY2JlN2E5LWMwYjMtNDA0Ny04MmI0LWNmMjg4Y2QxNGY4MCIsInB1aWQiOiIxMDAzMjAwMENERUMwNjJDIiwicmgiOiIwLkFWb0E1SGFOX0NJUjZFLTFHVWNpc3hUUF9Mc3JNOGRDVHZkQ2lVTW5WYmtfa1ZKYUFDQS4iLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiItYjl6eVpFbS1DS0QwNkVHd2RRTzVXWDBhY1lhZmFXUUh1M2xoWmFCcGlRIiwidGlkIjoiZmM4ZDc2ZTQtMTEyMi00ZmU4LWI1MTktNDcyMmIzMTRjZmZjIiwidW5pcXVlX25hbWUiOiJsaXZlLmNvbSNkdXNoeWFudGdAaG90bWFpbC5jb20iLCJ1dGkiOiJtMzg1cGlwVDRrMlFPTGFPUnBCb0FBIiwidmVyIjoiMS4wIiwid2lkcyI6WyI2MmU5MDM5NC02OWY1LTQyMzctOTE5MC0wMTIxNzcxNDVlMTAiLCJiNzlmYmY0ZC0zZWY5LTQ2ODktODE0My03NmIxOTRlODU1MDkiXX0.ohYY5JYcBlgGuqm0REadMwjAVekDLcTlxvHjkmcCTF9a8qwcEhSbFt8Auj-L_V9up9LZlxXuXOg-GOS9_0duzYKKBzMAOZJFmcQcR-naCkrUqDT5H1_9-P3cb5pRz9B600-3_mzqIZxBqv_TLFiknBSJcBIgqpBy3dKuxdoTRKX9nI1LbeWQflWRRVdyRk24L12daPwU0ZY7_SYGPYsyfAn5AxXU18cVxc7MZUNHu27E1FRI0Pm0bgqF7ZZmAv3mDuNHPvNf-80q8XtAGHxeVurpbTD0roAIWTkEgp2QNPWvMJG8Chr7Yvmq6XanpRLpRF0C1gXU2v-NvdUOEAA9rw",
    "content-type": "application/json",
    "user-agent": "PostmanRuntime/7.28.4",
    "accept": "
    /*",
    "postman-token": "a94ce59f-4de5-4b0c-8044-849c31a8aa0e",
    "host": "my-cs-resource.cognitiveservices.azure.com",
    "accept-encoding": "gzip, deflate, br",
    "connection": "keep-alive",
    "content-length": "92"
    },
    "Request Body": "{\"documents\": [{\"countryHint\": \"US\",\"id\": \"1\",\"text\": \"Hello world. How are you today!\"}]}\r\n",
    "Response Headers": {
    "content-length": "99",
    "content-type": "application/json",
    "apim-request-id": "xxxxxx",
    "strict-transport-security": "max-age=31536000; includeSubDomains; preload",
    "x-content-type-options": "nosniff",
    "date": "Sun, 05 Sep 2021 05:16:40 GMT"
    },
    "Response Body": "{\"error\":{\"code\":\"PermissionDenied\",\"message\": \"Principal does not have access to API/Operation.\"}}"
    }

Appreciate your assistance to help me know as to why I get Permission denied on this language detection on text analytics response. This works correctly if I were to use the API key instead of access token returned via the delegated authorized code process.
Please help.
thank you.

Azure AI Language
Azure AI Language
An Azure service that provides natural language capabilities including sentiment analysis, entity extraction, and automated question answering.
388 questions
Azure AI services
Azure AI services
A group of Azure services, SDKs, and APIs designed to make apps more intelligent, engaging, and discoverable.
2,633 questions
Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
20,565 questions
{count} votes

4 answers

Sort by: Most helpful
  1. Dushyant Godse 11 Reputation points
    2021-09-08T06:08:31.667+00:00

    Although I am able to solve this using the powershell script, I would appreciate if you can tell me the alternate method of determining, how I could generate the authorized token using web request since I can pass along the client_id, client_secret, response_type as code, but what I am not sure about is the scope.
    Here is my sample HTML code to generate an authorize code request

    <!DOCTYPE html>
    <html>
    <title> Client Authorization page </title>
    <body>
    <form action="https://login.microsoftonline.com/my-masked-tenant-id/oauth2/v2.0/authorize" action="post">
    Client ID: <input type="field" name="client_id" value="my-masked-registered-app-client-id"/> <br/>
    Client Secret: <input type="field" name="client_secret" value="my-masked-registered-app-client-secret-password"/> <br/>
    Scope: <input type="field" name="scope" value="https://cognitiveservices.microsoft.com/user_impersonation"/> <br/>
    Response Type: <input type="field" name="response_type" value="code"/> <br/>
    Redirect URL: <input type="field" name="redirect_url" value="https://localhost:5555/azuredemo"/> <br/>
    <input type="submit" value="Authorize client"/> <br/>
    </form>
    </body>
    </html>

    1 person found this answer helpful.
    0 comments No comments

  2. Dushyant Godse 11 Reputation points
    2021-09-08T05:59:06.02+00:00

    I struggled to figure out from where to run dotnet add package. It would be helpful if you can let me know what I will need to install besides dotnet CLI to successfully run the command.

    0 comments No comments

  3. Dushyant Godse 11 Reputation points
    2021-09-08T06:03:16.97+00:00

    Instead I followed the following steps to install the Azure AD module within my windows powershell

    Error returned when you execute the following step
    PS C:\WINDOWS\system32> $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList "https://login.windows.net/my-masked-tenant-id"
    New-Object : Cannot find type [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]: verify that the assembly containing this type is loaded.
    At line:1 char:16

    • ... thContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirecto ...
    • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    • CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
    • FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

    This is because Azure AD module is not present in WindowsPowershell module and needed to be installed.

    PS>Install-Module -Name AzureAD -AllowClobber
    Untrusted repository
    You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'?
    [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): A
    PS C:\users\dushy\downloads> Import-Module -Name AzureAD

    PS C:\users\dushy\downloads> Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.2.140\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    PS C:\users\dushy\downloads> $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList "https://login.windows.net/fc8d76e4-1122-4fe8-b519-4722b314cffc"
    PS C:\users\dushy\downloads> $SecureStringPassword = ConvertTo-SecureString -String mypasswordString -AsPlainText -Force
    PS C:\users\dushy\downloads> $secureSecretObject = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.SecureClientSecret" -ArgumentList $SecureStringPassword

    PS C:\users\dushy\downloads> $app = New-AzADApplication -DisplayName my-cs-app -Password $SecureStringPassword
    PS C:\users\dushy\downloads> $clientCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential" -ArgumentList $app.ApplicationId, $secureSecretObject
    PS C:\users\dushy\downloads> $token=$authContext.AcquireTokenAsync("https://cognitiveservices.azure.com/", $clientCredential).Result
    PS C:\users\dushy\downloads> $token

    AccessTokenType : Bearer
    AccessToken : accesstoken returned value
    ExpiresOn : 9/8/2021 2:03:56 AM +00:00
    ExtendedLifeTimeToken : False
    TenantId :
    UserInfo :
    IdToken :
    Authority : https://login.windows.net/my-masked-tenant-id/

    POST https://my-masked-custom-subdomain.cognitiveservices.azure.com/text/analytics/v3.0/languages
    Raw body
    {
    "documents": [
    {
    "id": "1",
    "text": "Bonjour tout le monde"
    },
    {
    "id": "2",
    "text": "Hello world. How are you today"
    },
    {
    "id": "3",
    "text": "La carretera estaba atascada. Había mucho tráfico el día de ayer."
    }
    ]
    }

    Use bearer token
    Content-type = application/json

    Response
    {
    "documents": [
    {
    "id": "1",
    "detectedLanguage": {
    "name": "French",
    "iso6391Name": "fr",
    "confidenceScore": 0.88
    },
    "warnings": []
    },
    {
    "id": "2",
    "detectedLanguage": {
    "name": "English",
    "iso6391Name": "en",
    "confidenceScore": 1.0
    },
    "warnings": []
    },
    {
    "id": "3",
    "detectedLanguage": {
    "name": "Spanish",
    "iso6391Name": "es",
    "confidenceScore": 1.0
    },
    "warnings": []
    }
    ],
    "errors": [],
    "modelVersion": "2021-01-05"
    }

    0 comments No comments

  4. romungi-MSFT 43,691 Reputation points Microsoft Employee
    2021-09-08T08:19:43.68+00:00

    @Dushyant Godse The alternate or recommended approach as per cognitive service documentation is to use managed identities to use the token. I have tried this scenario with a linux VM by enabling system assigned managed identity on the cognitive resource and the VM from the identity tab on Azure portal for these resources.

    After enabling MI on the resources I granted the linux VM the role or scope of "Cognitive Services User" from the IAM tab of the cognitive resource.

    130125-image.png

    This should enable using the VM to get the access token to the face API resource securely. With the help of this article published by a user on medium I used the sample from this repo to test the AD authentication.

    From the sample only update the config to point to the service endpoint URL and the resource endpoint.

    FaceTokenCognitiveServicesEndpoint = https://cognitiveservices.azure.com  
    CognitiveFaceApiUrl = https://<resource_name>.cognitiveservices.azure.com  
    

    Getting the token using MI:

    def get_face_client_with_mi(config):  
      # Create an authenticated FaceClient.  
      endpoint = config['Authentication']['CognitiveFaceApiUrl']  
      scope = config['Authentication']['FaceTokenCognitiveServicesEndpoint']  
      creds = DefaultAzureCredential()  
      access_token = creds.get_token(scope)  
      #Convert to Dictionary  
      dict_token = { "access_token": access_token.token}  
      face_client = FaceClient(endpoint, BasicTokenAuthentication(dict_token))  
      return face_client  
    

    Once the client is returned it is used to make a call to the Face API to get the detected face.
    The documentation from Azure identity service provides more options to use as managed identities but I hope this helps.

    Credit: Tomer Shaiman

    0 comments No comments