如何在 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,没有任何有用的信息。
更多上下文:
- 我可以验证签名的
id_token
就好了。只有在尝试使用加密的 access_token
时,我 运行 才遇到麻烦
- 我在正确的地方调用了
app.UseAuthentication()
,并且 [Authorize(AuthenticationScheme="something")]
属性是正确的。
- 我正在使用来自专用 openiddict nuget 提要的
OpenIddict.AspNetCore
包版本 3.0.0。
我相当确定它只是归结为无法在 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());
});
我正在尝试实现基于 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,没有任何有用的信息。
更多上下文:
- 我可以验证签名的
id_token
就好了。只有在尝试使用加密的access_token
时,我 运行 才遇到麻烦 - 我在正确的地方调用了
app.UseAuthentication()
,并且[Authorize(AuthenticationScheme="something")]
属性是正确的。 - 我正在使用来自专用 openiddict nuget 提要的
OpenIddict.AspNetCore
包版本 3.0.0。
我相当确定它只是归结为无法在 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());
});