【问题标题】:Use external claims form OpenId server使用 OpenId 服务器的外部声明
【发布时间】:2019-02-06 14:55:48
【问题描述】:

我正在尝试使用 Keycloak 作为我的 ASP.Net 核心应用程序的 OpenId 服务器。

几乎没问题。用户获得授权(通过[Authorize] 属性),但他的声明为空。

我的配置:

  services.AddAuthentication(options => { options.DefaultScheme = "cookie"; })
    .AddCookie("cookie")
    .AddOpenIdConnect("oidc", options =>
    {
      options.Authority = "http://localhost:8080/auth/realms/master/";
      options.RequireHttpsMetadata = false;
      options.ClientId = "test-client";
      options.ClientSecret = "ee117d6d-25c9-4317-83a0-54c2f252aa89";
      options.GetClaimsFromUserInfoEndpoint = true;
      options.SaveTokens = true;
      options.RemoteSignOutPath = "/SignOut";
      options.SignedOutRedirectUri = "Redirect-here";
      options.ResponseType = "code";
    });

可以正常工作的控制器操作(获得授权):

[Authorize]
public IActionResult About()
{
  ViewData["Message"] = "Your application description page.";

  return View();
}

未经授权的控制器操作(因为它需要角色):

[Authorize(Roles = "Administrators")]
public IActionResult About()
{
  ViewData["Message"] = "Your application description page.";

  return View();
}

AccountController.ExternalLoginCallback:

public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
  if (remoteError != null)
  {
    ErrorMessage = $"Error from external provider: {remoteError}";
    return RedirectToAction(nameof(Login));
  }
  var info = await _signInManager.GetExternalLoginInfoAsync(); // it has claims
  if (info == null)
  {
    return RedirectToAction(nameof(Login));
  }

  var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);

  if (result.Succeeded)
  {
    _logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
    return RedirectToLocal(returnUrl);
  }
  if (result.IsLockedOut)
  {
    return RedirectToAction(nameof(Lockout));
  }
  else
  {
    ViewData["ReturnUrl"] = returnUrl;
    ViewData["LoginProvider"] = info.LoginProvider;
    var email = info.Principal.FindFirstValue(ClaimTypes.Email);
    return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
  }
}

上面 sn-p 中的info objct 有声明,尤其是角色声明:

但是当我登录并尝试查看角色和蛤蜊时,它们是空的:

var user = await _signInManager.UserManager.GetUserAsync(HttpContext.User); // user is found
var roles = await _signInManager.UserManager.GetRolesAsync(user); // empty
var claims = await _signInManager.UserManager.GetClaimsAsync(user); //empty

所以看起来 Keycloak 一切正常,但我的应用在使用声明时遇到问题。

我错过了什么?

【问题讨论】:

    标签: asp.net-core openid keycloak


    【解决方案1】:

    您需要配置身份验证提供程序,以便它知道用于角色的声明类型。

    你可以这样配置:

    services.AddOpenIdConnect("oidc", options =>
    {
        ...
    
        options.TokenValidationParameters = new TokenValidationParameters
        {
            RoleClaimType = "user_roles"; // or "user_realm_roles"?
        };
    });
    

    【讨论】:

    • 我试过了,但没用。声明(包括角色)不会从 ExternalLoginInfo 复制。您能否提供AddOpenIdConnect 实现的其余部分?
    • 剩下的就是你已经实现的。您这里有两个应用程序...一个身份服务器,一个 Web 应用程序?
    • 不,只是最小的客户端应用程序和 Keycloak 本身作为身份服务器。问题的根源在于身份忽略了来自 Keycloak 令牌的声明(所有声明,而不仅仅是角色)。
    • 查看 OpenIdConnectOptions 中的options.Events.OnUserInformationReceived(登录后调用)...您可以创建一个处理程序,该处理程序允许您从 Keycloak 访问声明并将 ClaimsIdentity 添加到使用正确的角色声明类型。
    • 我是这样添加的:context.HttpContext.User.AddIdentity((ClaimsIdentity)context.Principal.Identity); 但在下一个请求中,Keycloak 身份从 HttpContext 中消失。在用户注销之前,我应该怎么做才能将其保留在 HttpContext 中?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多