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