4
votes

I am working on an ASP.NET MVC5 Web App that uses Azure ADAL libraries to authenticate users, it works fine, however, when I manually send requests to graph, ex: GET https://graph.microsoft.com/v1.0/me or GET https://graph.microsoft.com/v1.0/groups?$filter=from/displayName eq 'whatever'.

I have tried updating the App Registration in Azure as to add the required Graph permissions, and I have also tried creating new app registrations, no matter what I do my requests will always respond 401 Unauthorized, is there anything I am missing?

EDIT: Example response from Postman

{
  "error": {
    "code": "InvalidAuthenticationToken",
    "message": "Access token validation failure.",
    "innerError": {
      "request-id": "a142576b-acce-4e59-8a8d-adede61aaf59",
      "date": "2017-04-05T13:27:36"
    }
  }
}

EDIT: C# Request Example

public async Task<GroupGraph> GetGroupIdByDisplayName(string displayName)
{
    var accessToken = await authenticationService.GetTokenUserOnly();
    GroupGraph groupGraphResponse = null;
    using (var client = new HttpClient())
    {
        using (var request = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/groups?$filter=from/displayName eq '{displayName}'"))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                using (var response = client.SendAsync(request).Result)
                {
                    if (response.IsSuccessStatusCode)
                    {
                        using (var content = response.Content)
                        {
                            var result = await content.ReadAsStringAsync();
                            groupGraphResponse = JsonConvert.DeserializeObject<GroupGraph>(result);
                        }
                    }
                }
            }
        }
        return groupGraphResponse;
    }

EDIT: The way I obtain the token

public async Task<string> GetTokenUserOnly()
    {
        string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
        string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
        string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

        // get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
        ClientCredential clientcred = new ClientCredential(clientId, appKey);
        // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
        AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new TableTokenCache(signedInUserID));
        //AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
        AuthenticationResult authenticationResult = authenticationContext.AcquireToken(graphResourceID, clientcred);
        return authenticationResult.AccessToken;
    }
3
Can you please describe what you mean by when I manually send requests to graph? Are you sending the requests through a tool like Postman or Fiddler? Have you included Authorization header in your requests?Gaurav Mantri
Yes, I have included Authorization header in my requests, I have tried using both Postman and C# to send my requests.Lisgaira
Please update your question and include the code. Also you should see details about 401 error. Please include them in your question as well.Gaurav Mantri

3 Answers

1
votes

You can't use ADAL to get tokens for graph.microsoft.com. ADAL is for graph.windows.net.

In order to get tokens for the Graph library (graph.windows.com) look into the Nuget Package Microsoft.Graph. Microsoft also has some documentation on how to pull user info using Graph.

Be forewarned though, using Graph Libraries and ADAL libraries side by side can lead to some weird side effects, such as the credential cache being cleared.

1
votes

It seems you are using the client credential grant flow to acquire the access token for graph api(graphResourceID is https://graph.microsoft.com ?) :

  AuthenticationResult authenticationResult = authenticationContext.AcquireToken(graphResourceID, clientcred);

So you need to grant app permission in azure ad portal :

enter image description here

For error "Access token validation failure" , you could use online tool like http://jwt.calebb.net/ to decode your access token , check the audience or lifetime of the access token .

0
votes

To obtain a valid token for Microsoft Graph API you can use Azure.Identity.

To use any implementation of TokenCredential we need to build our own IAuthenticationProvider.

public class TokenCredentialAuthenticationProvider : IAuthenticationProvider
{
    private readonly TokenCredential _tokenCredential;

    public TokenCredentialAuthenticationProvider(TokenCredential tokenCredential)
    {
        _tokenCredential = tokenCredential;
    }
    public async Task AuthenticateRequestAsync(HttpRequestMessage request)
    {
        var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { "https://graph.microsoft.com" }), CancellationToken.None);
        request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken.Token);
    }
}

Now we can for instance use AzureCliCredential to acquire an access token.

Open Powershell and type in az login in order to login with your Azure AD account.

In Azure you could also use Managed Identity to get a token based on a Azure resource e.g. Azure App Service. Here need to use ManagedIdentityToken.

Usage:

var client = new GraphServiceClient(new TokenCredentialAuthenticationProvider(new AzureCliCredential()));
var user = await client.Me.Request().GetAsync();