c# - .AddOpenIdConnect() Dynamic Config on each request - Stack Overflow

admin2025-05-02  1

Currently I am trying to work on .AddOpenIdConnect() but each config would be different for each request based on the parameter/client. I have tried IConfigureNamedOptions but it seems it's only run/compiled during startup

public class ConfigureTenantOpenIdConnectOptions : IConfigureNamedOptions<OpenIdConnectOptions>
{
    private const string LoginOnMicrosoft = "login.microsoftonline";

    private readonly IHttpContextAccessor _httpContextAccessor;
    IDataProtectionProvider _rootProvider;
    public ConfigureTenantOpenIdConnectOptions(IHttpContextAccessor httpContextAccessor, IDataProtectionProvider rootProvider)
    {
        _httpContextAccessor = httpContextAccessor;
        _rootProvider = rootProvider;
    }
    public void Configure(string name, OpenIdConnectOptions options)
    {
        if(name == "tenant")
        {
            var context = _httpContextAccessor.HttpContext;
            // var tenantId = context.Items["TenantId"]?.ToString();
            var selectedIdp = context.Items["SelectedIdp"]?.ToString();

            var CurrentTenant = context.Items["CurrentTenant"] as Tenant;

            if (selectedIdp == null || CurrentTenant == null)
                return;

            var currentSSO = MultiTenancyUtil.GetCurrentSSOInfo(CurrentTenant.TenantInfo, selectedIdp);
            var authScheme = MultiTenancyUtil.GetAuthenticationScheme(currentSSO);

            switch (authScheme)
            {
                case OpenIdConnectDefaults.AuthenticationScheme:
                    options = MultiTenancyUtil.CreateOIDCOption(_rootProvider, CurrentTenant, selectedIdp, _httpContextAccessor.HttpContext);
                    break;
                default:
                    break;
            }
        }
    }
}}

I have also tried but it seems the changes here only applied at the next request. for example I click SSO Microsoft, go back, then click SSO Google, it will redirect to the Microsoft url. Now, I dont want to redirect manually from the controller and do HttpContext.ChallengeAsync because I use multi container

.AddOpenIdConnect(options =>
{
        options.Events.OnRedirectToIdentityProvider = async context =>
        {
        
            // var tenantId = context.HttpContext.Items[Constants.HTTP_CONTEXT_TENANT]?.ToString();
            var tenantContext = context.HttpContext.RequestServices.GetRequiredService<ITenantContext>();
            var tenantStore = context.HttpContext.RequestServices.GetRequiredService<ITenantStore>();
        
            var currentTenant = tenantContext.CurrentTenant;
            var erroruri = $"{context.HttpContext.Request.Host}/{currentTenant.TenantCode}/Error";
            var pProviderName = context.HttpContext.Items["SelectedIdp"]?.ToString();
        
            currentTenant.TenantInfo = await tenantStore.GetTenantInfoAsync(currentTenant.TenantCode);
            var ssoInfo = currentTenant.TenantInfo?.SSOs?.FirstOrDefault(c => c.Enabled && c.Name == pProviderName)
            ?? throw new InvalidOperationException("SSOInfo not found");
        
            var clid = ssoInfo?.ClientID;
            var clse = ssoInfo?.ClientSecret;
            
            context.ProtocolMessage.ClientId = clid;
            context.ProtocolMessage.ClientSecret = clse;
            var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration> 
            (stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
            options.ConfigurationManager = configurationManager;
            options.Authority = ssoInfo.Domain;
            await Task.CompletedTask;
        };
}

Any info if I can do SSO with dynamic config in 1 request?

Currently I am trying to work on .AddOpenIdConnect() but each config would be different for each request based on the parameter/client. I have tried IConfigureNamedOptions but it seems it's only run/compiled during startup

public class ConfigureTenantOpenIdConnectOptions : IConfigureNamedOptions<OpenIdConnectOptions>
{
    private const string LoginOnMicrosoft = "login.microsoftonline.com";

    private readonly IHttpContextAccessor _httpContextAccessor;
    IDataProtectionProvider _rootProvider;
    public ConfigureTenantOpenIdConnectOptions(IHttpContextAccessor httpContextAccessor, IDataProtectionProvider rootProvider)
    {
        _httpContextAccessor = httpContextAccessor;
        _rootProvider = rootProvider;
    }
    public void Configure(string name, OpenIdConnectOptions options)
    {
        if(name == "tenant")
        {
            var context = _httpContextAccessor.HttpContext;
            // var tenantId = context.Items["TenantId"]?.ToString();
            var selectedIdp = context.Items["SelectedIdp"]?.ToString();

            var CurrentTenant = context.Items["CurrentTenant"] as Tenant;

            if (selectedIdp == null || CurrentTenant == null)
                return;

            var currentSSO = MultiTenancyUtil.GetCurrentSSOInfo(CurrentTenant.TenantInfo, selectedIdp);
            var authScheme = MultiTenancyUtil.GetAuthenticationScheme(currentSSO);

            switch (authScheme)
            {
                case OpenIdConnectDefaults.AuthenticationScheme:
                    options = MultiTenancyUtil.CreateOIDCOption(_rootProvider, CurrentTenant, selectedIdp, _httpContextAccessor.HttpContext);
                    break;
                default:
                    break;
            }
        }
    }
}}

I have also tried but it seems the changes here only applied at the next request. for example I click SSO Microsoft, go back, then click SSO Google, it will redirect to the Microsoft url. Now, I dont want to redirect manually from the controller and do HttpContext.ChallengeAsync because I use multi container

.AddOpenIdConnect(options =>
{
        options.Events.OnRedirectToIdentityProvider = async context =>
        {
        
            // var tenantId = context.HttpContext.Items[Constants.HTTP_CONTEXT_TENANT]?.ToString();
            var tenantContext = context.HttpContext.RequestServices.GetRequiredService<ITenantContext>();
            var tenantStore = context.HttpContext.RequestServices.GetRequiredService<ITenantStore>();
        
            var currentTenant = tenantContext.CurrentTenant;
            var erroruri = $"{context.HttpContext.Request.Host}/{currentTenant.TenantCode}/Error";
            var pProviderName = context.HttpContext.Items["SelectedIdp"]?.ToString();
        
            currentTenant.TenantInfo = await tenantStore.GetTenantInfoAsync(currentTenant.TenantCode);
            var ssoInfo = currentTenant.TenantInfo?.SSOs?.FirstOrDefault(c => c.Enabled && c.Name == pProviderName)
            ?? throw new InvalidOperationException("SSOInfo not found");
        
            var clid = ssoInfo?.ClientID;
            var clse = ssoInfo?.ClientSecret;
            
            context.ProtocolMessage.ClientId = clid;
            context.ProtocolMessage.ClientSecret = clse;
            var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration> 
            (stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
            options.ConfigurationManager = configurationManager;
            options.Authority = ssoInfo.Domain;
            await Task.CompletedTask;
        };
}

Any info if I can do SSO with dynamic config in 1 request?

Share Improve this question edited Jan 2 at 6:44 Tiny Wang 16.5k2 gold badges18 silver badges38 bronze badges asked Jan 2 at 2:42 Prins AlvinoPrins Alvino 4711 bronze badges 3
  • Firstly, I think this shall be doable, just like what mentioned in this document, .Net core should support sign in with external auth provider. But you might use codes similart to this blog which uses – Tiny Wang Commented Jan 2 at 6:48
  • builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect("t1", options =>{}) .AddOpenIdConnect("t2", options =>{}); – Tiny Wang Commented Jan 2 at 6:49
  • @TinyWang I've tried that but it's not the answer that I'm looking for. the thing is, every requests will be dynamic and the list of tenant will be hundreds. so hardcoding the value foreach client is not doable – Prins Alvino Commented Jan 2 at 10:37
Add a comment  | 

1 Answer 1

Reset to default 0

I managed to find the answer. When we want to change the Options, it needs to be done before the OpenID Connect middleware processed its initial logic. this can be done like this

services.AddScoped<IOptionsMonitor<OpenIdConnectOptions>, CustomOpenIdConnectOptionsProvider>();

this way the options will be initiated before the UseAuthentication()

  public class CustomOpenIdConnectOptionsProvider : IOptionsMonitor<OpenIdConnectOptions>
  {
      public OpenIdConnectOptions Get(string name)
      {
          var startupOption = _options.Get(name);

          var newOption = GetOptions();
          if (newOption == null)
              return startupOption;

          return newOption;
      }

      private OpenIdConnectOptions GetOptions()
      {
          var context = _httpContextAccessor.HttpContext;
          var selectedIdp = context.Request.Query["provider"].ToString();

          var currentTenant = _tenantContext.CurrentTenant;
          currentTenant.TenantInfo = _tenantStore.GetTenantInfoAsync(currentTenant.TenantCode).GetAwaiter().GetResult();

          if (string.IsNullOrEmpty(selectedIdp) || currentTenant == null)
              return null;

          var currentSSO = MultiTenancyUtil.GetCurrentSSOInfo(currentTenant.TenantInfo, selectedIdp);
          var authScheme = MultiTenancyUtil.GetAuthenticationScheme(currentSSO);

          if (authScheme != OpenIdConnectDefaults.AuthenticationScheme)
              return null;

          var _oidc = MultiTenancyUtil.CreateOIDCOption(_rootProvider, currentTenant, selectedIdp, _httpContextAccessor);

          return _oidc;
      }
  }
转载请注明原文地址:http://www.anycun.com/QandA/1746136077a92071.html