ASP.NET Core Token Acquisition Exception

1

While following this guide to setup a new AzureAD app, I encountered this error after finishing the 3rd page:

System.AggregateException
  HResult=0x80131500
  Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Identity.Web.ITokenAcquisition Lifetime: Scoped ImplementationType: Microsoft.Identity.Web.TokenAcquisition': Unable to resolve service for type 'Microsoft.Identity.Web.TokenCacheProviders.IMsalTokenCacheProvider' while attempting to activate 'Microsoft.Identity.Web.TokenAcquisition'.)
  Source=Microsoft.Extensions.DependencyInjection
  StackTrace:
   at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
   at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at Jobbi3.Program.Main(String[] args) in C:\Users\jakes\Desktop\Jobbi3\Jobbi3\Program.cs:line 16

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Identity.Web.ITokenAcquisition Lifetime: Scoped ImplementationType: Microsoft.Identity.Web.TokenAcquisition': Unable to resolve service for type 'Microsoft.Identity.Web.TokenCacheProviders.IMsalTokenCacheProvider' while attempting to activate 'Microsoft.Identity.Web.TokenAcquisition'.

Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'Microsoft.Identity.Web.TokenCacheProviders.IMsalTokenCacheProvider' while attempting to activate 'Microsoft.Identity.Web.TokenAcquisition'.

I'm not sure what could be causing this issue, and I was hoping to find someone else who may have encountered it. Also, in the guide its showing how to setup a multitenant application, but I'm using a single tenant for this specific app.

Here's my startup file:

using Jobbi3.Util;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.Graph;
using System.Net;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;

namespace Jobbi3
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(options => {
                    Configuration.Bind("AzureAd", options);
                    options.Prompt = "select_account";
                    options.Events.OnTokenValidated = async context =>
                    {
                        var tokenAcquisition = context.HttpContext.RequestServices
                            .GetRequiredService<ITokenAcquisition>();

                        var graphClient = new GraphServiceClient(
                            new DelegateAuthenticationProvider(async (request) =>
                            {
                                var token = await tokenAcquisition
                                    .GetAccessTokenForUserAsync(Config.Scopes, user: context.Principal);
                                request.Headers.Authorization =
                                    new AuthenticationHeaderValue("Bearer", token);
                            })
                        );

                        var user = await graphClient.Me.Request()
                        .Select(u => new
                        {
                            u.DisplayName,
                            u.Mail,
                            u.UserPrincipalName,
                            u.MailboxSettings
                        })
                        .GetAsync();

                        context.Principal.AddUserGraphInfo(user);

                        try
                        {
                            var photo = await graphClient.Me
                                .Photos["48x48"]
                                .Content
                                .Request()
                                .GetAsync();

                            context.Principal.AddUserGraphPhoto(photo);
                        }
                        catch (ServiceException ex)
                        {
                            if (ex.IsMatch("ErrorItemNotFound") ||
                                ex.IsMatch("ConsumerPhotoIsNotSupported"))
                            {
                                context.Principal.AddUserGraphPhoto(null);
                            }
                            else
                            {
                                throw ex;
                            }
                        }

                        options.Events.OnAuthenticationFailed = context => {
                            var error = WebUtility.UrlEncode(context.Exception.Message);
                            context.Response
                                .Redirect($"/Home/ErrorWithMessage?message=Authentication+error&debug={error}");
                            context.HandleResponse();

                            return Task.FromResult(0);
                        };

                        options.Events.OnRemoteFailure = context => {
                            if (context.Failure is OpenIdConnectProtocolException)
                            {
                                var error = WebUtility.UrlEncode(context.Failure.Message);
                                context.Response
                                    .Redirect($"/Home/ErrorWithMessage?message=Sign+in+error&debug={error}");
                                context.HandleResponse();
                            }

                            return Task.FromResult(0);
                        };
                    };
                })
                .EnableTokenAcquisitionToCallDownstreamApi(options =>
                {
                    Configuration.Bind("AzureAd", options);
                }, Config.Scopes)
                .AddMicrosoftGraph(options =>
                {
                    options.Scopes = string.Join(' ', Config.Scopes);
                });

            //services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            //    .AddAzureAD(options => Configuration.Bind("AzureAd", options));

            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).AddMicrosoftIdentityUI();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
        }
    }
}

I'm very new to working with AzureAD and MS Graph, and I'm hoping someone can tell me where I've went wrong.

azure
asp.net-core
azure-active-directory
asked on Stack Overflow Oct 23, 2020 by Jake Starr

1 Answer

2

According to your documentation, I achieved the results, but I don't know why you made the mistake. enter image description here

From your error message, did you ignore step 5? enter image description here

If you just want the completed sample,you can find it here. And if you just need to setup a single tenant for specific app. 1.Choose the fisrt option. enter image description here 2. enter image description here Accordingly, the code here should be modified as follows.

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "{set up your own Directory (tenant) ID}",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
answered on Stack Overflow Oct 26, 2020 by Chauncy Zhou

User contributions licensed under CC BY-SA 3.0