【问题标题】:Azure AD B2C OpenIdConnect ConfigurationManager errorAzure AD B2C OpenIdConnect ConfigurationManager 错误
【发布时间】:2021-12-23 09:31:45
【问题描述】:

我一直在将 IdentityServer 与 OIDC 中间件一起用于 asp.net core 2.1 MVC 站点以进行 Azure B2C 登录。要在 b2c 策略(singin、密码重置)之间反弹,我正在使用自定义策略 ConfigurationManager。它一直工作正常,除了不时(每隔几周)在 PolicyConfigurationManager 类的 MergeConfig 方法中引发异常。这是非常随机的,所以你永远不知道它什么时候发生。回收应用程序池可以解决问题,但几天或几周后又会再次发生。有什么线索吗?

System.NullReferenceException: Object reference not set to an instance of an object.
   at IdentityServer4.WsFederation.PolicyConfigurationManager.<>c__DisplayClass7_0.<MergeConfig>b__0(SecurityKey k)
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at IdentityServer4.WsFederation.PolicyConfigurationManager.MergeConfig(OpenIdConnectConfiguration result, OpenIdConnectConfiguration source)

PolicyConfigurationManager.cs

 class PolicyConfigurationManager : IConfigurationManager<OpenIdConnectConfiguration>
    {
        private const string PolicyParameter = "p";

        private readonly Dictionary<string, IConfigurationManager<OpenIdConnectConfiguration>> managers =
            new Dictionary<string, IConfigurationManager<OpenIdConnectConfiguration>>();

        public PolicyConfigurationManager(string authority, IEnumerable<string> policies)
        {
            foreach (var policy in policies)
            {
                var metadataAddress = $"{authority}/.well-known/openid-configuration?{PolicyParameter}={policy}";
                managers.Add(policy.ToLowerInvariant(), new ConfigurationManager<OpenIdConnectConfiguration>(metadataAddress, new OpenIdConnectConfigurationRetriever()));
            }
        }

        public async Task<OpenIdConnectConfiguration> GetConfigurationAsync(CancellationToken cancel)
        {
            OpenIdConnectConfiguration mergedConfiguration = null;
            foreach (var manager in managers)
            {
                var configuration = await manager.Value.GetConfigurationAsync(cancel);
                if (mergedConfiguration == null)
                    mergedConfiguration = Clone(configuration);
                else
                    MergeConfig(mergedConfiguration, configuration);
            }

            return mergedConfiguration;
        }

        public Task<OpenIdConnectConfiguration> GetConfigurationByPolicyAsync(CancellationToken cancel, string policy)
        {
            if (string.IsNullOrEmpty(policy))
                throw new ArgumentNullException(nameof(policy));

            var policyKey = policy.ToLowerInvariant();
            if (managers.ContainsKey(policyKey))
                return managers[policyKey].GetConfigurationAsync(cancel);

            throw new InvalidOperationException($"Invalid policy: {policy}");
        }

      private static void MergeConfig(OpenIdConnectConfiguration result, OpenIdConnectConfiguration source)
        {
            foreach (var alg in source.IdTokenSigningAlgValuesSupported)
            {
                if (!result.IdTokenSigningAlgValuesSupported.Contains(alg))
                {
                    result.IdTokenSigningAlgValuesSupported.Add(alg);
                }
            }

            foreach (var type in source.ResponseTypesSupported)
            {
                if (!result.ResponseTypesSupported.Contains(type))
                {
                    result.ResponseTypesSupported.Add(type);
                }
            }

            foreach (var type in source.SubjectTypesSupported)
            {
                if (!result.ResponseTypesSupported.Contains(type))
                {
                    result.SubjectTypesSupported.Add(type);
                }
            }

            foreach (var key in source.SigningKeys)
            {
                if (result.SigningKeys.All(k => k.KeyId != key.KeyId))
                {
                    result.SigningKeys.Add(key);
                }
            }
        }

        public void RequestRefresh()
        {
            foreach (var manager in managers)
            {
                manager.Value.RequestRefresh();
            }
        }

        private static OpenIdConnectConfiguration Clone(OpenIdConnectConfiguration configuration)
        {
            var signingKeys = new List<SecurityKey>(configuration.SigningKeys);
            configuration.SigningKeys.Clear();

            var keySet = configuration.JsonWebKeySet;
            configuration.JsonWebKeySet = null;

            var json = OpenIdConnectConfiguration.Write(configuration);
            var clone = OpenIdConnectConfiguration.Create(json);

            foreach (var key in signingKeys)
            {
                configuration.SigningKeys.Add(key);
                clone.SigningKeys.Add(key);
            }

            configuration.JsonWebKeySet = keySet;
            clone.JsonWebKeySet = keySet;

            return clone;
        }

        
    }

startup.cs

\\code omitted
string[] policies = new string[] { config_AzureAdB2C_SignInPolicy, config_AzureAdB2C_ResetPasswordPolicyId };

services.AddAuthentication()
        .AddOpenIdConnect("aad", "Login", options => {
         options.ConfigurationManager = new PolicyConfigurationManager(config_AzureB2C_EndPointForPolicyManager, policies);

//code omitted
})

【问题讨论】:

  • 请检查分配给应用程序池的私有内存和同时打开和处理线程的阈值限制。
  • 您好@Flying Squirrel,如果我的回答对您有帮助,您可以点赞并接受它作为答案(点击答案旁边的复选标记,将其从灰色切换为已填充。)。这对其他社区成员可能是有益的。谢谢。

标签: identityserver4 azure-ad-b2c openid-connect


【解决方案1】:

• 它偶尔会发送关于“PolicyConfigurationManager”类的“MergeConfig”方法的随机“内存不足”异常,因为该方法应该从“其他”复制到“源”,但是它正在将“源”复制到“其他” .此外,一旦你解决了这个问题,你就需要在 Startup 类的“ConfigureAuth”方法中的“TokenValidationParameters”上设置“ValidateIssuer = false”。否则,OpenIdConnect 将失败,因为它无法识别 id_token 的颁发者。

• 但是,这不会修复“OutOfMemory”异常错误,但令牌返回的颁发者值不会为空,因为您需要指定您不想验证颁发者或在内部指定预期的有效颁发者你的'TokenValidationParameters'。因此,这将帮助您解决您面临的问题。

请找到以下链接供您参考:-

https://github.com/AzureADQuickStarts/B2C-WebApp-OpenIdConnect-DotNet/issues/3

【讨论】:

  • 感谢您的意见。我可以进行这些更改,但设置 'ValidateIssuer=false' 有任何安全漏洞吗?在您提供的链接中,该问题是 6 年前报告的。对于 PolicyConfigurationManager 类,我们有更好的方法或替代方案吗?
  • 是的,讨论这个问题的链接肯定有 6 年的历史,但恰当地讨论了你的情况。尝试进行这些更改并观察,如果它得到修复,那就没问题了。