Photo by NordWood Themes on Unsplash

Serverless B2B Authentication

Example of using API Gateway, EventBridge API Destinations and Cognito to onboard and integrate with external customers, including using API Keys and Usage Plans. Example written in the CDK and Typescript. In Part 2 we will productionise the solution looking at security, availability and scalability.

Introduction

In Part 1, we are going to create a solution based on two fictitious companies which need to communicate with each securely in a b2b fashion:

🚘 Lee James Luxury Cars. This company manufactures cars, and as part of that process needs to acquire tires from a 3rd party supplier. When a Car Order is created, with a status of OrderSubmitted, it creates a Tires Order, and waits for the tire order to be completed before completing the overall car order.

🛞 LJG Tires. This company supplies car manufactures with tires for their cars, and is called via Lee James Luxury Cars to procure tires for their cars. When a tire order is complete, a call back to the Car Orders API happens to complete the overall car order.

Let’s look at what we are building in the next section.

⚠️ Note: The code is not production quality and is only to help illustrate the architectural concepts and services discussed in the article.

You can pull down the code here: https://github.com/leegilmorecode/serverless-b2b-authentication

In Part 2 we are going to go into more detail around productionising the solution around the non functional requirements scalability, availability and security.

What are we building? 🔩

We are building a solution where the car manufacturing company (in one AWS account) automatically raises an order for tires each time somebody orders a new car from them (the tire company in a separate AWS account), which is fully authenticated using a Client Credentials Grant Flow with Amazon Cognito, as well as specific API Keys per car manufacturer.

Once the tire order has been completed, a web-hook using EventBridge API Destinations subsequently updates the associated car order as completed too based on events. This is shown in the diagram below:

Example of what we are building

In the next section let’s look at the AWS services we will be using and why.

Which AWS services are we using? 💭

Let’s see which AWS services we are using as part of this article:

✔️ AWS API Gateway Usage Plans

A usage plan specifies who can access one or more deployed API stages and methods — and optionally sets the target request rate to start throttling requests. The plan uses API keys to identify API clients and who can access the associated API stages for each key.

This is key for our Tires Order API as we could have many different car companies placing orders, and we need to be able to differentiate the calls for logging and perhaps different business logic; as well as being able to throttle them independently.

✔️ Amazon EventBridge API Destinations

Amazon EventBridge API destinations are HTTP endpoints that you can invoke as the target of a rule, similar to how you invoke an AWS service or resource as a target.

Using API destinations, you can route events between AWS services, integrated software as a service (SaaS) applications, and your applications outside of AWS by using REST API calls. When you specify an API destination as the target of a rule, EventBridge invokes the HTTP endpoint for any event that matches the event pattern specified in the rule and then delivers the event information with the request.

✔️ Amazon Cognito (Machine to Machine flow)

Using Amazon Cognito we can allow one or more services (machines) to talk to each other securely using a Client Credentials Grant Flow (as shown in the diagram below).

“With machine-to-machine (M2M) applications, such as CLIs, daemons, or services running on your back-end, the system authenticates and authorises the app rather than a user. For this scenario, typical authentication schemes like username + password or social logins don’t make sense. Instead, M2M apps use the Client Credentials Flow (defined in OAuth 2.0 RFC 6749, section 4.4), in which they pass along their Client ID and Client Secret to authenticate themselves and get a token.” — Auth0

This allows our Car Order domain to generate an access token (using a secret id and password) with the correct scopes (i.e. authorisation around what methods can be accessed on another domain), and the token can be used to call the Tire Order domain via its API (with the token subsequently validated for security reasons).

Example of a Client Credentials Grant Flow using Amazon Cognito

Let’s now look at how we deploy the code in the next section.

Deploying the solution 🏗️

To deploy the solution across two different AWS accounts (one for Car orders and one for Tire Orders) you will need to perform the following steps:

⚠️ Note: This will incur charges in your AWS account, and also remember to tear down the stacks afterwards.

✔️ Deploy the tires company stack

cd tires-company

npm run deploy

✔️ Deploy the car company stack

Go to the following file: car-company/bin/car-company.ts and pass through the required properties from ./tires-company/cdk-outputs.json

ℹ️ Note: for the property ‘ordersClientSecret’ you will need to get this from the Cognito console.

cd car-company

npm run deploy

✔️ Deploy the tires company destinations stack

Go to the following file tires-company-dest/bin/tires-company-dest.ts and pass through the Orders API endpoint from the file car-company/cdk-outputs.json

cd tires-company-dest

npm run deploy

Cool, thats all deployed to two different AWS accounts ready to talk through the code!

Testing the solution 🧪

To test the solution we can simply use the Postman file found here postman/Serverless B2B.postman_collection.json to hit the POST orders endpoint i.e. creating a car order, replacing the url with the Orders API URL e.g. https://{your-api}.execute-api.eu-west-1.amazonaws.com/prod/orders

Example call to create a new car order

ℹ️ Note: to make the demo easier to play with the POST does not contain a body, and the Lambda integration simply does this for you i.e. only the order ID is dynamic.

If you invoke the endpoint using Postman, you will be able to see in the Car Orders DynamoDB table in Account A that the Order has been created with a status of ‘OrderSubmitted’.

We can see in Account B that in the StockOrders table the Tire Order has also been created with the orderStatus of ‘OrderSubmitted’.

We then have a Lambda called complete-order found here tires-company/src/complete-order/complete-order.ts which is on a CRON of every minute which simulates the completion of the Tire Order asynchronously, and an event is subsequently raised to EventBridge, which uses API Destinations to perform a PATCH back to the Car Orders domain in Account A to complete the overall Car Order now that the tires have been provided!

⏰ After a minute you should be able to see that both orders in the relevant databases have a status of ‘OrderCompleted’. Nice!

Talking through key code 💬

OK, so we have now discussed the overall architecture, the services used, and how to deploy and test. Now let’s talk through the more interesting pieces of code.

✔️ Tires Authentication Setup

The following annotated code shows the Tires Company authentication setup; containing the Cognito User Pool, Usage Plan, API Keys, Resource Server, Client and scopes:

We can see above that we have created a Cognito Authoriser which can be used to authenticate our API calls to the Tires API, also ensuring that the access token has the valid scopes for that given endpoint:

In our example above, this means that the Tires Order API has a POST endpoint on /orders which requires that the consumer (The Car Orders domain) has a valid access token to access the endpoint, and that the access token has the scope ‘tires/create.order’. It also requires that the API Key is passed in the headers too.

✔️ API Destinations Setup

The following code shows the Amazon EventBridge Connection and API Destination setup:

We can see from the demo code above that we create:

Amazon EventBridge Connection

An API destination uses a Connection to manage the authentication credentials for a target. This defines the authorisation type used, which can be an API key, OAuth client credentials grant, or a basic user name and password. In our basic example for the Car Orders domain we are using API Key for simplicity in Part 1.

You create a connection to each different external API endpoint and share the connection with multiple endpoints.

Amazon EventBridge API Destination

To create an API destination, you provide a name, the HTTP endpoint and method, and the connection. When you configure the destination, you must also set an invocation rate limit between 1 and 300 events per second. This helps protect the downstream endpoint from surges in traffic. If the number of arriving events exceeds the limit, the EventBridge service queues up events. It delivers to the endpoint as quickly as possible within the rate limit.

It continues to do this for 24 hours. To make sure you retain any events that cannot be delivered, set up a dead-letter queue on the event bus. This ensures that if the event is not delivered within this timeframe, it is stored durably in an Amazon SQS queue for further processing. This can also be useful if the downstream API experiences an outage for extended periods of time.

Example of Amazon EventBridge API Destinations

Amazon EventBridge Target Rule

We setup an EventBridge Target Rule which matches events with the pattern below, and subsequently passes them to the API Destination setup previously:

We use pathParameters to ensure that we take the carOrderId from the event, which then replaces the ‘*’ placeholder in the API Destinations setup i.e. /orders/* which is the specific car order resource we want to patch to state the order is now completed.

Example of the path parameters configuration from the console

We can also see from the code below that we only want to pass through on the API call body the detail within the event to the patch call (i.e. we don’t want to pass through the rest of the EventBridge event detail).

Which gets translated in the console as:

Example in the console of the matched events target input

We can see the summary of the overall CDK calls in the console below:

Example results in the console from our CDK calls.

Summary

I hope you found that useful as a basic example of b2b communication using Amazon Cognito, Amazon EventBridge API Destinations and API Gateway.

In Part 2 we are going to go into more detail around productionising the solution around the non functional requirements scalability, availability and security.

Go and subscribe to my Enterprise Serverless Newsletter here for more of the same content:

Wrapping up 👋

Please go and subscribe on my YouTube channel for similar content!

I would love to connect with you also on any of the following:

https://www.linkedin.com/in/lee-james-gilmore/
https://twitter.com/LeeJamesGilmore

If you enjoyed the posts please follow my profile Lee James Gilmore for further posts/series, and don’t forget to connect and say Hi 👋

Please also use the ‘clap’ feature at the bottom of the post if you enjoyed it! (You can clap more than once!!)

About me

Hi, I’m Lee, an AWS Community Builder, Blogger, AWS certified cloud architect and Global Serverless Architect based in the UK; currently working for City Electrical Factors (UK) & City Electric Supply (US), having worked primarily in full-stack JavaScript on AWS for the past 6 years.

I consider myself a serverless advocate with a love of all things AWS, innovation, software architecture and technology.

*** The information provided are my own personal views and I accept no responsibility on the use of the information. ***

You may also be interested in the following:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store