【问题标题】:Access token doesn't contain 'sub' claim in access token after call to RequestClientCredentialsTokenAsync调用 RequestClientCredentialsTokenAsync 后,访问令牌在访问令牌中不包含“子”声明
【发布时间】:2019-06-21 18:18:17
【问题描述】:

我已经使用以下三个资产设置了一个解决方案:

  • 一个 ASP.NET MVC 核心应用程序
  • 一个 ASP.NET Core Web API
  • IdentityServer4 主机

目前,身份验证有效:如果我尝试访问 MVC 应用程序中的受保护资源,它会重定向到 IdentityServer4,然后我可以使用我的 Facebook 帐户登录,配置用户并生成主题 ID,然后我m 重定向回 MVC 应用程序。这可以正常工作。

我还可以从 MVC 站点中调用受保护的 Web API 函数。但是,在我通过 Web API 收到的声明中,我没有收到主题 ID。

如果我进一步调查,我可以看到在我的 MVC 应用程序中,我从 RequestClientCredentialsTokenAsync 调用中取回了一个访问令牌,但它只包含:

{
  "nbf": 1548693531,
  "exp": 1548697131,
  "iss": "http://localhost:5000",
  "aud": [
    "http://localhost:5000/resources",
    "mgpApi"
  ],
  "client_id": "mgpPortal",
  "scope": [
    "mgpApi"
  ]
}

我希望在此访问令牌中也收到主题 ID,以便我在调用的 Web API 函数中也有它。

我的 IdentityServer4 主机配置为:

public void ConfigureServices(IServiceCollection services)
{
    var builder = services.AddIdentityServer()
            .AddInMemoryIdentityResources(Resources.GetIdentityResources())
            .AddInMemoryApiResources(Resources.GetApiResources())                
            .AddProfileService<ProfileService>()
            .AddInMemoryClients(Clients.Get());
}

...我正在注册一个客户:

new Client
{
    EnableLocalLogin = false,

    ClientId = "mgpPortal",
    ClientName = "MGP Portal Site",
    AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials,

    // where to redirect to after login
    RedirectUris = { "http://localhost:5002/signin-oidc" },

    // where to redirect to after logout
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    // secret for authentication
    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "mgpApi"
    },

    AllowOfflineAccess = true,
    AlwaysIncludeUserClaimsInIdToken = true,
    AlwaysSendClientClaims = true,
}

...作为测试,我还尝试使用 ProfileService 添加声明,例如:

public class ProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.IssuedClaims.AddRange(context.Subject.Claims);
        context.IssuedClaims.Add(new Claim("sub", "test"));

        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        return Task.FromResult(0);
    }
}

但这并没有什么区别。

我的 MVC 客户端配置有:

public void ConfigureServices(IServiceCollection services)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";                 
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {                
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;                
        options.ClientId = "mgpPortal";
        options.SaveTokens = true;                
    });           
}

那么我应该添加什么才能在访问令牌中接收主题 ID?

如果需要更多信息,请告诉我。任何帮助表示赞赏!

【问题讨论】:

  • 我认为您使用了错误的流程,请使用混合流程。在客户端凭据中没有用户主题的概念,因此您遇到了问题。
  • 谢谢你,我会调查你的提议!
  • 是的,即使只是隐式授权也足以满足您的工作需要,因为如果您在不涉及用户上下文的情况下代表 mvc 应用程序执行一些 api 调用,则只需要隐式和客户端凭据。
  • @Vidmantas Blazevicius 你的建议救了我!我确实错过了各种赠款类型的含义。所以我读到了它们,事实上,混合在我的情况下是有意义的。所以我切换到混合流,现在它工作得很好!谢谢!!
  • 非常欢迎您,我将其作为答案发布,以便其他人遇到相同问题时可以更轻松地找到它。

标签: asp.net-core asp.net-core-mvc identityserver4 asp.net-core-webapi


【解决方案1】:

您使用了错误的流程,请使用hybrid 流程。在client credentials 中没有用户主题的概念,因此您遇到了问题。

【讨论】:

    【解决方案2】:

    我也遇到过类似的情况。经过大量证明后,我发现使用 MVC 应用程序用户的相同令牌访问不同的 Web Api 应用程序非常容易。解释为here

    public async Task<IActionResult> CallApi()
    {
        var accessToken = await HttpContext.GetTokenAsync("access_token");
    
        var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        ...
    }
    

    这里使用IdentityModel的另一个版本:

    public async Task<IActionResult> CallApi()
    {
      var accessToken = await HttpContext.GetTokenAsync("access_token");
      var apiClient = new HttpClient();
      client.SetBearerToken(accessToken); 
      ....
      await client.PutAsync(url, json);   
    

    希望对您有所帮助...

    【讨论】:

      猜你喜欢
      • 2021-12-27
      • 2017-03-23
      • 2021-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-13
      • 2020-11-05
      • 1970-01-01
      相关资源
      最近更新 更多