1

I am likely expecting or doing something that is not correct. Need some help in getting back to the right path.

Simple Usecase - How to configure a client so it can request a token with all the right scopes? currently I am running it via postman but actual client is going to be a react app using msal.

Setup:

  • App Registration in Azure.
    • API Permissions:
      • Microsoft.Graph --> email & User.Read
    • Exposed an API:
      • Scope URI: api://someguid
      • One Scope is Added : api//someguid/testscope
  • Net Core 6 API
    • AppSettings.Json
    {
      "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "Audience":"api//someguid"
        "ClientId": "my-client-id",
        "TenantId": "my-tenant-id"     
      },
      "Graph": {
        "BaseUrl": "https://graph.microsoft.com/v1.0",
        "Scopes": "user.read,email"
      }
    }
  • Middleware
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(Configuration, Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi()
        .AddMicrosoftGraph(Configuration.GetSection("Graph"))
        .AddInMemoryTokenCaches();
  • This is how I am calling graph in Controller.
 [Authorize]
 public class AbcController: Controller
 {
     private readonly GraphServiceClient _graphClient;     

     public AbcController(GraphServiceClient graphClient)
     {
         _graphClient = graphClient;
     }

     [HttpGet("get-me")]
     public async Task<ActionResult> GetSomeDetails()
     {
         var user = await _graphClient.Me.Request().GetAsync();
         return null;
     }

I run this via postman with Auth Code flow with PKCE, Here are the issues

  1. When I set the Scope as : api//someguid/testscope

    • Call gets authenticated and the Token is acquired correctly in postman
    • The API get-me get authorized correctly
    • But the call _graphClient.Me.Request().GetAsync() throws throws a 500 error
    • Also, a direct call to https://graph.microsoft.com/v1.0/me in postman using the token gives insufficient privilege error
  2. When I set the scope as : api//someguid/testscope https://graph.microsoft.com/email

    • Call gets authenticated But the acquire token fails with incorrect scope
  3. When I set the scope as : https://graph.microsoft.com/email https://graph.microsoft.com/user.read

    • Call gets authenticated and the acquire token is acquired
    • Direct call to https://graph.microsoft.com/v1.0/me works as expected
    • But now my API does not get Authorized and gives 401

Can someone suggest what am i missing in my setup or if i am doing something crazy wrong?

All i am looking to do is get my API authorized, and get the email address pulled from graph in the API, without explicitly re-acquiring the token or specifying my client secret in the API to build the graph client.

This was taken as an input to try and build my poc https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-api-call-api-call-api?tabs=aspnetcore

fireholster
  • 622
  • 1
  • 9
  • 22
  • Please see if this answers your question: https://stackoverflow.com/questions/71389400/authenticate-azure-ad-user-to-multiple-backend-services/71389632#71389632. – Gaurav Mantri Nov 28 '22 at 16:25
  • I do have EnableTokenAcquisitionToCallDownstreamApi but still iam not able to get any of the scope combinations work. I used a different section of the link you have shared. https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-call-api-call-api?tabs=aspnetcore to write my stuff... – fireholster Nov 28 '22 at 16:34
  • What you would need to do is explicitly get token for graph API in your code. Please see `GetAccessTokenForAzureSubscriptionManagementApiRequest` method in my answer and use that token for executing Graph API. – Gaurav Mantri Nov 28 '22 at 16:38
  • hmm!! let me try that. I have to setup the client if I acquire the token. I was hoping for this middleware to build that for me EnableTokenAcquisitionToCallDownstreamApi() .AddMicrosoftGraph(Configuration.GetSection("Graph")) – fireholster Nov 28 '22 at 16:41
  • well guess it is expected but that is not what i was looking for. so in order to get the GetAccessTokenForUserAsync to work, i had to expose the client secret in the API (currently on appsettings) otherwise this throws an exception. What I am try to do is to enable my client (postman in this case) to acquire a token with all the right scopes and reach out to the API...so i can read the token and process it without worrying about configuring the client secret – fireholster Nov 28 '22 at 17:57
  • I am not sure I understand. When you use `ITokenAcquisition` to acquire the token for a different service, it uses the identity of the user making your API call (`get-me`). At least that's how I was able to get token for Azure Management API using the token acquired by the user for my API. – Gaurav Mantri Nov 28 '22 at 18:09
  • That is exactly what I am looking for..but doesn;t seemt o be happening..acquiretoken with teh client secret errors out One client credential type required either: ClientSecret, Certificate, ClientAssertion or AppTokenProvider must be defined when creating a Confidential Client. – fireholster Nov 28 '22 at 18:16
  • Am i missing something in my App registration config? – fireholster Nov 28 '22 at 18:17
  • I see what you are saying. Basically `GraphClient` requires a `TokenCredentialAuthProvider`. One thing you could do is create your own `TokenCredential` using the access token you get and then use that to create a new instance of `GraphClient` (you won't be able to inject it as a dependency then). Please see my answer here: https://stackoverflow.com/questions/72941213/how-to-get-datalakeserviceclient-instance-having-access-token/72941420#72941420. HTH. – Gaurav Mantri Nov 28 '22 at 18:22

1 Answers1

2

Note that, one token can only be issued to one audience. You cannot acquire access token for multiple audience (Ex: custom API and MS Graph) in single call.

In your scenario, you need to make two separate requests for acquiring access tokens i.e., one for your API and other for Microsoft Graph.

I tried to reproduce the same in my environment via Postman and got below results

I registered one Azure AD application and added same API permissions as below:

enter image description here

Now I exposed one API named testscope same as you like below:

enter image description here

Make sure to select Single-page application while adding Redirect URIs to your application like below:

enter image description here

I acquired token successfully using Auth code flow with PKCE from Postman like below:

POST  https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:<appID>
grant_type:authorization_code
scope: api://someguid/testscope
code:code
redirect_uri: https://jwt.ms
code_verifier:S256

enter image description here

The above token won't work for calling Microsoft Graph /me endpoint and works only to authorize API based on its audience.

To check the audience of above token, decode it in jwt.ms like below:

enter image description here

To call /me endpoint for mail, you need to acquire token again with Microsoft graph scope without configuring client secret like below:

POST  https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:<appID>
grant_type:authorization_code
scope: https://graph.microsoft.com/.default
code:code
redirect_uri: https://jwt.ms
code_verifier:S256

enter image description here

The above token won't work for authorizing API whereas you can call Microsoft Graph based on its audience.

To check the audience of above token, decode it in jwt.ms like below:

enter image description here

When I call /me endpoint using above token, I got the results successfully with mail like below:

enter image description here

References:

Azure AD Oauth2 implicit grant multiple scopes by juunas

reactjs - azure msal-brower with nextJs: passing only one scope to the API

Sridevi
  • 10,599
  • 1
  • 4
  • 17
  • 2
    Thanks for attempting the answer. The the way you are attempting to re-acquire the token is happening in postman i.e. the client. That is not in question here. We are looking to do the /me in the our API not in our client. That is why we are trying to use the identity from our api scoped token and reacquire it in our API for additional scopes by trying to built the graph client using the same identity. Which doesn’t seem to be possible without providing the cert, secret or api validator. Which is what we ended up doing. – fireholster Dec 03 '22 at 12:16