如何在API中正确验证OpenIddict JWT access_token?

wjm*_*m03 5 jwt asp.net-core openiddict

我正在尝试实现一个基于 OpenIddict 的身份服务器。我们的用例是一个大型 JavaScript 应用程序,需要对多个后端 API 进行用户身份验证。JavaScript 应用程序使用密码流从专用 OpenIddict 服务器获取令牌。然后,前端调用的各种 API 会验证令牌。

我已经使用 ASP.NET Identity 和 EF 实现了服务器,并且可以成功检索有效令牌。我们的 API 在 AWS Lambda 中运行,因此我们不能(或不想)使用标准.AddDataProtection方法。我们使用存储在 S3 中的自签名证书来生成令牌并验证它们。

那么问题是,当我将 access_token 发送到后端 API 时,它们无法验证令牌并提供访问权限。我知道令牌是有效的,因为我可以使用JwtSecurityTokenHandler自签名证书在 Linqpad 中手动解密它们。

这是我的服务器配置:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.ClaimsIdentity.UserNameClaimType = Claims.Name;
                options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
                options.ClaimsIdentity.RoleClaimType = Claims.Role;
            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

            services.AddOpenIddict()
            .AddCore(options =>
            {
                options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
            })
            .AddServer(options =>
            {
                options.SetTokenEndpointUris("/connect/token");
                options.AllowPasswordFlow();
                options.AcceptAnonymousClients();
                options.AddEncryptionCertificate(MyCustomCertificate);
                options.AddSigningCertificate(MyOtherCustomCertificate);
                options.UseAspNetCore().EnableTokenEndpointPassthrough();
            });

            services.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseSqlServer(connectionString);
                options.UseOpenIddict();
            });

        }
Run Code Online (Sandbox Code Playgroud)

我在 API 中尝试了两种不同的配置,但都不起作用。选项 1(首选),使用内置 OpenIddict 令牌验证:

        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers();

            services.AddOpenIddict().AddValidation(
                options =>
                {
                    options.SetTokenValidationParameters(config =>
                    {
                        config.ValidateAudience = false; //just to make sure it's not a typo causing the problem
                        config.ValidateIssuer = false; //just to make sure it's not a typo causing the problem
                        config.TokenDecryptionKey = new X509SecurityKey(MyCustomCertificate);
                        config.IssuerSigningKey = new X509SecurityKey(MyOtherCustomCertificate);
                    });

                    options.UseAspNetCore();
                });

        }
Run Code Online (Sandbox Code Playgroud)

{"error":"invalid_token","error_description":"The specified token is not valid."}即使我可以手动解密和验证令牌,这总是会导致。我尝试将默认日志级别设置为“Trace”,但日志中没有显示任何其他内容可以解释问题所在。

选项 2,使用内置 .NET 令牌验证:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = "https://openiddictserver";
                    options.Audience = "clientid"; 
                    options.RequireHttpsMetadata = false;
                    options.IncludeErrorDetails = true; 
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        NameClaimType = OpenIdConnectConstants.Claims.Subject,
                        RoleClaimType = OpenIdConnectConstants.Claims.Role,
                        ValidateAudience = false, //just to make sure it's not a typo problem
                        ValidateIssuer = false, //just to make sure it's not a typo problem
                        TokenDecryptionKey = new X509SecurityKey(MyCustomCertificate),
                        IssuerSigningKey = new X509SecurityKey(MyOtherCustomCertificate)
                    };
                });
Run Code Online (Sandbox Code Playgroud)

选项 2 只会导致 401 错误,没有任何有用的信息。

更多背景信息:

  • id_token我可以很好地验证签名。只有在尝试使用加密时access_token我才会遇到麻烦
  • app.UseAuthentication()在正确的位置调用并且[Authorize(AuthenticationScheme="something")]属性是正确的。
  • 我使用的是OpenIddict.AspNetCore专用 openiddict nuget feed 中的软件包版本 3.0.0。

我相当确定这只是由于无法access_token在 API 中以正确的方式解密。

Kév*_*let 4

您看到的行为是由 OpenIddict 验证处理程序的 alpha 位限制引起的,该处理程序检查是否TokenValidationParameters.IssuerSigningKeys为 null,而不是TokenValidationParameters.IssuerSigningKey

要解决这个问题,您可以使用:

config.IssuerSigningKeys = new[] { new X509SecurityKey(MyOtherCustomCertificate) };
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用 discovery 来允许它从授权服务器下载签名密钥:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.SetIssuer(new Uri("https://localhost:44365/"));
        options.AddEncryptionCertificate(AuthenticationExtensionMethods.TokenEncryptionCertificate());

        options.UseAspNetCore();
        options.UseSystemNetHttp();
    });
Run Code Online (Sandbox Code Playgroud)

值得注意的是,该options.SetTokenValidationParameters()方法很快就会被删除(作为内省支持添加的一部分)。注册静态 OIDC 配置的新语法将类似于:

services.AddOpenIddict()
    .AddValidation(options =>
    {
        options.SetConfiguration(new OpenIdConnectConfiguration
        {
            Issuer = "https://localhost:44365/",
            SigningKeys = { new X509SecurityKey(AuthenticationExtensionMethods.TokenSigningCertificate()) }
        });

        options.AddEncryptionCertificate(AuthenticationExtensionMethods.TokenEncryptionCertificate());
    });
Run Code Online (Sandbox Code Playgroud)

  • AuthenticationExtensionMethods 类是你的还是某个 nuget 包? (2认同)