如何将作用域的值映射到身份声明? [英] How can I map 'scope' values to Identity Claims?

查看:8
本文介绍了如何将作用域的值映射到身份声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已指定需要作用域my_custom_value的授权策略,例如

services.AddAuthorization(AuthConfig.GetAuthorizationOptions);

// ...

public static void GetAuthorizationOptions(AuthorizationOptions options)
{
    options.AddPolicy("MyPolicy", policy =>
    {
        policy.RequireScope("my_custom_value");
    });

请求受MyPolicy保护的终结点失败,因为主体不包含任何作用域

我可以看到我的身份验证令牌具有以下作用域:

"scope": [
    "openid",
    "profile",
    "my_custom_value",
    "offline_access"
],

这些似乎没有映射到主体的声明。当我稍后在用户尝试访问受保护终结点时检查声明时,没有作用域。

policy.RequireAssertion(context =>
{
    if (context.User.HasClaim(c => c.Type == "scope")) // <-- always false
    {
        if (context.User.HasClaim(c => c.Value == "my_custom_value"))
        {
            return true;
        }
    }
为什么没有映射作用域?我需要做什么才能映射它们?

作为参考,我已经尝试过

options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Scope, "scope");
options.Scope.Add("my_custom_value");

我是否应该实现自定义IProfileService以将作用域包括在OnUserInformationReceived事件中?

推荐答案

使用mvc执行oidc身份验证时,只将标识令牌声明映射到ClaimsPain。我找不到将访问令牌声明映射或包括到Claims主体的方法。

我最终编写了一个授权处理程序来验证访问令牌并执行所需的声明检查。我假设您已经阅读了ASP.NET 5.0中的授权策略。

public class AccessTokenAuthorizationHandler : AuthorizationHandler<AccessTokenRequirement> {

readonly IOptionsMonitor<OpenIdConnectOptions> _openIdConnectOptions;
readonly ILogger<AccessTokenAuthorizationHandler> _logger;
readonly IOptions<OpenIdOptions> _openIdOptions;

public AccessTokenAuthorizationHandler(
  ILogger<AccessTokenAuthorizationHandler> logger, 
  IOptionsMonitor<OpenIdConnectOptions> openIdConnectOptions, 
  IOptions<OpenIdOptions> openIdOptions) {
  _logger = logger;
  _openIdConnectOptions = openIdConnectOptions;
  _openIdOptions = openIdOptions;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AccessTokenRequirement requirement) {
  if (context == null) {
    throw new ArgumentNullException(nameof(context));
  }
  if (requirement == null) {
    throw new ArgumentNullException(nameof(requirement));
  }
  if (context.Resource is Microsoft.AspNetCore.Mvc.ActionContext actionContext) {
    ClaimsPrincipal principal = await GetAccessTokenPrincipal(actionContext.HttpContext).ConfigureAwait(false);
   
    // verify your requirement
    if (condition met) {
      context.Succeed(requirement);
    }
  }
}

private async Task<ClaimsPrincipal> GetAccessTokenPrincipal(HttpContext httpContext) {
  if (httpContext == null) {
    return null;
  }
  String accessToken = await httpContext.GetUserAccessTokenAsync().ConfigureAwait(false);
  if (!String.IsNullOrWhiteSpace(accessToken)) {
    try {
      TokenValidationParameters validationParameters = await BuildValidationParameters();
      return new JwtSecurityTokenHandler().ValidateToken(accessToken, validationParameters, out var rawValidatedToken);
      
    }
    catch (SecurityTokenValidationException validationException) {
      _logger.LogWarning(validationException, "Access token not valid.");
    }
    catch (Exception ex) {
      _logger.LogError(ex, "Access token could not be validated.");
    }
  }
  return null;
}

private async Task<TokenValidationParameters> BuildValidationParameters() {
  var options = _openIdConnectOptions.Get(OpenIdConnectDefaults.AuthenticationScheme);
  var discoveryDocument = await options.ConfigurationManager.GetConfigurationAsync(CancellationToken.None);
  var signingKeys = discoveryDocument.SigningKeys;
  var validationParameters = new TokenValidationParameters {
    RequireExpirationTime = true,
    RequireSignedTokens = true,
    ValidateIssuer = true,
    ValidIssuer = options.Authority,
    ValidateIssuerSigningKey = true,
    IssuerSigningKeys = signingKeys,
    ValidateLifetime = true,
    ValidateAudience = true,
    ValidAudience = "your audience",
    ValidateActor = false,
    ValidTypes = new String[] { "at+jwt" },
    ClockSkew = TimeSpan.FromMinutes(2),
  };
  return validationParameters;
}

}

我不高兴我不得不这样做,尽管我认为它做得很好。要检索我正在使用的访问令牌nuget package IdentityModel.AspNetCore, Version=3.0.0.0

我不明白为什么没有更多的人有这个问题。当然,如果您的应用程序使用来自您传递的访问令牌的API的数据,则访问令牌将成为声明主体。但是,如果您的MVC应用程序执行直接数据库访问(并且可能稍后被提取到API中),您需要以某种方式检查访问令牌的声明。也许我们有一些概念上的误解...

关于配置文件服务。我认为尝试将访问令牌声明包括到身份令牌中不是正确的方法。我认为这甚至是不可能的,因为当为标识令牌调用服务时,您没有关于请求的作用域的信息。

这篇关于如何将作用域的值映射到身份声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆