gpt4 book ai didi

asp.net-core - openid connect - 登录期间识别租户

转载 作者:行者123 更新时间:2023-12-01 17:32:34 25 4
gpt4 key购买 nike

我有一个 Multi-Tenancy (单数据库)应用程序,允许不同租户使用相同的用户名/电子邮件。

登录时(隐式流程)如何识别租户?我想到了以下可能性:

  1. 在注册时要求用户提供帐户 slug(公司/租户 slug),并且在登录期间用户应提供 slug 以及 用户名密码

    但是open id请求中没有参数来发送slug。

  2. 在注册时创建 OAuth 应用程序,并使用 slug 作为 client_id。登录时在 client_id 中传递 slug,我将使用它来获取租户 ID 并进一步验证用户。

这个方法可以吗?

编辑:

还尝试将 slug 作为路由参数的一部分

.EnableTokenEndpoint("/connect/{slug}/token");

但是 openiddict 不支持这一点。

最佳答案

编辑:此答案已更新为使用 OpenIddict 4.x。

<小时/>

McGuire 建议的方法将与 OpenIddict 一起使用(您可以通过 acr_values 访问 OpenIddictRequest.AcrValues 属性)但这不是推荐的选项(从安全角度来看这并不理想:因为发行者对于所有租户来说都是相同的,他们最终共享相同的签名 key )。

相反,请考虑为每个租户运行一个发行人。为此,您至少有两个选择:

  • OrchardCore's OpenID module尝试一下:它基于 OpenIddict,原生支持 Multi-Tenancy 。它仍处于测试阶段,但正在积极开发。

  • 覆盖 OpenIddict 使用的选项监视器以使用每租户选项

下面是第二个选项的简化示例,使用自定义监视器和基于路径的租户解析:

实现您的租户解析逻辑。例如:

public class TenantProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;

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

public string GetCurrentTenant()
{
// This sample uses the path base as the tenant.
// You can replace that by your own logic.
string tenant = _httpContextAccessor.HttpContext.Request.PathBase;
if (string.IsNullOrEmpty(tenant))
{
tenant = "default";
}

return tenant;
}
}
public void Configure(IApplicationBuilder app)
{
app.Use(next => context =>
{
// This snippet uses a hardcoded resolution logic.
// In a real world app, you'd want to customize that.
if (context.Request.Path.StartsWithSegments("/fabrikam", out PathString path))
{
context.Request.PathBase = "/fabrikam";
context.Request.Path = path;
}

return next(context);
});

app.UseDeveloperExceptionPage();

app.UseStaticFiles();

app.UseStatusCodePagesWithReExecute("/error");

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(options =>
{
options.MapControllers();
options.MapDefaultControllerRoute();
});
}

实现自定义IOptionsMonitor<OpenIddictServerOptions> :

public class OpenIddictServerOptionsProvider : IOptionsMonitor<OpenIddictServerOptions>
{
private readonly ConcurrentDictionary<(string Name, string Tenant), Lazy<OpenIddictServerOptions>> _cache;
private readonly IOptionsFactory<OpenIddictServerOptions> _optionsFactory;
private readonly TenantProvider _tenantProvider;

public OpenIddictServerOptionsProvider(
IOptionsFactory<OpenIddictServerOptions> optionsFactory,
TenantProvider tenantProvider)
{
_cache = new ConcurrentDictionary<(string, string), Lazy<OpenIddictServerOptions>>();
_optionsFactory = optionsFactory;
_tenantProvider = tenantProvider;
}

public OpenIddictServerOptions CurrentValue => Get(Options.DefaultName);

public OpenIddictServerOptions Get(string name)
{
var tenant = _tenantProvider.GetCurrentTenant();

Lazy<OpenIddictServerOptions> Create() => new(() => _optionsFactory.Create(name));
return _cache.GetOrAdd((name, tenant), _ => Create()).Value;
}

public IDisposable OnChange(Action<OpenIddictServerOptions, string> listener) => null;
}

实现自定义IConfigureNamedOptions<OpenIddictServerOptions> :

public class OpenIddictServerOptionsInitializer : IConfigureNamedOptions<OpenIddictServerOptions>
{
private readonly TenantProvider _tenantProvider;

public OpenIddictServerOptionsInitializer(TenantProvider tenantProvider)
=> _tenantProvider = tenantProvider;

public void Configure(string name, OpenIddictServerOptions options) => Configure(options);

public void Configure(OpenIddictServerOptions options)
{
var tenant = _tenantProvider.GetCurrentTenant();

// Resolve the signing credentials associated with the tenant (in a real world application,
// the credentials would be retrieved from a persistent storage like a database or a key vault).
options.SigningCredentials.Add(tenant switch
{
"fabrikam" => new(new RsaSecurityKey(RSA.Create(keySizeInBits: 2048)), SecurityAlgorithms.RsaSha256),

_ => new(new RsaSecurityKey(RSA.Create(keySizeInBits: 2048)), SecurityAlgorithms.RsaSha256)
});

// Resolve the encryption credentials associated with the tenant (in a real world application,
// the credentials would be retrieved from a persistent storage like a database or a key vault).
options.EncryptionCredentials.Add(tenant switch
{
"fabrikam" => new(new RsaSecurityKey(RSA.Create(keySizeInBits: 2048)),
SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512),

_ => new(new RsaSecurityKey(RSA.Create(keySizeInBits: 2048)),
SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)
});

// Other tenant-specific options can be registered here.
}
}

在 DI 容器中注册服务:

public void ConfigureServices(IServiceCollection services)
{
// ...

services.AddOpenIddict()

// Register the OpenIddict core components.
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})

// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization, device, introspection,
// logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("connect/authorize")
.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");

// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowDeviceCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();

// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");

// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();

// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough();
});

services.AddSingleton<TenantProvider>();
services.AddSingleton<IOptionsMonitor<OpenIddictServerOptions>, OpenIddictServerOptionsProvider>();
services.AddSingleton<IConfigureOptions<OpenIddictServerOptions>, OpenIddictServerOptionsInitializer>();
}

要确认其正常工作,请导航到 https://localhost:[port]/fabrikam/.well-known/openid-configuration(您应该获得包含 OpenID Connect 元数据的 JSON 响应)。

关于asp.net-core - openid connect - 登录期间识别租户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49596938/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com