Skip to content

System.InvalidOperationException: The service collection cannot be modified because it is read-only. on .NET 8 rc1 #2448

@jmprieur

Description

@jmprieur

Microsoft.Identity.Web Library

Microsoft.Identity.Web

Microsoft.Identity.Web version

2.13.4-preview

Web app

Sign-in users and call web APIs

Web API

Protected web APIs call downstream web APIs

Token cache serialization

In-memory caches

Description

A change in .NET 8 does no longer allow the service collection to be modified in callbacks to options.

Reproduction steps

Update the CIAM samples (for instance) to .NET 8 and Id.Web 2.13.4-preview
Run

Error message

An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The service collection cannot be modified because it is read-only.
at Microsoft.Extensions.DependencyInjection.ServiceCollection.ThrowReadOnlyException()
at Microsoft.Extensions.DependencyInjection.ServiceCollection.System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>.Add(ServiceDescriptor item)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(IServiceCollection services, Type serviceType, Func2 implementationFactory) at Microsoft.Extensions.Options.OptionsBuilder1.Configure[TDep](Action2 configureOptions) at Microsoft.Extensions.DependencyInjection.MetricsServiceExtensions.AddMetrics(IServiceCollection services) at Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.AddHttpClient(IServiceCollection services) at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderWithConfiguration.<EnableTokenAcquisitionToCallDownstreamApi>b__1_0(ConfidentialClientApplicationOptions options) at Microsoft.Extensions.Options.OptionsFactory1.Create(String name)
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode) at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy1.CreateValue() at Microsoft.Extensions.Options.OptionsCache1.GetOrAdd[TArg](String name, Func3 createOptions, TArg factoryArgument) at Microsoft.Extensions.Options.OptionsMonitor1.Get(String name)
at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder.<>c__DisplayClass11_0.b__0(OpenIdConnectOptions options, IMergedOptionsStore mergedOptionsMonitor, IOptionsMonitor1 ccaOptionsMonitor, IOptions1 ccaOptions)
at Microsoft.Extensions.Options.ConfigureNamedOptions4.Configure(String name, TOptions options) at Microsoft.Extensions.Options.OptionsFactory1.Create(String name)
at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode) at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy1.CreateValue() at Microsoft.Extensions.Options.OptionsCache1.GetOrAdd[TArg](String name, Func3 createOptions, TArg factoryArgument) at Microsoft.Extensions.Options.OptionsMonitor1.Get(String name)
at Microsoft.AspNetCore.Authentication.AuthenticationHandler1.InitializeAsync(AuthenticationScheme scheme, HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, String authenticationScheme) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context) fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidOperationException: The service collection cannot be modified because it is read-only. at Microsoft.Extensions.DependencyInjection.ServiceCollection.ThrowReadOnlyException() at Microsoft.Extensions.DependencyInjection.ServiceCollection.System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>.Add(ServiceDescriptor item) at Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(IServiceCollection services, Type serviceType, Func2 implementationFactory)
at Microsoft.Extensions.Options.OptionsBuilder1.Configure[TDep](Action2 configureOptions)
at Microsoft.Extensions.DependencyInjection.MetricsServiceExtensions.AddMetrics(IServiceCollection services)
at Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.AddHttpClient(IServiceCollection services)
at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilderWithConfiguration.b__1_0(ConfidentialClientApplicationOptions options)
at Microsoft.Extensions.Options.OptionsFactory1.Create(String name) at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) at System.Lazy1.CreateValue()
at Microsoft.Extensions.Options.OptionsCache1.GetOrAdd[TArg](String name, Func3 createOptions, TArg factoryArgument)
at Microsoft.Extensions.Options.OptionsMonitor1.Get(String name) at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder.<>c__DisplayClass11_0.<WebAppCallsWebApiImplementation>b__0(OpenIdConnectOptions options, IMergedOptionsStore mergedOptionsMonitor, IOptionsMonitor1 ccaOptionsMonitor, IOptions1 ccaOptions) at Microsoft.Extensions.Options.ConfigureNamedOptions4.Configure(String name, TOptions options)
at Microsoft.Extensions.Options.OptionsFactory1.Create(String name) at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)
--- End of stack trace from previous location ---
at System.Lazy1.CreateValue() at Microsoft.Extensions.Options.OptionsCache1.GetOrAdd[TArg](String name, Func3 createOptions, TArg factoryArgument) at Microsoft.Extensions.Options.OptionsMonitor1.Get(String name)
at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.InitializeAsync(AuthenticationScheme scheme, HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, String authenticationScheme)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Id Web logs

No response

Relevant code snippets

public new MicrosoftIdentityAppCallsWebApiAuthenticationBuilder EnableTokenAcquisitionToCallDownstreamApi(
            IEnumerable<string>? initialScopes = null)
        {
+            Services.AddHttpClient(); // should be outside
 
            return EnableTokenAcquisitionToCallDownstreamApi(
                options =>
                {
                    ConfigurationSection?.Bind(options);
                    if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled)
                    {
                        options.ClientId = AppServicesAuthenticationInformation.ClientId;
                        options.ClientSecret = AppServicesAuthenticationInformation.ClientSecret;
                        options.Instance = AppServicesAuthenticationInformation.Issuer;
                    }
-                   Services.AddHttpClient(); // shouldn't be in the callback
                },
                initialScopes);
        }

Regression

No response

Expected behavior

No exception

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1bugSomething isn't workingquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions