我是 AWS 新手,需要使用 Cognito 保护对 .NET Core Web API 应用程序的访问。我主要关注 Les Jackson 在 YouTube 上发布的精彩视频 (https://www.youtube.com/watch?v=3PyUjOmuFic),但他使用的是 Azure 而不是 AWS。
到目前为止,我有一个非常简单的 API 控制器操作,只需从 SQL Server 中提取一些记录并发送 JSON 响应。
[HttpGet]
public async Task<IEnumerable<TodoItem>> Get()
{
var items = await context.TodoItem.ToListAsync();
return items;
}
这在 Visual Studio 本地运行良好,并且在使用 Elastic Beanstalk 部署到 AWS 实例时也运行良好。
为了保护应用程序,我添加到 Startup.cs 中的 ConfigureServices 方法中
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.Audience = "App client id";
options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/Cognito User Pool id";
});
并且在Configure方法中
app.UseAuthentication();
我向应用程序添加了一个控制器,只是为了通过 Cognito 进行身份验证并取回访问令牌。
private const string _clientId = "App client id";
private readonly RegionEndpoint _region = RegionEndpoint.EUWest2;
[HttpPost]
[Route("/api/signin")]
public async Task<ActionResult<string>> SignIn(User user)
{
var cognito = new AmazonCognitoIdentityProviderClient(_region);
var request = new AdminInitiateAuthRequest
{
UserPoolId = "Cognito User Pool id",
ClientId = _clientId,
AuthFlow = AuthFlowType.ADMIN_USER_PASSWORD_AUTH
};
request.AuthParameters.Add("USERNAME", user.Username);
request.AuthParameters.Add("PASSWORD", user.Password);
var response = await cognito.AdminInitiateAuthAsync(request);
return Ok(response.AuthenticationResult);
}
经过一番混乱之后,我已经在本地和 AWS 上实现了此功能,当使用 Postman 发布 Cognito 用户名和密码时返回令牌,例如
{
"accessToken": ".....",
"expiresIn": 3600,
"idToken": "...",
"newDeviceMetadata": null,
"refreshToken": "...",
"tokenType": "Bearer"
}
因此,我将 [Authorize] 属性添加到我的 API 方法中,使用 SignIn 方法获取访问令牌,并在 Postman 中构造一个请求,其中包含值为“Bearer”+ accessToken 的授权 HTTP 标头。
可悲的是,这不起作用,我想不出还有什么可以尝试的。
通过 Visual Studio 对 IIS Express 中本地运行的应用程序使用 Postman 请求,我得到响应
System.InvalidOperationException: IDX20803: Unable to obtain configuration from: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
---> System.IO.IOException: IDX20807: Unable to retrieve document from: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. HttpResponseMessage: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]', HttpResponseMessage.Content: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
使用 Elastic Beanstalk 部署应用程序时,我只收到 500 内部服务器错误,没有其他消息。
我做错了什么?我将不胜感激任何帮助/指示。
道格
我不太清楚为什么,但现在似乎工作正常!
我最终得到的代码是:
在ConfigureServices中,Startup.cs
services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false };
options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_xxxxxxxxx";
options.RequireHttpsMetadata = false;
});
在配置中,Startup.cs
app.UseAuthentication();
在appsettings.json中
"AWSCognito": {
"Region": "{Region}",
"PoolId": "{PoolId}",
"AppClientId": "{AppClientId}"
}
当授权 HTTP 标头中提供访问令牌时,对用 [Authorize] 属性修饰的 API 方法的调用会成功,并且 User.Identity.Claims.Where(c => c.Type == "username").FirstOrDefault()给出经过身份验证的用户的用户 ID。
感谢您的建议。
您可以采取以下措施来使用 Amazon Cognito 保护您的 .NET 6 API。
安装
Microsoft.AspNetCore.Authentication.JwtBearer
NuGet 包。
在
appsettings.json
文件中添加以下设置。
{
.
.
"AppSettings": {
"AllowedOrigions": "http://localhost:80", //change it as per your requirement, this should have a comma values
"Cognito": {
"AppClientId": "",
"UserPoolId": "",
"AWSRegion": ""
}
}
}
在
WebApplicationBuilder
文件中的 Program.cs
之后添加以下代码以读取配置。
var builder = WebApplication.CreateBuilder(args);
// read configurations
string[] allowedDomains = builder.Configuration["AppSettings:AllowedOrigions"].Split(",");
string cognitoAppClientId = builder.Configuration["AppSettings:Cognito:AppClientId"].ToString();
string cognitoUserPoolId = builder.Configuration["AppSettings:Cognito:UserPoolId"].ToString();
string cognitoAWSRegion = builder.Configuration["AppSettings:Cognito:AWSRegion"].ToString();
string validIssuer = $"https://cognito-idp.{cognitoAWSRegion}.amazonaws.com/{cognitoUserPoolId}";
string validAudience = cognitoAppClientId;
此外,在您的
Program.cs
文件中添加以下代码来注册必要的服务。
// Configure CORS
builder.Services.AddCors(item =>
{
item.AddPolicy("CORSPolicy", builder =>
{
builder.WithOrigins(allowedDomains)
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// Register authentication schemes, and specify the default authentication scheme
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
// Note: Authority is the address of the token-issuing authentication server.
// The JWT bearer authentication middleware will use this URI to find and retrieve the public key that can be used to validate the token’s signature.
// It will also confirm that the iss parameter in the token matches this URI. Hence, we don't need to specify ValidateIssuer explicitly in TokenValidationParameters.
// See https://devblogs.microsoft.com/dotnet/jwt-validation-and-authorization-in-asp-net-core/
options.Authority = validIssuer;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidAudience = validAudience,
ValidateAudience = true,
};
});
var app = builder.Build();
添加CORS和身份验证中间件。
app.UseCors("CORSPolicy");
app.UseAuthentication(); // resposible for constructing AuthenticationTicket objects representing the user's identity
app.UseAuthorization();
请注意,身份验证过程是由我们使用
app.UseAuthentication()
代码注册的身份验证中间件处理的。
创建如下所示的
ProfileController
用于测试目的。
[Authorize]
[ApiController]
[Route("[controller]")]
public class ProfileController : ControllerBase
{
[HttpGet]
public IEnumerable<KeyValuePair<string, string>> Get()
{
return User.Claims.Select(item => new KeyValuePair<string, string>(item.Type, item.Value)).ToList();
}
}