An unhandled exception occurred while processing the request. ArgumentNullException: Value cannot be null. (Parameter 'ClientId')
System.ArgumentNullException.Throw(string paramName) System.ArgumentNullException.ThrowIfNull(object argument, string paramName) System.ArgumentException.ThrowNullOrEmptyException(string argument, string paramName) System.ArgumentException.ThrowIfNullOrEmpty(string argument, string paramName) Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions.Validate() Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions.Validate(string scheme) Microsoft.AspNetCore.Authentication.AuthenticationBuilder+<>c__DisplayClass4_0<TOptions, THandler>.<AddSchemeHelper>b__1(TOptions o) Microsoft.Extensions.Options.ValidateOptions<TOptions>.Validate(string name, TOptions options) Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name) Microsoft.Extensions.Options.OptionsMonitor<TOptions>+<>c.<Get>b__10_0(string name, IOptionsFactory<TOptions> factory) Microsoft.Extensions.Options.OptionsCache<TOptions>+<>c__DisplayClass3_1<TArg>.<GetOrAdd>b__2() System.Lazy<T>.ViaFactory(LazyThreadSafetyMode mode) System.Lazy<T>.ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor) System.Lazy<T>.CreateValue() Microsoft.Extensions.Options.OptionsCache<TOptions>.GetOrAdd<TArg>(string name, Func<string, TArg, TOptions> createOptions, TArg factoryArgument) Microsoft.Extensions.Options.OptionsMonitor<TOptions>.Get(string name) Microsoft.AspNetCore.Authentication.AuthenticationHandler<TOptions>.InitializeAsync(AuthenticationScheme scheme, HttpContext context) Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.GetHandlerAsync(HttpContext context, string authenticationScheme) Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) Volo.Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext() Volo.Abp.Studio.Client.AspNetCore.AbpStudioMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Volo.Abp.Studio.Client.AspNetCore.AbpStudioMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext() Volo.Abp.AspNetCore.Tracing.AbpCorrelationIdMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext() Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+InterfaceMiddlewareBinder+<>c__DisplayClass2_0+<<CreateMiddleware>b__0>d.MoveNext() Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
- Steps to reproduce the issue: I am trying to setup azure AD as an external provider but with dynamic options so I can setup the client id, secret, etc from the application instead of the appsettings. I added the following code to both the HttpApi.Host and the AuthServer. However when the AuthServer starts it throws the exception mentioned before.
context.Services.AddDynamicExternalLoginProviderOptions<OpenIdConnectOptions>( "AzureOpenId", options => { options.WithProperty(x => x.Authority); options.WithProperty(x => x.ClientId); options.WithProperty(x => x.ClientSecret, isSecret: true); options.WithProperty(x => x.CallbackPath); });
context.Services.AddAuthentication()
.AddOpenIdConnect("AzureOpenId", "Azure AD OpenId", options =>
{
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("email");
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
});
2 Answer(s)
-
0
To add Azure AD as an external provider with dynamic options in ABP, you need to ensure that the dynamic options are properly initialized before the authentication middleware tries to use them. The error you are seeing (
ArgumentNullException: Value cannot be null. (Parameter 'ClientId')
) occurs because theClientId
(and possibly other required properties) are not set at application startup, so the OpenIdConnect middleware fails validation.Here's the correct approach for dynamic external login providers in ABP:
Register Dynamic Options and Post-Configure Handler
You must use
.WithDynamicOptions<OpenIdConnectOptions, OpenIdConnectHandler>()
after adding the OpenIdConnect provider, and register a post-configure handler to ensure dynamic properties are set before validation.
context.Services.AddAuthentication() .AddOpenIdConnect("AzureOpenId", "Azure AD", options => { options.ResponseType = OpenIdConnectResponseType.CodeIdToken; options.RequireHttpsMetadata = false; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("email"); options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub"); options.CallbackPath = configuration["AzureAd:CallbackPath"]; // Optional if dynamic }) .WithDynamicOptions<OpenIdConnectOptions, OpenIdConnectHandler>( "AzureOpenId", options => { options.WithProperty(x => x.Authority); options.WithProperty(x => x.ClientId); options.WithProperty(x => x.ClientSecret, isSecret: true); } ); context.Services.TryAddEnumerable( ServiceDescriptor.Singleton< IPostConfigureAccountExternalProviderOptions<OpenIdConnectOptions>, OpenIdConnectPostConfigureAccountExternalProviderOptions>());
Implement the Post-Configure Handler
This ensures that after dynamic properties are set, the options are re-validated:
public class OpenIdConnectPostConfigureAccountExternalProviderOptions : IPostConfigureAccountExternalProviderOptions<OpenIdConnectOptions> { private readonly IEnumerable<IPostConfigureOptions<OpenIdConnectOptions>> _postConfigureOptions; public OpenIdConnectPostConfigureAccountExternalProviderOptions(IEnumerable<IPostConfigureOptions<OpenIdConnectOptions>> postConfigureOptions) { _postConfigureOptions = postConfigureOptions; } public Task PostConfigureAsync(string name, OpenIdConnectOptions options) { foreach (var configureOption in _postConfigureOptions) { configureOption.PostConfigure(name, options); } return Task.CompletedTask; } }
Do Not Set Required Properties to Null at Startup
Do not leave
ClientId
,Authority
, etc., unset at startup. The dynamic provider system will inject them at runtime, but the post-configure handler is required to ensure this happens after all dynamic properties are available.Summary
- Use
.WithDynamicOptions
after.AddOpenIdConnect
. - Register the
IPostConfigureAccountExternalProviderOptions
implementation. - Do not set required properties to null at startup; let the dynamic system handle them.
- Use
This approach ensures that the OpenIdConnect middleware does not throw validation errors at startup and that your dynamic configuration is applied correctly.
Sources:
- https://abp.io/docs/latest/modules/account-pro
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
Just to summarize in case this happens to anyone else, using .WithDynamicOptions after .AddOpenIdConnect in the AuthServer fixed the issue but you also need to add
context.Services.AddDynamicExternalLoginProviderOptions<OpenIdConnectOptions>( "AzureOpenId", options => { options.WithProperty(x => x.Authority); options.WithProperty(x => x.ClientId); options.WithProperty(x => x.ClientSecret, isSecret: true); });
in the ApiHost to be able to configure the provider in the application.