无法使用IdentityServer4在API中验证JWT或access_token

时间:2019-03-18 00:29:41

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

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

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

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

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 OPTIONS http://localhost:5001/api/values信息:   Microsoft.AspNetCore.Hosting.Internal.WebHost [1]         请求启动HTTP / 1.1 OPTIONS 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.1352ms 204信息:Microsoft.AspNetCore.Hosting.Internal.WebHost [2]         请求完成于61.1272ms 204信息:Microsoft.AspNetCore.Hosting.Internal.WebHost [1]         请求启动HTTP / 1.1 GET http://localhost:5001/api/values应用程序/ json信息:   Microsoft.AspNetCore.Hosting.Internal.WebHost [1]         请求启动HTTP / 1.1 GET http://localhost:5001/api/values应用程序/ 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.8827毫秒中的信息:   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.6677ms 401信息:Microsoft.AspNetCore.Hosting.Internal.WebHost [2]         请求在473.7648ms内完成401

将日志记录设置为 Trace 并没有发现其他任何内容。

我确实尝试去邮差看我是否可以手动对令牌进行自省。我将授权设置为基本,并将用户名设置为“ test-api”,api ID和我在其客户端机密中设置的密码。在主体中,将其设置为x-www-form-urlencoded,并使用键“ token”和值access_token。我得到200 OK的索赔以及“ active:true”。似乎应该不错,对吧?

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

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

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

编辑:RE access_token和id_token

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

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

访问令牌的音频为:“ http:/// resources” id_token的声音为:“ spa-client”

1 个答案:

答案 0 :(得分:2)

  
    

access_token和id_token都相同,并且都是使用RS256作为签名方法的JWT。

  

access_token和id_token不应该相同。对于id_token,令牌中的aud声明应该是客户端的名称,但是在访问令牌中,您的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以获得代码流代码示例。

更新:

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

enter image description here

您可以单击here以获得代码示例。