AWS Cognito and Web Applications – Protecting and Accessing APIs (JavaScript and .NET Core)

Introduction

A typical web application consists of a frontend (HTML, JavaScript, CSS), a backend (e.g. REST API) and database for persistence.

Frontend typically communicate over REST calls to the backend for services. Token based authentication is a very common way to manage authentication and authorization information back and forth in the web applications. Applications need to know who users are and what they can do.

In this post, we are going to see how to allow web applications running in user browser to securely communicate with backend APIs with the help of AWS Cognito user pool.

On high level, we will be covering following topics:

  • Integrate AWS Cognito User Pool and .NET Core Web API.
  • Testing API access using Postman REST client.
  • Integrating Web frontend (HTML, JavaScript, CSS) to make authenticated calls to .NET Core Backend API.

Now, some of pre-requisite work is already done and we will build whatever is remaining to have this demo ready.

Setting the Scene

We have an API (.NET Core) which is deployed as a Lambda function. The API expose few endpoints publicly accessible. Now we want a mechanism to protect few of those endpoints and then they can only be accessible when call is authenticated.

For backend, I will be using an existing .NET Core API which we build for a post on topic .NET On AWS – Basics. The source code for the API is available on this git repo. You can use also your own .NET Core application and simply follow the steps.

For frontend, I have a static web application(HTML, JavaScript, CSS), configured to use Cognito for Sign-in/Sign-up services. You can find details on the post AWS Cognito – Web Application Integration Basics. The source code for this web application is available on this git repo.

So, we have backend API, we have frontend browser application and we have AWS Cognito setup in cloud and now we are going to integrate these together.

Designing Solution

Here is diagram of main component of our solution:

Lets see what need to be sent back and forth between AWS cognito and our web application:

The expectation is that when a user authenticated in AWS Cognito and obtained a Token tries to access the API using the Token, the API must be able to validate the Token for its authenticity and let the user pass or deny access.

  • Our Cognito pool will Issue JWTs and sign them.
  • JWTs payload contains user specific information:
    • Groups
    • Issuer
    • client_id
    • username
    • …other

Here is how the general flow looks like:

  1. Client tries to login with cognito user pool, assuming this successful.
  2. Cognito user pool is going to send some data back to app client and this going to contains three different JSON web tokens (JWTs) ID, ACESS, REFRESH.
  3. Client is then going to use ACESS token to make a request to API Gateway.
  4. Now at this point, AWS API Gateway have capability to inspect and validate this token, however we are not going to do this today, so the request will be simply passed to underlying Lambda function which will pass it to .NET Core API.
  5. Next, .NET Core API’s infrastructure (Authentication, Authorization, Authorize attribute etc.) will be equipped to deal with JWT token.
  6. Once validation is done, API controller will return the data.

And now we’ve been able to create a situation where trust can exist between this Web Application Client and the API tier.

JWT Tokens

There’s this thing called web tokens that some people think are God’s gift to the internet, other people call a scourge on all mankind. Put their differences aside, these are digitally signed token.

  • ID Tokens contains User identity information.
  • Access Tokens contains groups and scopes
  • Refresh Tokens are used to get new ID and Access Tokens.

Wiring .NET Core API with Amazon Cognito

Lets start with API part first, we need to configure it, so it can inspect and validate the tokens coming from AWS cognito in an Authorization header. This mechanism is covered in much details in my other posts, so I will keep it to the basics here.

Install NuGet Package

Lets install following package from visual studio:

Microsoft.AspNetCore.Authentication.JwtBearer

Next, Update Startup.cs with Authentication/Authorization Middleware

ConfigureServices Method

Configure Method

You can check the source code for details.

At this point, .NET Core API pipeline is configured to work with AWS Cognito tokens.

Protecting the API Endpoint with Authorize Attribute

Here is a controller with two methods. I decorated one method (Get) with Authorize attribute and the other one (token) without Authorize attribute:

Now we can run the application and test calling these methods.

Testing the API Access

Lets start with API Endpoint without Authorize Attribute:

and as you can see, API happily returned the data.

Next, lets call API Endpoint with Authorize Attribute:

and this time we got 401 Unauthorized error, so endpoint is protected and API is working as expected.

Lets provide an Authorization header to API Endpoint with Authorize Attribute and a valid bearer token and see how API response

and you can see that this time we got the data from REST endpoint.

(The bearer token in example above, I got from web application, which we will see next).

So, our .NET Core API part is done, it is working as expected and now we will turn over attention to frontend components.

Connecting JavaScript Client for REST API Calls

I’ve covered the web application structure in details in earlier post, please check that post if you need more details.

So, actually what we are doing here is just coding the REST communication, so when user login, we store the received JWT to some storage (session storage) and when user click another button (Authenticated AJAX API-Call) then we add JWT as an authorization header to AJAX call (like we did using postman earlier).

here are the corresponding methods to make authenticated REST API calls from JavaScript.

Login Success –> Get JWT Token

User Click Button To make REST call to API

and here is the UI displaying the data received form .NET Core API protected endpoint

With that, all component of our solution design are wired together.

  • We have an AWS Cognito User-Pool.
  • .NET Core API is protected by Cognito User-Pool.
  • A Web Application, which uses Cognito Hosted UI for login purposes and gets token.
  • Web application makes REST call to .NET Core API with JWT token in header.

Here is the diagram again from the start of this post:

I have published the .NET Core API and Web Client application to AWS Cloud, you can check the functionality on the following link:

https://cognito.awsclouddemos.com/

Related Links

Summary

In this post, we setup a web application to securely communicate a backend web API which is protected by AWS Cognito.

We saw that integrating cognito to .NET Core API was straight forward and we just needed to configure the JWT middleware with cognito endpoints information, Configure the .NET Core middleware and decorate the controller method. AWS Cognito service and .NET Core Authentication middleware hides all the ugly, back door shenanigans of signing tokens, rotating keys, verifying tokens and other security details.

On the web client side, we used AWS Amplify and cognito Hosted UI and once user is authenticated then it was just a matter of using JWT as header in Ajax calls.

Let me now if you have some questions or comments. Till next time, Happy Coding.