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

How to properly validate OpenIddict JWT access_token in API?

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

我已经使用 ASP.NET Identity 和 EF 实现了服务器,并且可以成功检索到有效令牌。我们的 APIs 在 AWS Lambda 中是 运行ning,所以我们不能(或不想)使用标准的 .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();
            });

        }

我在 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();
                });

        }

这总是会导致 {"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)
                    };
                });

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

更多上下文:

我相当确定它只是归结为无法在 API 中以正确的方式解密 access_token

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

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

config.IssuerSigningKeys = new[] { new X509SecurityKey(MyOtherCustomCertificate) };

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

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

        options.UseAspNetCore();
        options.UseSystemNetHttp();
    });

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

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

        options.AddEncryptionCertificate(AuthenticationExtensionMethods.TokenEncryptionCertificate());
    });