Jeff Bacon
Jeff Bacon

Reputation: 388

How do I pass OpenID authentication from Blazor WebAssembly to a .NET Core WebApi backend, both using Cognito as the OpenID provider?

Technology Goal:

Backend:

Startup.cs:ConfigureServices

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
    };
});

Client-Side:

Program.cs:Main

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();

Problem In my Blazor app, I want to call my back-end API (which requires Authentication) and use the authorization that the Blazor app already has since both the client and backend are using the same Cognito user pool. i.e.

  1. Load up Blazor app
  2. Complete Login
  3. Make Http call to my Backend <-- use the login session from #2
            HttpResponseMessage response = await Http.SendAsync(requestMessage);

How do I configure the HttpClient instance to send along the authorization the Blazor app has via authenticating with Cognito so that it can call the protected Apis?

Upvotes: 4

Views: 1590

Answers (2)

Jeff Bacon
Jeff Bacon

Reputation: 388

Ugh, of course it was simple...

@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);

EDIT: for anyone reading this, please refer to How do I get the id_token in Blazor WebAssembly after authenticating with OpenId (Cognito)? as well as you will need the id_token not the access_token if you are intending to use Identity Pools in Cognito to scope access for a user to a particular IAM Role.

Upvotes: 3

Jon Edwards
Jon Edwards

Reputation: 381

In the Client projects Program.cs, you can add an HttpClient as follows that will add the neccessary tokens to your http requests. This can then by injected into code as required to make your http calls.

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"));

for example inside a .razor file called Foobar.razor:

@page "/foobar"
@inject HttpClient Http

<h1>Hello Foobar</h1>

@code{
    protected override async Task OnInitializedAsync()
    {
        HttpResponseMessage response = await Http.SendAsync(requestMessage);
    }
}

Upvotes: 1

Related Questions