Skip to content

Simplifying Tenant-Specific Authentication with AWS Cognito and Pre-Token Generation Lambda

0

I have a multi-tenant application with the following setup:

  • Frontend: Telerik Blazor
  • Backend: .NET Core AWS Serverless Application
  • Database: SQL Server (each tenant has its own database)

Users access tenant-specific environments through URLs that include the tenant name as a subdomain, e.g., https://{tenantname}.{baseurl}. Authentication is handled using AWS Cognito with its Managed Login Page. Authorization is managed via a Pre-Token Generation Lambda function, which retrieves user claims from the tenant database and adds them to the ID token.

Currently, the application redirects users to Cognito's Managed Login Page with a state parameter that includes tenant information. However, the Pre-Token Generation Lambda function cannot access this state parameter. Some forums suggest that clientMetadata could carry the parameters, but I've tried, it is empty and does not include the values sent via the state parameter. To resolve this, we use a custom Cognito attribute (custom:CurrentTenant) to store the tenant information.

Since the username is unknown until after the user submits the login form, the login process involves three steps:

  1. Redirecting the user to Cognito's Managed Login Page to obtain an authorization code (response_type=code).
  2. Using the callback to exchange the code with token
  3. Set the user's custom:CurrentTenant attribute with the correct tenant information, then refreshing the token.

This process works but is unnecessarily complex. To simplify, I would like to bypass the need for a two-step process by opening the Managed Login Page with response_type=token to obtain the token (including claims) directly instead of the code.

Question: Is there a way to access tenant information in the Pre-Token Generation Lambda function without relying on a three-step process or using the custom:CurrentTenant attribute? Specifically, is it possible to pass the tenant information directly and make it accessible to the Lambda function during token generation?

2 Answers
0

Yes, it is possible to simplify your authentication process and access tenant information in the Pre-Token Generation Lambda function without relying on a three-step process or using the custom:CurrentTenant attribute. You can achieve this by leveraging Amazon Cognito's clientMetadata parameter and modifying your authentication flow.

Here's how you can implement this:

  1. When redirecting users to the Cognito Managed Login Page, include the tenant information in the clientMetadata parameter. This parameter allows you to pass custom key-value pairs to your Lambda function.

  2. In your Pre-Token Generation Lambda function, you can access the clientMetadata information directly. This allows you to retrieve the tenant information without needing to store it in a custom attribute or perform additional steps.

  3. Use the tenant information within your Lambda function to fetch the necessary user claims from the appropriate tenant database and add them to the ID token.

  4. Configure your user pool to send a version 2 event to the Lambda function, which allows for access token customization in addition to ID token customization.

By implementing this approach, you can reduce the authentication process to a single step, obtaining the token (including claims) directly with response_type=token. This eliminates the need for the additional steps of exchanging the code for a token and refreshing the token after setting the custom attribute.

This method not only simplifies your authentication flow but also improves performance by reducing the number of API calls and eliminating the need for token refreshing. It allows you to maintain your multi-tenant architecture while streamlining the authentication process for a better user experience.

Remember to ensure that your Lambda function is properly secured and that you handle the clientMetadata securely, as it will contain sensitive tenant information. Also, make sure to update your frontend application to include the necessary clientMetadata when initiating the authentication process.
Sources
Pre token generation Lambda trigger - Amazon Cognito
Community | Cognito Starter Kit with Rust and Lambda
How authentication works with Amazon Cognito - Amazon Cognito

answered a year ago
  • I've already tried ClientMetadata. It does not work. For more information; I am redirecting user to Cognito Managed Login Page as below; https://.amazoncognito.com/login?client_id=&prompt=login&redirect_uri=***&response_type=code&scope=openid+profile+aws.cognito.signin.user.admin&state=tenant%3Dadmin

    In PreTokenGenerator Lambda I've logged the Client Metadata as below. It is empty;

    public async Task<CognitoPreTokenGenerationEvent> idsPreTokenGenerationHandler(CognitoPreTokenGenerationEvent cognitoEvent, ILambdaContext context) {
    foreach (var item in cognitoEvent.Request.UserAttributes) { context.Logger.LogLine($"UserAttributes:Key: {item.Key}, Value: {item.Value}"); }

     if (cognitoEvent.Request.ClientMetadata != null)
     {
         foreach (var item in cognitoEvent.Request.ClientMetadata)
         {
             context.Logger.LogLine($"ClientMetadata:Key: {item.Key}, Value: {item.Value}");
         }
     }
    

    }

0

Hi,

To investigate why the ClientMetadata is empty, we require account and resource details that are non-public information. Please open a support case with AWS using the following link.

To bypass the extra step of exchanging the authorization code for tokens at the /token endpoint, you can make use of the Implicit grant (response_type=token) by enabling this grant on your app client. This is however a legacy grant and is not recommended as tokens are exposed in the callback fragment.

As you correctly mentioned, Lambda triggers cannot access the state parameter. Considering that ClientMetadata is sent to the Lambda function using either the AdminRespondToAuthChallenge or RespondToAuthChallenge APIs which can be seen in the documentation, I suspect that this is the reason ClientMetadata is empty as you are using the /login endpoint to authenticate and not the user pool APIs InitiateAuth and RespondToAuthChallenge.

clientMetadata


To pass this data to your Lambda function, use the ClientMetadata parameter in the AdminRespondToAuthChallenge and RespondToAuthChallenge API operations. Amazon Cognito doesn't include data from the ClientMetadata parameter in AdminInitiateAuth and InitiateAuth API operations in the request that it passes to the pre token generation function.
AWS
SUPPORT ENGINEER
answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.