Search code examples
asp.net-coreazure-active-directorymicrosoft-graph-apiasp.net-core-webapiazure-ad-graph-api

How to get users info list from Azure AD app by calling Microsoft Graph API from ASP.NET Core Web API?


I have written this code - this API is supposed to return user's information list of my Azure AD app (has delegated permission - User.ReadBasic.All). I have done settings for

"TenantId": "","ClientId": "" & "ClientSecret": "" 

in appsettings.json file. But I am getting an "401 Unauthorized" error even if I sent the request with token from Postman.

namespace WebApplication4.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UsersController : ControllerBase
    {
        private readonly ITokenAcquisition _tokenAcquisition;

        public UsersController(ITokenAcquisition tokenAcquisition)
        {
            _tokenAcquisition = tokenAcquisition;
        }

        [HttpGet]
        public async Task<IActionResult> GetUsers()
        {
            try
            {
                // Acquire the access token for Microsoft Graph
                var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "User.ReadBasic.All", "User.Read" });

                // Create the GraphServiceClient with an authentication provider
                var graphClient = new GraphServiceClient(new AuthProvider(token));

                var usersRequestBuilder = graphClient.Users;

                var users = await usersRequestBuilder
                    .GetAsync(requestConfiguration: request =>
                    {
                        request.QueryParameters.Select = new[] { "displayName", "userType", "mail" };
                    });

                return Ok(users);
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while fetching users.");
            }
        }

        private class AuthProvider : IAuthenticationProvider
        {
            private readonly string _token;

            public AuthProvider(string token)
            {
                _token = token;
            }

            public async Task AuthenticateRequestAsync(RequestInformation request, Dictionary<string, object>? additionalAuthenticationContext = null, CancellationToken cancellationToken = default)
            {
                // Set the Authorization header
                request.Headers.Add("Authorization", $"Bearer {_token}");
                await Task.CompletedTask;
            }
        }
    }
}

I am attaching the image - This is how I've added token in request header. enter image description here

I am also getting the correct token. But when I am sending the get request I am getting 401 Unauthorized. Instead I want the user info.

What am I doing wrong?


Solution

  • The error you are facing is due to not configuring the Scope in the appsettings.json.

    I added the Scope as access_as_user in the Azuree AD app as shown below.

    enter image description here

    appsettings.json :

    {
      "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "TenantId": "<tenantID>",
        "ClientId": "<clientID>",
        "Scopes": "access_as_user"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "MicrosoftGraph": {
        "BaseUrl": "https://graph.microsoft.com/v1.0"
      }
    }
    

    Below is the complete ASP.NET Web API code to authenticate and get a list of user information from an Azure AD app by calling the Microsoft Graph API.

    UsersController.cs :

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Graph;
    using Microsoft.Identity.Web;
    using System.Net.Http.Headers;
    
    namespace WebApplication5.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class UsersController : ControllerBase
        {
            private readonly ITokenAcquisition _tokenAcquisition;
            private readonly ILogger<UsersController> _logger;
    
            public UsersController(ITokenAcquisition tokenAcquisition, ILogger<UsersController> logger)
            {
                _tokenAcquisition = tokenAcquisition;
                _logger = logger;
            }
    
            [HttpGet]
            public async Task<IActionResult> GetUsers()
            {
                try
                {
                    var token = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "User.ReadBasic.All", "User.Read" });
                    var graphClient = new GraphServiceClient(new AuthProvider(token));
    
                    var users = await graphClient.Users
                        .Request()
                        .Select(u => new { u.DisplayName, u.UserType, u.Mail })
                        .GetAsync();
    
                    return Ok(users);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "An error occurred while fetching users.");
                    return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while fetching users.");
                }
            }
    
            private class AuthProvider : IAuthenticationProvider
            {
                private readonly string _token;
                public AuthProvider(string token)
                {
                    _token = token;
                }
                public async Task AuthenticateRequestAsync(HttpRequestMessage request)
                {
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
                    await Task.CompletedTask;
                }
            }
        }
    }
    

    Program.cs :

    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.Identity.Web;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
            .EnableTokenAcquisitionToCallDownstreamApi()
                .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
                .AddInMemoryTokenCaches();
    
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
    

    I granted admin consent for User.ReadBasic.All and User.Read as shown below.

    enter image description here

    Postman Output :

    In Postman, I configured the Authorization header with the following values.

    enter image description here

    Make sure to add the Scope as below in the Postman.

    Scope : api://<clientID>/access_as_user
    

    enter image description here

    I added the below callback URL from Postman to the Azure AD app's Authentication settings as a Web URI.

    https://oauth.pstmn.io/v1/callback
    

    enter image description here

    I successfully retrieved the user information list from the Azure AD app by calling the Microsoft Graph API in Postman, as shown below.

    enter image description here