Simple and Secure User Authentication for a Web Application using AWS ALB Authentication, Cognito, and IBM Tivoli

Harith Sankalpa
Sysco LABS Sri Lanka
11 min readJun 1, 2021

User authentication is a crucial part of any significantly large web application and usually bears a significant cost in development time. This article discusses the workflow required to integrate IBM Tivoli Identity Provider (IdP) into a single-page web application (SPA) that has a backend-for-frontend (BFF) as the backend.

TL: DR;

The application in this scenario consists of an SPA as the frontend, a backend-for-frontend (BFF) that consumes multiple REST APIs to perform the required tasks. The BFF completes its role as a mediator between the frontend and the REST APIs and provides the mapping between backend and frontend data models.

An AWS ALB is set up in front of the BFF and both the ALB and the SPA distribution are set with the same domain name to avoid issues with cross-origin requests. The routes of the BFF that require an active user session to access are protected by introducing authentication as an action to the listener rules in the ALB.

Amazon Cognito is integrated with AWS ALB using OIDC, and IBM Tivoli is set up with Cognito as a SAML IdP. Tivoli is set up for both the Single Sign-On and Single Logout flows.

The status of the user authentication is kept on the browser using a cookie set by the backend after authentication.

Scenario

The setup discussed in this article is targeted towards this scenario:

Users will be shown a landing/welcome page in the frontend SPA application and once a user clicks a login button that is available on the landing page, the user will be redirected to the login page of Tivoli IdP. Once credentials are entered and the user logs in, they will be redirected to my account/dashboard screen of the frontend application.

If an unauthenticated user tries to access a private route in the BFF, the user will be redirected to the Tivoli login page. If they try to access a private route in the frontend, they will be redirected to the landing page of the SPA. Users will log out by clicking on a logout button which will destroy the ALB session and log them out from both Amazon Cognito and Tivoli.

It is assumed that the BFF is already hosted, and the AWS ALB is configured for the BFF. The BFF can be hosted using the AWS Lambda service and the frontend SPA can be hosted in an AWS S3 bucket.

In this scenario, Tivoli is a third-party identity provider and therefore its configuration details are omitted. This article discusses configuration from the viewpoint of Amazon Web Services (AWS). However, the parameters that need to be extracted from Cognito and set up in Tivoli are mentioned.

The approach discussed here is to integrate user authentication into a web application in a highly secure way with minimal code implementation.

AWS Application Architecture Diagram

Introduction to Architectural Components

IBM Tivoli is an identity service provider which supports multiple authentication protocols including OIDC and SAML2.

Amazon Cognito can act as an identity provider as well as an identity manager based on the developer’s requirements.

AWS provides an easier and effortless way to integrate user authentication into a web application via AWS ALB configurations rather than implementing the authentication from scratch.

In AWS ALB configuration rules, a route or a set of routes can be marked as protected routes by introducing authentication as an action in the ruleset of these routes. Once authenticated, the session information of an AWS ALB is stored in server-only cookies in the user’s browser.

It should be noted that the AWS ALB Authentication component is unable to add or process cross-origin headers. Therefore, to get most of the facilities provided by AWS ALB authentication both ALB and SPA are required to have the same domain name. This can be achieved via the AWS CloudFront service by adding the S3 bucket and the ALB as two origins for respective paths of the same domain. Otherwise, you will not be able to use redirection or request denial on unauthenticated facilities and will be required to implement them yourselves on the BFF.

As any IdP that supports OIDC workflow can be directly integrated with the ALB, IBM Tivoli could be directly integrated with the ALB using the OIDC federation. However, in this scenario, Amazon Cognito is introduced between the ALB and Tivoli as an identity manager for more control over user attributes exposed to the application, and to keep the flexibility to integrate multiple IdPs into the application in the future.

Authentication Flow

The following sequence diagram depicts the overall authentication flow of the scenario explained in this article.

Authentication using AWS ALB, Cognito, and IBM Tivoli

The following sequence diagram depicts the sign-out flow of the application which is remarkably similar to the steps of the above flow.

Single Sign Out using AWS ALB, Cognito, and IBM Tivoli

Steps

The following points explain the configurations required to achieve the above authentication and sign-out flows one by one.

1. Create an Amazon Cognito User Pool

The first step of the workflow is to create a Cognito user pool with the required user attributes. You can easily create one by logging into the AWS Management Console and navigating into the Cognito service (Official documentation).

While most of the default configuration will satisfy your requirements, be careful when selecting the user attributes required by default as they cannot be changed later.

You can create an app client with any name you want, but make sure the profile and all the other required OAuth scopes are checked under attribute read and write permissions. This allows the backend to retrieve user profiles and other attributes from the headers of authenticated requests.

2. Integrate IBM Tivoli as an SAML IdP in Cognito

Once the user pool is set up, you need to add IBM Tivoli as a SAML federation partner. To do that you are required to provide the SAML assertion consumer endpoint of the Cognito user pool you created to the Tivoli team, and obtain a SAML federation metadata file from them which is the only thing required to set Tivoli as a SAML IdP in Cognito.

Login Parameters

The SAML assertion consumer endpoint is provided by Cognito by default and it will be in the format as follows. This endpoint only supports HTTP POST binding and therefore Tivoli should be configured to use HTTP POST for the callback.

https://<CognitoUserPoolDomainName>/saml2/idpresponse

Your domain name is the one given at the setup time and can be found in the App integration -> Domain name section of the user pool settings. The domain name should be in the format of

<YourDomainPrefix>.auth.<region>.amazoncognito.com

Note: Currently, Amazon Cognito does not support signed SAML requests for the authentication and therefore Tivoli should be configured to accept authentication requests without a certificate.

Once the above information is given and configured in Tivoli, you will receive the SAML federation metadata file. This file can be used to integrate Tivoli as a SAML IdP by uploading it in Federation -> Identity Provider -> SAML -> Select file panel (shown in the image below). You are required to provide a name for the federation (you can give Tivoli any name you want).

Tick the Enable IdP sign-out flow to log the user out from Tivoli, and once they are logged out the Cognito and ALB session should be destroyed. If you use Tivoli Single Sign-On for other applications as well, you may need to have this unchecked.

Keep in mind to add attribute mapping for any user attributes you marked as required during the user pool setup (if you don’t, it would result in a failure). You can find these mappings by navigating into Federation -> Identity Provider -> Attribute Mapping -> SAML. In here, you need to add the user attribute name from SAML IdP and the user attribute of the user pool.

Single Logout Parameters

To initiate the Single Log-Out flow to sign out the user from Tivoli once they are logged out from Cognito, you are required to provide a little more information such as the sign-out callback URL and the sign-out request signing certificate.

The Cognito logout callback URL follows the format of:

https://<CognitoUserPoolDomainName>/saml2/logout

Note that the above URL supports only HTTP POST binding and therefore Tivoli should be configured to send the reply using HTTP POST.

The signing certificate can be found in the panel where you added the SAML federation metadata file. You can click ‘Show Signing Certificate’ to copy the cert string to be provided to the Tivoli team.

3. Configure the app client

After adding Tivoli as a SAML IdP, you are required to complete the configuration of the app client created during the setup of the user pool. You can find app client settings in App integration -> App client settings.

In the configuration, you are required to enable the Tivoli identity provider and provide sign-in and sign-out callback URLs.

The sign-in callback URLs define the destinations that the authentication information will be sent to, and in this scenario, it will be ALB as it’s always the ALB that will initiate the login request. The pre-configured AWS ALB sign-in callback URL follows the following format.

https://<YourALBDomainName>/oauth2/idpresponse

The sign-out callback URL defines the destinations to be redirected after a successful sign-out flow. This should be the landing page URL of your application as users should be redirected there after logging out.

After the callback URLs are added, make sure Authorization Code Grant is selected under Allowed OAuth flows and all the required OAuth scopes are selected including profile.

After the above configurations are made, take a note of the app client ID displayed under the app client name and save changes.

4. Add ALB authentication rules for the private routes

Once the user pool is configured with Tivoli you are required to add authentication rules for three sets of routes of the BFF in the AWS ALB.

1. Login route — Implementation is discussed below

You are required to set up a rule for a /login route, which will be used to initiate the login flow when a login button on the frontend is clicked, or an unauthenticated user tries to access a private route in the frontend and set a session cookie accessible by the frontend.

For the action of the rule, you are required to select Authenticate, and after it is selected, set Amazon Cognito as the IdP for the rule. The Cognito user pool would be the ID of the user pool. You will be required to select the app client ID that you will be given at the setup time of the user pool.

You can leave the other configurations of the action as they are or change them to meet your requirements. Note that under Advanced settings action on unauthenticated request is set to authenticate which results in the browser redirection to the Tivoli login page if an unauthenticated user tries to access this route.

Don’t forget to add a forward action to the rule where users will be redirected after the authentication.

2. Logout route — Implementation is discussed below

You are required to set up a rule for a /logout route, which will be used to invalidate session cookies set by the ALB and initiate Cognito and Tivoli logout flows.

The rule for this route should be the same configuration as above, but make sure that action on unauthenticated in Advanced Settings of the authentication actions is set to allow. You can avoid setting any authentication action if this route is not captured in the private route selection conditions.

3. Private route — any business route that requires an active user session

You can provide exact paths or any other matching rule to capture all the private paths as the selector of the rule.

The rule for this route should be the same configuration as above, but make sure that action on unauthenticated request in Advanced Settings of the authentication action is set to either authenticate or deny. If you select deny for the on authenticated action you will receive a 401 error without being redirected to the login page

Make sure to add the rule for private paths last, as routes are matched against rules in the order they are in the console (priority order).

5. Implement a /login route in the BFF

The purpose of adding a /login route to the BFF is to initiate the login flow when a user clicks the login button on the landing screen in the frontend.

Once the user is shown the landing/welcome page of the single page application, they should be shown a login button that will initiate a GET request to the login route on the BFF. As the user is not authenticated initially the ALB will redirect the user to the Tivoli login page, and once they log in, they will be redirected to the /login route of the API.

The logic in the /login route is responsible for:

  1. Setting a cookie accessible by the frontend to mark an active session.
  2. Redirecting the user to the dashboard screen of the frontend application

The following gist presents the implementation of the above two tasks using ExpressJs.

6. Implement a /logout route in the BFF

The purpose of adding a /logout is three-fold.

  1. Invalidate cookies set by the AWS ALB.
  2. Invalidate the session flag cookie set at the login.
  3. Redirect the user to the logout URL of Amazon Cognito.

The following gist presents the implementation of the above three tasks using ExpressJs.

Once the user is redirected to the Amazon Cognito logout URL it will initiate the Single Logout flow and log the user out from Tivoli.

7. Route protection in the frontend

As presented above, a session flag will be saved as a cookie named UserSession at login with the value of Active. In any route that should have an active user session to access, the frontend application should check for this cookie and its value. If the cookie is unavailable or the value is not Active the user should be redirected to the /logout route, which in turn will be redirected to the landing page of the frontend application.

If the frontend and the ALB have two different domain names, the frontend will not be able to access the cookie, and therefore will require the utilization of other storage options such as local storage to store the session flag.

You are good to go!

If you have successfully configured everything including the Cognito access token with user profile, the above components will now have an active user authentication mechanism in the web application.

Users will be able to log in by clicking the login button on the welcome screen and if an unauthenticated use tries to access a private route in the BFF, they will be redirected to the Tivoli login page. If a user tries to access a protected route in the frontend, he will be redirected to the welcome page of the frontend application.

You can retrieve the user session details in the BFF routes by extracting the header named X-AMZN-OIDC-DATA that includes the Cognito access token with the user profile.

Cheers and Happy Coding!

--

--

Harith Sankalpa
Sysco LABS Sri Lanka

Software Engineer | Tech Enthusiast | Gamer | Photographer