AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
671 stars 208 forks source link

GraphServiceClient - Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed. #2827

Open sam-wheat opened 4 months ago

sam-wheat commented 4 months ago

Status Code: 0 Microsoft.Graph.ServiceException: Code: generalException Message: An error occurred sending the request.

System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed. at Autofac.Core.Lifetime.LifetimeScope.ThrowDisposedException() at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable1 parameters) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider) at Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost.GetEffectiveAuthenticationScheme(String authenticationScheme) at Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost.GetOptions(String authenticationScheme, String& effectiveAuthenticationScheme) at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(IEnumerable1 scopes, AuthorizationHeaderProviderOptions downstreamApiOptions, ClaimsPrincipal claimsPrincipal, CancellationToken cancellationToken) at Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider.AuthenticateRequestAsync(HttpRequestMessage request) at Microsoft.Graph.AuthenticationHandler.SendAsync(HttpRequestMessage httpRequestMessage, CancellationToken cancellationToken) at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) --- End of inner exception stack trace --- at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) at Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) at Microsoft.Graph.BaseRequest.SendRequestAsync(Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption) at Microsoft.Graph.BaseRequest.SendAsync[T](Object serializableObject, CancellationToken cancellationToken, HttpCompletionOption completionOption) at Microsoft.Graph.UserRequest.GetAsync(CancellationToken cancellationToken)

Project

<ItemGroup>
  <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.4" />
  <PackageReference Include="MudBlazor" Version="6.15.0" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.4" NoWarn="NU1605" />
  <PackageReference Include="Microsoft.Identity.Web" Version="2.18.1" />
  <PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" Version="2.18.1" />
  <PackageReference Include="Microsoft.Identity.Web.UI" Version="2.18.1" />   
</ItemGroup>   

Config

"DownstreamApi": {
  /*
   'Scopes' contains space separated scopes of the Web API you want to call. This can be:
    - a scope for a V2 application (for instance api:b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
    - a scope corresponding to a V1 application (for instance <App ID URI>/.default, where  <App ID URI> is the
      App ID URI of a legacy v1 Web application
    Applications are registered in the https:portal.azure.com portal.
  */
  "BaseUrl": "https://graph.microsoft.com/beta",
  "Scopes": "user.read"
},

Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLogging(x => x.AddConsole().AddSerilog());

var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
    .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
    .AddInMemoryTokenCaches();

builder.Services.AddControllersWithViews().AddMicrosoftIdentityUI();

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddScoped<AppAuthenticationProvider>();

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(ConfigureContainer);

// Add services to the container.   
builder.Services.AddExceptionHandler<ExHandler>();
builder.Services.AddRazorComponents(options => options.DetailedErrors = true)
    .AddInteractiveServerComponents();

builder.Services.AddControllersWithViews();
builder.Services.AddMudServices();
builder.Services.AddMessageBoxBlazor();
builder.Services.AddServerSideBlazor().AddMicrosoftIdentityConsentHandler();
builder.Services.AddScoped(x => BuildAppState(appConfig));

var app = builder.Build();

private static void ConfigureContainer(ContainerBuilder builder)
{
    string dir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), $"appsettings.{environmentName}.json");
    IEnumerable<IEndPointConfiguration> endPoints = EndPointUtilities.LoadEndPoints(dir);
    //builder.RegisterModule(new AutofacModule());
    RegistrationHelper registrationHelper = new RegistrationHelper(builder);

    registrationHelper
        .RegisterEndPoints(endPoints)
        .RegisterModule(new Dashboard.Services.AdaptiveClientModule());

    // Register ODBCHelper using first ERP Endpoint
    IEndPointConfiguration ep = endPoints.First(x => x.IsActive && x.API_Name == API_Name.MRP && x.EndPointType == EndPointType.DBMS);
    ILoggerFactory loggerFactory = new LoggerFactory();
    ILogger<ODBCSQLHelper> logger = loggerFactory.CreateLogger<ODBCSQLHelper>();
    ODBCSQLHelper odbchelper = new(ep.ConnectionString, logger);
    builder.RegisterInstance(odbchelper).SingleInstance();
    builder.RegisterModule(new Dashboard.Services.AutofacModule());
}

Page

@code {

     [Inject] GraphServiceClient GraphServiceClient { get; set; }

     protected override async Task OnInitializedAsync()
     {
         await base.OnInitializedAsync();
         var usr = GraphServiceClient.Me.Request().GetAsync().Result; // Works
         var user = await GraphServiceClient.Me.Request().GetAsync(); // Crash here
     }
}

My page is called via navigation. None of my code is awaiting it. What am I doing wrong?

Related issues I have found through my research:

https://github.com/AzureAD/microsoft-identity-web/wiki/calling-graph

https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/58

https://github.com/AzureAD/microsoft-identity-web/issues/1658

https://github.com/AzureAD/microsoft-identity-web/issues/1443

This issue replaces https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/2472

jmprieur commented 4 months ago

Is is Blazor on .NET 8?

sam-wheat commented 4 months ago

Yes, Blazor on .net 8.

danieltharris commented 4 months ago

When the code looks like it's not awaiting I think that's just what happens when the exception is thrown in Blazor in this situation.

The only thing I can see that's significantly different between your code and similar code I have working in some projects is that you're using AutoFac and I'm not.

As far as I know, AutoFac should be pulling in anything you registered earlier on like the GraphServiceClient but it might be worth ruling it out if you can easily comment out AutoFac temporarily and use the build in DI and see if the issue still occurs.