[英]Issue a new claim from ChallengeResult() in IdentityServer4
我們目前有一個需要多租戶的API。 成功登錄后,我們會將所有可用的租戶發布給用戶,但是當用戶選擇更改租戶時,我開始遇到有關用戶嘗試訪問哪個租戶上下文的問題。
我的想法是,在更改租戶后,我可以提供一個ChallengeResult()
,然后在IdentityServer中進行處理,以檢查用戶想要訪問的租戶,然后使用聲明“ ChosenTenant”(或類似的東西)發行新令牌。 像這樣:
var authProps = new AuthenticationProperties();
authProps.SetString("CurrentTenantId", response.Result.Id.ToString());
authProps.RedirectUri = "/#home";
return new ChallengeResult(authProps);
但這並不能真正起作用,因為ChallengeResult只是命中了IdentityServers“ / connect / authorize ”端點,然后返回到我的應用程序。 我的IdentityServer中有IProfileService的實現,但無法訪問以前發布的AuthenticationProperties。
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.FindFirst("sub").Value;
context.IssuedClaims = await _userStore.GetClaimsForUser(sub, context.RequestedClaimTypes, context.Client.ClientId);
return;
}
然后我的問題是:在打/ connect / authorize時如何截獲或做一些內部邏輯? 也許我正在以錯誤的方式進行操作?
...但是當用戶選擇更改租戶時,我開始遇到有關用戶試圖訪問資源的租戶上下文的問題。
這真的不是問題。 安全是關於保護資源。 選擇租戶不會更改用戶的授權。 盡管當前未選擇,但該用戶仍然有權使用為此用戶配置的所有租戶。
因此,將tenantid作為參數添加到api調用中,然后驗證是否允許用戶使用此租戶是完全可以的。 不要將授權與過濾混淆。
從Api的角度來看,唯一的上下文是客戶端提供和維護的上下文。
客戶端可以通過多種方式跟蹤當前租戶:cookie,會話,內存中的路由,例如/tenant/id/
或tenant.domain.com
。
由於我在Identity Server中托管了一些API函數來進行一些內部用戶修改,因此我需要將租戶反映在令牌中,以便可以驗證對此API的調用。
我通過在Identity Server中實現新的自定義授予來管理它,該授予將查看原始上下文並提取請求的租戶。
public class ActAsGrantValidator : IExtensionGrantValidator
{
private readonly ITokenValidator _tokenValidator;
private readonly ITenantStore _tenantStore;
public ActAsGrantValidator(ITokenValidator tokenValidator, ITenantStore tenantStore)
{
_tokenValidator = tokenValidator;
_tenantStore = tenantStore;
}
public string GrantType => "act-as";
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var userToken = context.Request.Raw.Get("accessToken");
var tenant = context.Request.Raw.Get("chosenTenant");
if (string.IsNullOrEmpty(userToken))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var result = await _tokenValidator.ValidateAccessTokenAsync(userToken);
if (result.IsError)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
// logic to validate the user and tenant
// ..
// issue a new claimsprincipal to reflect the new "persona"
var claims = result.Claims.ToList();
claims.RemoveAll(p => p.Type == "role");
claims.RemoveAll(p => p.Type == "chosentenant");
claims.Add(new Claim("chosentenant", tenant));
var identity = new ClaimsIdentity(claims);
var principal = new ClaimsPrincipal(identity);
context.Result = new GrantValidationResult(principal);
return;
}
}
然后在API的控制器中,我稱為自定義授予:
// access token from httpcontext, chosenTenant from controller
var payload = new
{
accessToken,
chosenTenant
};
var tokenClient = new TokenClient($"{_identityServerSettings.Authority}/connect/token",
_identityServerSettings.ClientId, _identityServerSettings.ClientSecret);
var response = await tokenClient.RequestCustomGrantAsync("act-as", "profile openid", payload);
然后使用新訪問令牌中的聲明重新登錄並調用用戶信息端點:
var authProperties = new AuthenticationProperties();
authProperties.StoreTokens(tokens);
authProperties.ExpiresUtc = DateTime.Now.AddHours(9);
var identity = new ClaimsIdentity(claims, "Cookies");
var principal = new ClaimsPrincipal(identity);
await _httpContext.HttpContext.SignInAsync("Cookies", principal, authProperties);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.