nba*_*man 4 jwt amazon-cognito .net-core jwk asp.net-core-webapi
我在弄清楚如何验证.NET Core Web API中由AWS Cognito提供给客户端的JWT时遇到了一些麻烦。
我不仅无法弄清楚Microsoft.IdentityModel.Tokens.TokenValidationParameters应该使用的变量是什么,而且一旦我终于知道了,我不知道如何从中检索JWT密钥集。https://cognito-idp.{region}.amazonaws.com/{pool ID}/.well-known/jwks.json
最后,尽管经过了很多随机的Google搜索和反复试验,我还是找到了一个(看似不是很有效的解决方案)解决方案。但是,我花了太多时间来做。出于这一点,再加上严重缺乏有关该主题的AWS文档这一事实,我决定发布此问答,以帮助其他人将来更轻松地找到此解决方案。
如果有更好的方法,请告诉我,因为除了下面列出的答案外,我还没有找到其他方法。
nba*_*man 11
答案主要在于正确定义TokenValidationParameters.IssuerSigningKeyResolver(此处显示的参数等:https : //docs.microsoft.com/zh-cn/dotnet/api/microsoft.identitymodel.tokens.issuersigningkeyresolver?view=azure-dotnet)。
这就是告诉.NET Core如何验证发送JWT的依据。还必须告诉它在哪里可以找到密钥列表。人们不一定必须对密钥集进行硬编码,因为它通常由AWS旋转。
一种方法是从IssuerSigningKeyResolver方法内部的URL中获取列表并进行序列化。整体.AddJwtBearer()可能看起来像这样:
Startup.cs ConfigureServices()方法:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
// get JsonWebKeySet from AWS
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
// serialize the result
var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
// cast the result to be the type expected by IssuerSigningKeyResolver
return (IEnumerable<SecurityKey>)keys;
},
ValidIssuer = "https://cognito-idp.{region}.amazonaws.com/{pool ID}",
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = "{Cognito AppClientID}",
ValidateAudience = true
};
});
Run Code Online (Sandbox Code Playgroud)
如果您使用JS库,例如AWS放大,你可以看到参数,如ValidIssuer和ValidAudience在浏览器的控制台通过观察结果Auth.currentSession()
利用上面实现的JWT身份验证以及使用[Authorize]控制器上的标记,从JS客户端到.NET Core Web API的REST获取请求可能看起来像这样:
使用@ aws-amplify / auth节点包的JS客户端:
// get the current logged in user's info
Auth.currentSession().then((user) => {
fetch('https://localhost:5001/api/values',
{
method: 'GET',
headers: {
// get the user's JWT token given to it by AWS cognito
'Authorization': `Bearer ${user.getIdToken().getJwtToken()}`,
'Content-Type': 'application/json'
}
}
).then(response => response.json())
.then(data => console.log(data))
.catch(e => console.error(e))
})
Run Code Online (Sandbox Code Playgroud)
jsp*_*lla 10
这无疑是我去年处理过的最困难的代码。“在 .NET Web API 应用程序中验证来自 AWS Cognito 的 JWT 令牌”。AWS 文档仍有很多不足之处。
这是我用于新的.NET 6 Web API 解决方案的内容(因此 Startup.cs 现在包含在 Program.cs 中。如果需要,请调整以适合您的 .NET 版本。与 .NET 5 及更早版本的主要区别是Services访问对象通过一个名为 的变量builder,因此每当您看到类似 的代码时services.SomeMethod...,您都可以将其替换为builder.Services.SomeMethod...使其与 .NET 6 兼容):
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://cognito-idp.{aws region here}.amazonaws.com/{Cognito UserPoolId here}",
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = "{Cognito AppClientId here}",
ValidateAudience = false
};
options.MetadataAddress = "https://cognito-idp.{aws region here}.amazonaws.com/{Cognito UserPoolId here}/.well-known/openid-configuration";
});
Run Code Online (Sandbox Code Playgroud)
请注意,我已ValidateAudience设置为false. 否则,我会从 .NET 应用程序收到 401 未经授权的响应。SO 上的其他人表示他们必须这样做才能使 OAuth 的身份验证/身份验证代码授予类型正常工作。显然,ValidateAudience = true对于隐式授予来说效果很好,但是大多数人认为隐式授予不推荐使用,如果可能的话,您应该尽量避免它。
另请注意,我正在设置options.MetadataAddress. 根据另一个 SO 用户的说法,这显然允许在幕后缓存来自 AWS 的签名密钥,这些密钥是他们不时轮换的。
builder.Services.AddCognitoIdentity();我被一些让我使用(针对.NET 5及更早版本)的官方AWS文档引入了歧途(嘘)services.AddCognitoIdentity();。显然,这是针对后端为前端提供服务的“ASP.NET”应用程序(例如 Razor/Blazor)。或者也许它已被弃用,谁知道呢。它位于 AWS 的网站上,因此很可能会被弃用......
至于控制器,[Authorize]类级别的简单属性就足够了。无需在AuthenticationScheme属性中指定“Bearer” [Authorize],或创建中间件。
如果您想跳过向每个控制器添加另一个using以及[Authorize]属性的过程,并且希望每个控制器中的每个端点都需要 JWT,则可以将其放入 Startup/Program.cs 中:
builder.Services.AddControllers(opt =>
{
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
opt.Filters.Add(new AuthorizeFilter(policy));
});
Run Code Online (Sandbox Code Playgroud)
确保 Program.cs(.NET 5 及更早版本的 Startup.cs)中的app.UseAuthentication位于 之前app.UseAuthorization()。
以下是usingProgram.cs/Startup.cs 中的 s:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.IdentityModel.Tokens;
Run Code Online (Sandbox Code Playgroud)
仅当您需要对验证进行更细粒度的控制时才需要此处提供的答案。
否则,以下代码足以验证 jwt。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "{yourAuthorizationServerAddress}";
options.Audience = "{yourAudience}";
});
Run Code Online (Sandbox Code Playgroud)
Okta 在这方面有一篇很好的文章。https://developer.okta.com/blog/2018/03/23/token-authentication-aspnetcore-complete-guide
当 JwtBearer 中间件第一次处理请求时,它会尝试从授权服务器(也称为授权或颁发者)检索一些元数据。此元数据或 OpenID Connect 术语中的发现文档包含验证令牌所需的公钥和其他详细信息。(想知道元数据是什么样的?这是一个示例发现文档。)
如果 JwtBearer 中间件找到这个元数据文档,它会自动配置自己。相当漂亮!
| 归档时间: |
|
| 查看次数: |
1816 次 |
| 最近记录: |