2

技术目标:

  • 客户端:Blazor WebAssembly
  • 后端: .NET Core 3.1 WebApi on Lambda (AWS) via ApiGateway (/{proxy+})
  • 身份验证:AWS Cognito

后端:

  • 我有一个 .NET Core Lambda 函数,可以通过 ApiGateway /{proxy+} => Lambda 通过 AWS ApiGateway (RestApi) 访问。这部署和工作正常。我已将 WebApi 配置为使用 AWS Cognito 作为其身份验证方法(下面的 Startup..cs:ConfigureServices 代码)。
  • 我可以在我的路由上成功使用 [Authorize] 属性,当我直接在浏览器中使用 [Authorize] 访问路由时,我会被发送到 Cognito 签名页面,我可以登录并被发送回我的 Api 和调用执行。效果很好。

Startup.cs:配置服务

RegionEndpoint region = Configuration.GetAWSOptions().Region;
string CognitoMetadataAddress = $"https://cognito-idp.{region.SystemName}.amazonaws.com/{AppConfig.CognitoPoolId}/.well-known/openid-configuration";

//
// Ref: https://criticalhittech.com/2019/02/19/asp-net-core-with-aws-lambda-and-cognito/
//
services.Configure<OpenIdConnectOptions>(options =>
{
    options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
    options.ClientId             = AppConfig.CognitoClientId;
    options.MetadataAddress      = CognitoMetadataAddress;
    options.ResponseType         = OpenIdConnectResponseType.Code;
    options.SaveTokens           = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true
    };
});
services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    options.ClientId        = AppConfig.CognitoClientId;
    options.MetadataAddress = CognitoMetadataAddress;
    options.ResponseType    = OpenIdConnectResponseType.Code;
    options.SaveTokens      = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true
    };
    options.Events = new OpenIdConnectEvents()
    {
        OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
        OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut,
        OnAuthenticationFailed = OnAuthenticationFailed,
        OnUserInformationReceived = OnUserInformationReceived
    };
});

客户端:

  • 我有一个使用 WebAssembly 的基本 Blazor 项目(因此我最终可以将编译后的静态输出放到 AWS S3 上并将其托管在那里)。
  • 我已将项目配置为使用 Cognito 作为其身份验证源(请参阅下面的 Program.cs:Main 代码)并且它工作“正常”(它当然有愚蠢的 Cognito X-Frame-Options 问题,但它可以工作)。

程序.cs:主要

string CognitoPoolId = "ca-central-1_<REMOVED>";
string region = CognitoPoolId.Substring(0, CognitoPoolId.IndexOf('_', StringComparison.InvariantCultureIgnoreCase));
string CognitoAuthority = $"https://cognito-idp.{region}.amazonaws.com/{CognitoPoolId}";
string CognitoMetadataAddress = $"https://cognito-idp.{region}.amazonaws.com/{CognitoPoolId}/.well-known/openid-configuration";

builder.Services.AddOidcAuthentication(options =>
{
    options.ProviderOptions.Authority = CognitoAuthority;
    options.ProviderOptions.MetadataUrl = CognitoMetadataAddress;
    options.ProviderOptions.ClientId = "<REMOVED>";
    options.ProviderOptions.RedirectUri = $"{builder.HostEnvironment.BaseAddress.TrimEnd('/')}/authentication/login-callback";
    options.ProviderOptions.ResponseType = "code";

});

builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();

问题 在我的 Blazor 应用程序中,我想调用我的后端 API(需要身份验证)并使用 Blazor 应用程序已经拥有的授权,因为客户端和后端都使用相同的 Cognito 用户池。IE

  1. 加载 Blazor 应用
  2. 完成登录
  3. 对我的后端进行 Http 调用 <--使用 #2 中的登录会话
            HttpResponseMessage response = await Http.SendAsync(requestMessage);

如何配置 HttpClient 实例以通过使用 Cognito 进行身份验证来发送 Blazor 应用程序具有的授权,以便它可以调用受保护的 API?

4

2 回答 2

0

在客户端项目 Program.cs 中,您可以添加一个 HttpClient,如下所示,它将必要的令牌添加到您的 http 请求中。然后可以根据需要将其注入代码中以进行 http 调用。

builder.Services.AddHttpClient("UniqueClientNameHere", client => client.BaseAddress = serverBaseAddress)
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("UniqueClientNameHere"));

例如在名为 Foobar.razor 的 .razor 文件中:

@page "/foobar"
@inject HttpClient Http

<h1>Hello Foobar</h1>

@code{
    protected override async Task OnInitializedAsync()
    {
        HttpResponseMessage response = await Http.SendAsync(requestMessage);
    }
}
于 2020-07-16T14:15:39.043 回答
0

嗯,当然很简单……

@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
...
HttpRequestMessage requestMessage = new HttpRequestMessage()
{
   ...
};
AccessTokenResult result = await TokenProvider.RequestAccessToken();
if (result.TryGetToken(out AccessToken token))
{
    requestMessage.Headers.Authorization =
        new AuthenticationHeaderValue("Bearer", token.Value);
}
HttpResponseMessage response = await Http.SendAsync(requestMessage);

编辑:对于阅读本文的任何人,请参阅如何在使用 OpenId (Cognito) 进行身份验证后在 Blazor WebAssembly 中获取 id_token?如果您打算使用 Cognito 中的身份池将用户的访问范围限定为特定 IAM 角色,您将需要 id_token 而不是 access_token。

于 2020-07-16T15:47:50.613 回答