【问题标题】:Unable to Validate JWT or access_token in API with IdentityServer4无法使用 IdentityServer4 验证 API 中的 JWT 或 access_token
【发布时间】:2019-03-19 09:37:04
【问题描述】:

我正在努力在我们拥有现有系统的情况下构建一些新功能。我们的目标是让不同的 Angular SPA 与 API 进行通信,从而为我们的旧版应用程序提供一个新界面。一个团队已设置 IdentityServer4 以使用旧应用程序进行凭据验证。

使用 Angular 7 编写的 SPA 使用隐式流程进行身份验证。 响应类型是“token id_token”我已经能够让这个流程工作,因为我可以让未经身份验证的用户来到站点,被踢到 IdentityServer 身份验证屏幕,登录正确的凭据,然后重定向回应用程序。当我取回令牌时,声明对象具有 aud 的单个值,即 SPA 的 client_id,例如 spa-client。 access_token 和 id_token 都是以 RS256 作为签名方式的 JWT。最后,我在 SPA 中创建了一个拦截器来发送 JWT(实际上它只是使用作为 access_token 发回的任何值)作为 API 请求的“Bearer”令牌。

我现在创建了一个 .NET Core 2.2 WebApi 应用程序来构建一个服务 API 以供 SPA 使用。此应用程序使用 IdentityServer4.AccessTokenValidation 包来“保护”API 我已经阅读了 IdentityServer4 文档中的部分。在我的 ConfigureServices 方法中,我编写了以下内容来配置身份验证: p>

services    
    .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
    .AddIdentityServerAuthentication(options =>
    {
        if (string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.Authority)) throw new ConfigurationException("", nameof(serviceConfiguration.Authentication.Authority), "An authority must be defined.");
        if (string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.ApiName)) throw new ConfigurationException("", nameof(serviceConfiguration.Authentication.ApiName), "An api name must be defined.");

        options.Authority = serviceConfiguration.Authentication.Authority;
        options.ApiName = serviceConfiguration.Authentication.ApiName;

        if (!string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.ApiSecret))
        {
            options.ApiSecret = serviceConfiguration.Authentication.ApiSecret;
        }

        options.RequireHttpsMetadata = false;
        options.SupportedTokens = SupportedTokens.Both;
    }

serviceConfiguration 是我的 JSON appsettings.json 的强类型表示

我的理解是options.SupportedTokens = SupportedTokens.Both会检查JWT并进行自省。

现在所有这些都已设置好,我采用了由 API 模板创建的简单示例 ValuesController,并为其添加了 [Authorize] 属性。正如我所料,在 Postman 中对该端点执行一个简单的 GET 请求会返回 401。

接下来我尝试登录 SPA 并转到一个测试页面,该页面将执行相同的操作,但拦截器将拥有不记名令牌、access_token 或基本上是 JWT。我仍然从服务中收到 401。

查看服务输出的日志,我没有看到任何有用的具体内容:

信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 选项http://localhost:5001/api/values 信息: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 选项http://localhost:5001/api/values 信息: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] CORS 策略执行成功。信息:Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] CORS 策略执行成功。信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 61.1352 毫秒内完成 204 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 61.1272 毫秒内完成 204 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 GET http://localhost:5001/api/values application/json 信息: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 GET http://localhost:5001/api/values application/json 信息: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] CORS 策略执行成功。信息:Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4] CORS 策略执行成功。信息:Microsoft.AspNetCore.Routing.EndpointMiddleware[0] 执行端点'Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)'信息: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] 执行端点'Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)'信息: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] 与 {action = "Get", controller = "Values"} 匹配的路由。执行动作 示例.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)信息: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1] 与 {action = "Get", controller = "Values"} 匹配的路由。执行动作 示例.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)信息: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 授权失败。信息:Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 授权失败。信息:Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] 过滤器“Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter”处的请求授权失败。信息: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3] 过滤器“Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter”处的请求授权失败。信息: Microsoft.AspNetCore.Mvc.ChallengeResult[1] 使用身份验证方案 () 执行 ChallengeResult。信息:Microsoft.AspNetCore.Mvc.ChallengeResult[1] 使用身份验证方案 () 执行 ChallengeResult。信息:IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[12] AuthenticationScheme:承载被挑战。信息:IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[12] AuthenticationScheme:承载被挑战。信息:Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 执行的动作 Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web) 在 166.3038ms 信息: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2] 执行的动作 Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web) 在 162.8827ms 信息: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] 执行端点'Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)'信息: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] 执行端点'Examples.TestApp.API.Web.Controllers.ValuesController.Get (Examples.TestApp.API.Web)'信息: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 459.6677 毫秒内完成 401 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 473.7648 毫秒内完成 401

将日志记录设置为 Trace 并没有透露任何其他信息。

我确实尝试过去 Postman 看看我是否可以手动对令牌进行自省。我将授权设置为基本并将用户名设置为“test-api”、api id 以及我在其客户端密码中设置的密码。在正文中,将其设置为 x-www-form-urlencoded,键为“token”,值为 access_token。我得到了 200 OK 的声明以及“活动:真实”。看起来应该不错吧?

我不拥有 IdentityServer 实现,所以除了能够登录到管理 UI 之外,我不太确定最后发生了什么。我已登录身份服务器的管理 UI,并仔细检查了客户端和 API 的设置。客户端被授予“api”范围,API 获得相同的范围“api”。这显示在自省响应中发回的范围以及我从客户端获得的令牌中。

我也尝试过设置options.SupportedTokens = SupportedTokens.Referenceoptions.SupportedTokens = SupportedTokens.JWT。两者都没有任何变化。 :(

我做错了什么?这是配置问题还是我是个白痴(可能是后者)。

编辑:RE access_token 和 id_token

我在原始问题中不正确。标记不同(我需要查看的不仅仅是标题部分)。

观众很奇怪。解码时我从未注意这里可能存在的差异。我的感知力一定很烂!

访问令牌有 aud: "http:///resources" id_token 有 aud: "spa-client"

【问题讨论】:

    标签: c# authentication asp.net-core identityserver4 openid-connect


    【解决方案1】:

    access_token和id_token都是一样的,都是以RS256为签名方式的JWT。

    access_token 和 id_token 不应该相同。对于 id_token ,令牌中的 audclaim 应该是客户端的名称,但在访问令牌中,您的 api 名称应该包含在 aud 中,以便您的 api 资源可以验证访问令牌的颁发是为了“我的” api 调用。您可以使用 jwt.io 之类的在线工具来解码 JWT 令牌中的声明。由于您还没有发布客户端和 IDS4 的配置,您应该检查响应类型、范围......如果使用 Fiddler 跟踪请求,它应该是:

    GET /connect/authorize/callback?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A5003%2Fcallback.html&response_type=id_token%20token&scope=openid%20profile%20api1&state=xxxxxxxxxxxxxxx&nonce=xxxxxxxxxxxxxxxxxxx
    

    另外,来自文档:http://docs.identityserver.io/en/latest/topics/grant_types.html

    对于基于 JavaScript 的应用程序,不再推荐使用隐式。请改用带有 PKCE 的授权码。

    您可以点击here获取代码流代码示例。

    更新:

    访问令牌的受众还应该包括api名称,除了"http:///resources

    您可以点击here 获取代码示例。

    【讨论】:

    • 从来没有真正看过编码标头部分之外的标记,所以这对我来说是一个糟糕的假设。我用有关此的更多信息更新了问题。 access_token 中的观众看起来很奇怪,我试图弄清楚它是如何获得这个价值的。另外,我了解隐式流程。那不是我的决定 :( 哪些配置值有助于调试?
    • @MikeG,见更新回复,access token 的访问者还应包括api名称,除了“http:///resources:
    • 将您的答案标记为正确,因为您的更新也是一个问题。主要问题是我在Configure 方法中不小心有app.UseAuthentication()app.UseMvc()。 :( :(
    猜你喜欢
    • 2020-09-02
    • 2020-04-22
    • 1970-01-01
    • 2019-04-29
    • 1970-01-01
    • 2022-07-05
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    相关资源
    最近更新 更多