azuremicrosoft-graph-apimicrosoft-graph-sdksmicrosoft-identity-platformmicrosoft-identity-web

Get UsageRights for User via Microsoft Graph throwing error


I am writing an Excel Web Add-in that asks the user to login and calls a Web API for generating the access token. I have been successfully able to login using Microsoft and the add-in is working fine. After introducing the group plan to my add-in, Microsoft suggested to use "Microsoft Graph UsageRights API" to check whether the user has valid license assigned for the add-in as defined here. I am getting the AccessToken using the code below and it works fine.

    ConfidentialClientApplicationBuilder clientBuilder = ConfidentialClientApplicationBuilder.Create(_azureAdOptions.ClientId);
    clientBuilder.WithClientSecret(_azureAdOptions.ClientSecret);
    clientBuilder.WithRedirectUri(LoginRedirectUri.ToString());
    clientBuilder.WithAuthority(_azureAdOptions.Authority);

    ConfidentialClientApplication clientApp = (ConfidentialClientApplication)clientBuilder.Build();
    string[] sassScopes = $"{_azureAdOptions.SaaSScopes}".Split(new[] { ' ' });

    var authResultBuilder = clientApp.AcquireTokenByAuthorizationCode(
        sassScopes,
        HttpContext.Request.Query["code"].ToString()
    );

    var authResult = await authResultBuilder.ExecuteAsync();
    var accessToken = authResult.AccessToken; //THIS WORKS FINE.

Once I have the Access token, I am trying to call Graph API. However, trying to Get UsageRights working is throwing me errors as below - Approach 1: Using HttpClient

using (var httpClient = new HttpClient())
{
    string url = "https://graph.microsoft.com/beta/users/" + authResult.Account.HomeAccountId.ObjectId + "/usageRights";
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var groupsResponse = await httpClient.GetAsync(url);
    var groupsContent = await groupsResponse.Content.ReadAsStringAsync();

    var groups = JsonConvert.DeserializeObject<GraphUsageRightsResponse>(groupsContent);//Class defined here to parse JSON.
    _values.AddRange(groups.value);
    if (groups.odatanextLink != null)
    {
        //Call again API here to get other data
    }
}

The above code works when users are from my internal domain xyz.com but I am getting the below error when users from other domains (abc.com) are trying to login

{"error":{"code":"AccessDenied","message":"Access is denied to the requested resource.","innerError":{"date":"2024-08-20T13:26:39","request-id":"GUID","client-request-id":"GUID"}}}

Approach 2: I also tried calling the UsageRights using Graph Client object as below

    var scopes = new[] { "User.Read" };
    var tenantId = "common";
    var clientId = _azureAdOptions.ClientId; // App Client ID
    var clientSecret = _azureAdOptions.ClientSecret; // App Client Secret

    var authorizationCode = HttpContext.Request.Query["code"].ToString();//Authroization Code returned by Microsoft

    var options = new AuthorizationCodeCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
    };
    // https://learn.microsoft.com/dotnet/api/azure.identity.authorizationcodecredential
    var authCodeCredential = new AuthorizationCodeCredential(tenantId, clientId, clientSecret, authorizationCode, options);

    var graphClient = new GraphServiceClient(authCodeCredential, scopes);
    var result2 = await graphClient.Me.UsageRights.GetAsync(); 

The above piece of code is throwing an error as Original exception: AADSTS500112: The reply address 'https://replyurlnotset' does not match the reply address 'https://localhost:53054/AzureADAuth/Authorize' provided when requesting Authorization code. . Although this error message is clear, but I have configured the Return URL on my Microsoft Entra ID properly and re-checked this.

Any help in this regard is greatly appreciated.


Solution

  • The problem was not in the code shared above but the way the "Login" function was written to get the Authorization code. Below was my original code -

    public async Task<ActionResult> Login()
    {
        ConfidentialClientApplicationBuilder clientBuilder = ConfidentialClientApplicationBuilder.Create(_azureAdOptions.ClientId).WithClientSecret(_azureAdOptions.ClientSecret);
        ConfidentialClientApplication clientApp = (ConfidentialClientApplication)clientBuilder.Build();
    
       string[] graphScopes = { "profile" };
       var urlBuilder = clientApp.GetAuthorizationRequestUrl(graphScopes);
       urlBuilder.WithRedirectUri(LoginRedirectUri.ToString());
       urlBuilder.WithAuthority(_azureAdOptions.Authority);
    
       var authUrl = await urlBuilder.ExecuteAsync(System.Threading.CancellationToken.None);
       return Redirect(authUrl.ToString());
    }
    

    The error was in defining my graph scope. It is supposed to be User.Read instead of profile. After changing this, the Approach 1 used above worked :)