Я нахожусь в процессе создания многопользовательской среды на основе 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 конечная точка все вместе? Там так много противоречивой информации; надеясь, что я рядом.