【问题标题】:ASP.NET Web Api not accepting own Openiddict JWTASP.NET Web Api 不接受自己的 Openiddict JWT
【发布时间】:2021-04-19 09:08:36
【问题描述】:

我正在使用 Web API 项目在 ASP.NET Core 中学习 OpenID Connect 实现。我的客户目前是 Postman。

上下文(XY问题): 我希望 Sendgrid 通过身份验证报告 Webhook 数据。 Sendgrid 使用 OAuth 2 流。我在 Postman 上模拟了一个 Sendgrid Webhook 调用。

我按照afew教程设置授权服务器,即。将向您发出令牌的部分,特别是使用基于 EF Core 的临时内存存储。目前,这个解决方案对我来说已经足够了,我必须做更多的研究和原型设计,然后才能成为生产级以便在未来的项目中重复使用。

我可以使用 Postman 使用硬编码凭据成功获取令牌。现在我希望控制器 API 验证由同一台服务器发出的令牌。让我展示一些代码:

Startup.cs

   public void ConfigureServices(IServiceCollection services)
    {
        ....
        services.AddControllers();
        ....
  
        services.AddDbContext<OpenIddictDbContext>(ef => //OpenIddictDbContext contains no set, I want the framework to handle its own entities

             // Configure the context to use an in-memory store.
             ef.UseInMemoryDatabase(nameof(OpenIddictDbContext))

             // Register the entity sets needed by OpenIddict.
             .UseOpenIddict()
        );

        services.AddOpenIddict(options =>
            options.AddServer(server => server.AddEphemeralEncryptionKey()
                    .AddEphemeralSigningKey() //Not production-grade but I'll deal with this in the future
                    .RegisterScopes("api")
                    .SetTokenEndpointUris("/api/v1/Auth/token")
                    .SetAuthorizationEndpointUris("/api/v1/Auth/authorize")
                    .AllowClientCredentialsFlow()
                    .UseAspNetCore()
                    .EnableTokenEndpointPassthrough())
                .AddCore(core => core.UseEntityFrameworkCore()
                                  .UseDbContext<OpenIddictDbContext>())
                .AddValidation(validation => validation.UseLocalServer()))
            .AddHostedService<OpenIddictHostedService>() //Used only to create the tech-user for client, I prefer not to paste as it's mostly identical to linked tutorial
            .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(); //Security hardening (i.e. trusted authorities) TBD in the next future

}

AuthController.cs(路径相同)

    [HttpPost("token"), Produces("application/json")]
    public async Task<IActionResult> Token(CancellationToken cancellationToken = default)
    {
        var request = HttpContext.GetOpenIddictServerRequest();
        if (!request.IsClientCredentialsGrantType())
        {
            throw new NotImplementedException("The specified grant is not implemented.");
        }

        // Note: the client credentials are automatically validated by OpenIddict:
        // if client_id or client_secret are invalid, this action won't be invoked.

        var application =
            await _applicationManager.FindByClientIdAsync(request.ClientId, cancellationToken) ??
            throw new InvalidOperationException("The application cannot be found.");

        // Create a new ClaimsIdentity containing the claims that
        // will be used to create an id_token, a token or a code.
        var identity = new ClaimsIdentity(
            TokenValidationParameters.DefaultAuthenticationType,
            OpenIddictConstants.Claims.Name, OpenIddictConstants.Claims.Role);

        // Use the client_id as the subject identifier.
        identity.AddClaim(OpenIddictConstants.Claims.Subject,
            await _applicationManager.GetClientIdAsync(application, cancellationToken),
            OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken);

        identity.AddClaim(OpenIddictConstants.Claims.Name,
            await _applicationManager.GetDisplayNameAsync(application, cancellationToken),
            OpenIddictConstants.Destinations.AccessToken, OpenIddictConstants.Destinations.IdentityToken);

        return SignIn(new ClaimsPrincipal(identity),
            OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
    }

我的代码与教程中显示的代码不同,因为并非所有方法都适用于 OpenIddict 3.0,也许文档需要更新。

此时,我可以通过 Postman 获取令牌:take and eat; this is my ephemeral token

eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJWMFZaS1pGR1c0WTJGUUpRNTNMQktaVktXSzZKS1pXV1lDMVREQUpDIiwidHlwIjoiYXQrand0In0.NNnSRN18drc3hj8PXon7bz7SyOpmhBds1fOQVnKZzD9dNSeFOQpc5MNJxkxcSSb3Z-XGGm-2z8Fw03yYJPBn2KkE9zz2udUpWNsWmToiiDOYd_5WVmm3GpZopWAusM-YhzPuavnxY1BFVsIylJs9yX0_jXs1RGIIhNvkGG_AmCBVnPdoDvu41CthRsyJc1IQCX5HuHO2XpvmnaRiVJZyaDbey4pDAs-4Fy-yDosg9w8szUWGcw-Y7e4xYKi6HqmjgDDqJQ14QDW839BOkctdRUrk3GhT7HDZN5Oaq8itPTLoxBrLgG9BE2yqfLKJibWD5MNSpj9OQu8GY-uBFsmt2A.YeBSpHgDu4291YJE-jWhOQ.WjxqjTXykAfFk7NRjXTMjPVsUFOtKK7fJxiLy0T7xiUnPSHIAI9m3UlkmsmQuK7sdxYtZnJ2gw-iJxcd1q1UYYRz5N82bc97py6bTH5cykWwMddCRMLnOFrOqKgTS8cZdgxfPwiz46SZKHYrxOtTtSU7Jy0YtNNYTBt47TXTMAjFwm81QrVmpPwEdt_wodaar7b23Wlw2SYbparw-hTdY-uaitL3olnCDzIn9Jwc7Zkqz8PwNxLYRPoUz1gpKJdO6c-azHcvYI8yY4Ty0OLdUuAZRGVwM4CRETW-Wixig37Sf6meXohJ1aS2Xe6LWAY5Db-Nyfs1H3D6tXL0hzI-q82xZO2oFNcj-yumYL14FoPlxl4TORgSh3L15rBY7e1VxzoJRPYuXAZUCOHrrdAF_DMnM7TSqe52ckMy2_aKNRsFaLZF8brwi4IDnXpe778Nw-ujTQ-djCPTZ-2gI1BP2h0NbpoSSKwI2_9eeUk_IsF5hq54En9oaQpNmz-7oPUAEa4GBmCeowrvzrtMKGMwPCsaOtYAJRnd63Y3YbTAe-NnGTH0EGvBga_9ElwDWswa-UR8CqeMLCqmDKOq9ryYbL4LWSHp7rmsykd2oHqzu_If8c5wzbLQBp3m-bt3w2EaYROXpLGlNvzVLla78Okwe9jFy8QeGVj1pCU8UsgwWCRzlwY4idV0SBSy8dPbEzCunLGbZgx2W81qXPLxWIybTGuOKyBvNffWXhnriUTjsL5khHrfArtjdbsoP93Ig0nqgOGiNXh82RVAuVVmLqOs6yohxLWUbDlxkiophBCmf8RD8h0Eak1zjmzQGTOS9D2BpWouZuEyZOHftnl07SmhXvZXLdw_gSZCoka4EY3FcIxb-9rw53wQAjn-00JLaMbIEaO79fZGJJytiNTagJJouVmvTCh-luNhJ2TULGC5nTb_szyY9yfvkfjK_tP7WIbW._42bMf01NOZ9tXyalK-ONdrPBDPqq-jL-TjWA2aHSuo

但每次我尝试调用带有 [Authorize] 注释的控制器时,我都会得到带有标题 WWW-Authenticate: Bearer error="invalid_token" 的 401。

我认为这可能与services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer() 有关,但尝试使用OpenIddictServerAspNetCoreDefaults.AuthenticationScheme 而不是JwtBearerDefaults.AuthenticationScheme 会导致以下错误

The OpenIddict ASP.NET Core server handler cannot be used as the default scheme handler.
Make sure that neither DefaultAuthenticateScheme, DefaultChallengeScheme, DefaultForbidScheme, DefaultSignInScheme, DefaultSignOutScheme nor DefaultScheme point to an instance of the OpenIddict ASP.NET Core server handler.

问题

如何使用同一服务器使用 Openiddict 颁发的令牌正确调用 AuthorizeAttribute-protected 方法?

请注意,我没有找到有关身份验证错误的日志。正确提高日志级别可能是有效的第一步调查

理论

根据我对 OIDC 的了解(我的论文是关于 OAuth 2 的,所以我可以声称有能力)和对代码的研究,这就是我可能缺少的。

我已经成功实现了 OIDC 周期的授权服务器部分。 IE。使用上面的代码,我终于有了一个验证客户端凭据并调用SignIn 的端点。据我了解,ASP.NET Core 的 SignIn 方法假定调用代码已经通过匹配客户端 ID 和客户端密码对用户进行身份验证。

据我了解,ASP.NET Core 不再关心身份验证,SignIn 发布了一个令牌,稍后将在 资源服务器 周期中使用该令牌来验证进一步的调用,就像 Sendgrid 将 Webhooks 发送给我一样。

所以在这种情况下,我应该努力正确地利用authenticationScheme 参数。

在非 API 场景中(或者如果资源使用者曾经支持过)我可以使用 cookie,以便SignIn 将 cookie 释放到 HttpResponse 对象。

或者在这种情况下,我可以找到正确的方法来集成 Openiddict 的 资源服务器 流,或者利用 不同的 身份验证器,如 ASP.NET Core 的嵌入式 JWT。

对于后者,SignIn 可以充当委托人。 “嘿,JWT Provider 先生,我在这里成功验证了 Sendrig,他正在敲我们的门调用我们的身份验证 API。您介意发出一个您将来会接受的有效令牌吗?”。

使用失败

 return SignIn(new ClaimsPrincipal(identity),
           // OpenIddictServerAspNetCoreDefaults.AuthenticationScheme
           JwtBearerDefaults.AuthenticationScheme
            );

但是上面的代码应该告诉 JWT Provider 为声明的身份释放一个令牌。

【问题讨论】:

  • 您好,对于您的 401 错误,请务必添加 UseAuthentication 中间件,并且必须按顺序调用 UseRoutingUseAuthenticationUseAuthorizationUseEndpoints
  • @Rena 谢谢,已经这样了
  • 您确定令牌有效并受 API 支持吗?我试图在这里复制你的令牌,但它抱怨奇怪的编码jwt.io
  • 我从 Postman 复制并粘贴了它。请注意,它应该是加密的。明天我会尝试 ro 关闭加密。
  • 我已经从理论角度回答了我自己的问题,并且对于那些不习惯身份验证系统的人来说,措辞要简单得多。我再试一次。是时候从理论转变为“让它发挥作用”了

标签: asp.net-core openid-connect openiddict


【解决方案1】:

关键是要添加正确的身份验证方案

services
.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)

来自example

完整的代码片段

        services.AddOpenIddict(options =>
                options.AddServer(server => server.AddEphemeralEncryptionKey()
                        .AddEphemeralSigningKey()
                        .RegisterScopes("api")
                        .SetTokenEndpointUris("/api/v1/Auth/token")
                        .SetAuthorizationEndpointUris("/api/v1/Auth/authorize")
                        .AllowClientCredentialsFlow()
                        .UseAspNetCore()
                        .EnableTokenEndpointPassthrough())
                    .AddCore(core => core.UseEntityFrameworkCore()
                        .UseDbContext<OpenIddictDbContext>())
                    .AddValidation(validation =>
                        validation.UseLocalServer(_ => { })
                            .UseAspNetCore(_ => { })
                    )
            )
            .AddHostedService<OpenIddictHostedService>()
            .AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)
            ;

这也需要我添加 Openiddict.Validation.AspNetCore 作为项目依赖

【讨论】:

    猜你喜欢
    • 2018-01-09
    • 2015-12-13
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 2017-08-08
    • 2017-03-09
    相关资源
    最近更新 更多