IdentityServer4 N-Multitenant AzureAD - Невозможно снять защиту message.state - PullRequest
0 голосов
/ 30 марта 2020

Я нахожусь в процессе создания многопользовательской среды на основе Saas. (например, tenant1.mydomain.com, tenant2.mydomain.com). Арендаторы управляются базой данных и не известны во время запуска приложения. Сервер аутентификации - auth.mydomain.com и в настоящее время работает IdentityServer4 & AspNetIdentity.

У арендаторов будет возможность привлечь своего собственного провайдера AzureAD и предоставить свои собственные ClientID и TenantID.

У меня уже есть MVC Клиент, отправляющий через свойства арендатора, подобные

        services.AddAuthentication(o =>
        {
            o.DefaultScheme = "Cookies";
            o.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", o =>
        {
            o.SignInScheme = "Cookies";
            o.Authority = Configuration.GetSection("MicroFlux")["OidcAuthority"];
            o.RequireHttpsMetadata = true;
            o.GetClaimsFromUserInfoEndpoint = true;
            o.ClientId = Configuration.GetSection("OidcClientSettings")["ClientId"];
            o.ClientSecret = Configuration.GetSection("OidcClientSettings")["ClientSecret"];
            o.ResponseType = Configuration.GetSection("OidcClientSettings")["ResponseType"];
            //options.SaveTokens = true;
            o.Scope.Add("roles");
            o.Events.OnRedirectToIdentityProvider = n =>
            {
                SetTenantRedirectToIdentityProps(n);
                return Task.CompletedTask;
            };
        });

    public void SetTenantRedirectToIdentityProps(RedirectContext n)
    {

        var tenant = n.HttpContext.GetTenant();

        if (tenant != null && tenant.TenantSsoProviderType != TenantSSOProviderType.MicroFluxIdentity)
        {
            //set props to auth server that reflects tenant's settings

            n.ProtocolMessage.Parameters.Add("tenantid", tenant.Id.ToString());
            n.ProtocolMessage.Parameters.Add("tenantname", tenant.Name);

            switch (tenant.TenantSsoProviderType)
            {
                case TenantSSOProviderType.AzureAd:
                    n.ProtocolMessage.Parameters.Add("preferred_provider", "aad_" + tenant.Name);
                    n.ProtocolMessage.Parameters.Add("aad_authority", $"https://login.microsoftonline.com/{tenant.SSO_AzureAd_TenantId}/v2.0");
                    n.ProtocolMessage.Parameters.Add("aad_clientid", tenant.SSO_AzureAd_ClientId);
                    break;
                case TenantSSOProviderType.Okta:
                    n.ProtocolMessage.Parameters.Add("preferred_provider", "okta");
                    //todo:add okta next
                    break;
            }

        }
    }

Эта часть хорошо работает, так как перенаправление на мой сервер идентификации (auth.mydomain.com) может получить доступ URL-адрес полномочий, идентификатор клиента во время выполнения (в основном, написано из этого Как я могу динамически установить параметры доступа для промежуточного программного обеспечения OpenIdConnect? ).

Мой соответствующий код auth startup.cs имеет следующий код:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("aad", "Login with Azure AD", o =>
        {
            o.Authority = $"https://login.microsoftonline.com/";
            o.TokenValidationParameters = new TokenValidationParameters {ValidateIssuer = false};
            o.ClientId = "<<set dynamically>>";
            o.CallbackPath = "/signin-oidc";
        });

        services.AddSingleton<TenantProvider>();
        services.AddSingleton<IOptionsMonitor<OpenIdConnectOptions>, OpenIdConnectOptionsProvider>();
        services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, OpenIdConnectOptionsInitializer>();

Вот поставщик арендатора:

public class TenantProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public TenantProvider(IHttpContextAccessor httpContextAccessor) => _httpContextAccessor = httpContextAccessor;

    public bool UsingExternalProvider { get; set; } = false;
    public string Authority { get; set; }
    public string SSO_AzureAd_ClientId { get; set; }
    public string SignInScheme { get; set; }


    public string GetCurrentTenant()
    {
        var name = "default";
        var cntx = _httpContextAccessor.HttpContext;

        if (cntx != null && !string.IsNullOrEmpty(cntx.Request.Query["returnUrl"]))
        {

            var decodedQueryString = WebUtility.UrlDecode(cntx.Request.Query["returnUrl"]);
            var dict = QueryHelpers.ParseQuery(decodedQueryString);

            UsingExternalProvider = true;

            if (dict.ContainsKey("preferred_provider") && dict["preferred_provider"].ToString().StartsWith("aad_"))
            {
                Authority = dict["aad_authority"].ToString();
                SSO_AzureAd_ClientId = dict["aad_clientid"];
                SignInScheme = dict["tenantname"].ToString().ToLower();
            }

            name = dict["tenantname"];

        }

        return name ?? "default";
    }
}

public class OpenIdConnectOptionsInitializer : IConfigureNamedOptions<OpenIdConnectOptions>
{
    private readonly IDataProtectionProvider _dataProtectionProvider;
    private readonly TenantProvider _tenantProvider;

    public OpenIdConnectOptionsInitializer(
        IDataProtectionProvider dataProtectionProvider,
        TenantProvider tenantProvider)
    {
        _dataProtectionProvider = dataProtectionProvider;
        _tenantProvider = tenantProvider;
    }

    public void Configure(string name, OpenIdConnectOptions options)
    {
        if (!string.Equals(name, OpenIdConnectDefaults.AuthenticationScheme, StringComparison.CurrentCultureIgnoreCase))
        {
            //return;
        }

        var tenant = _tenantProvider.GetCurrentTenant();

        // Create a tenant-specific data protection provider to ensure
        // encrypted states can't be read/decrypted by the other tenants.
        options.DataProtectionProvider = _dataProtectionProvider.CreateProtector(tenant);

        // Other tenant-specific options like options.Authority can be registered here.
        options.Authority = _tenantProvider.Authority;
        options.ClientId = _tenantProvider.SSO_AzureAd_ClientId;
        options.SignInScheme = _tenantProvider.SignInScheme;

    }

    public void Configure(OpenIdConnectOptions options)
        => Debug.Fail("This infrastructure method shouldn't be called.");
}

Текущее состояние: все вышеперечисленное работает в том, что я Я могу перенаправить на экземпляр AzureAD клиента, выполнить вход в систему, а затем получить токен идентификатора от динамически определенных полномочий.

Однако по возвращении из AzureAD в мою конечную точку / signin-oid c я получаю следующую ошибку:

Исключение: невозможно снять защиту сообщения. Состояние. Неизвестное местоположение

Я ссылался на несколько сообщений об этом (например, Федерация множественных удостоверений: Ошибка: невозможно снять защиту с сообщения. Состояние ), и было принято решение иметь уникальные URL-адреса для разных поставщиков. Но я не совсем понимаю, что делать дальше, поскольку у меня есть один провайдер, которого я хочу изменить на основе запроса на аутентификацию от нижестоящего MVC клиента.

Дополнительный вопрос: поскольку я получаю id_token от провайдера динамического c AzureAD, я должен просто реализовать свой собственный логин входа c и вручную войти в систему пользователя и пропустить / signin-oid c конечная точка все вместе? Там так много противоречивой информации; надеясь, что я рядом.

1 Ответ

0 голосов
/ 30 марта 2020

Вы можете справиться с этим другим способом, более простым. Вы можете определить своих арендаторов как ApiResource в IdentityServer. когда вы перенаправляете своего пользователя для аутентификации, вы должны указать своего арендатора, используя scope .

В IdentityServer вы должны реализовать все возможные службы OAuth, такие как Azure. Затем во время аутентификации пользователя пользователь выберет свой путь.

...