Error with Using Cognito Identity Pool with React App, NestJs Backend and Quicksight Embedding

Hello brothers and sisters,

Could someone please give me some direction on how I should proceed next? I have been stuck here for some time now.

My Architecture: My internal web application uses React and runs on Amplify with Cognito authentication. The backend, built with NestJS, runs on Beanstalk. I want my users who are logged in on my website to be able to see embedded QuickSight dashboards/visuals.

I have already set up a user pool and identity pool in Cognito. The identity pool has this role: “Cognito_WebAuth_Role” with the following setup:

Role Permission:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "quicksight:GetDashboardEmbedUrl",
                "quicksight:ListDashboards",
                "quicksight:ListUserGroups",
                "quicksight:ListUsers",
                "quicksight:RegisterUser"
            ],
            "Resource": "*"
        }
    ]
}

Role Trust Relationships:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "quicksight.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "cognito-identity.amazonaws.com:aud": "ap-southeast-2:b8c646ee-4444-4bc6-858a-bcfb998161b4"
                },
                "ForAnyValue:StringLike": {
                    "cognito-identity.amazonaws.com:amr": "authenticated"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::730109622289:user/abc-admin",
                    "arn:aws:iam::730109622289:user/cody-abc",
                    "arn:aws:iam::730109622289:root"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

I have also assigned this “Cognito_WebAuth_Role” to QuickSight.

My NestJS Setup

quicksight.module.ts

import { Module } from '@nestjs/common';
import { QuicksightController } from './quicksight.controller';
import { QuicksightService } from './quicksight.service';
import { STSClient } from '@aws-sdk/client-sts';

@Module({
  imports: [],
  controllers: [QuicksightController],
  providers: [
    {
      provide: STSClient,
      useFactory: () => {
        return new STSClient({ region: 'ap-southeast-2' }); 
      },
    },
    {
      provide: 'ROLE_ARN',
      useValue:
        'arn:aws:iam::730109622289:role/Cognito_WebAuth_Role', 
    },
    QuicksightService, 
  ],
})
export class QuickSightModule {}

quicksight.service.ts

import { Injectable, Inject } from '@nestjs/common';
import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts';
import {
  QuickSightClient,
  GetSessionEmbedUrlCommand,
} from '@aws-sdk/client-quicksight';

@Injectable()
export class QuicksightService {
  constructor(
    @Inject(STSClient) private stsClient: STSClient,
    @Inject('ROLE_ARN') private roleArn: string,
  ) {}

  async getQuickSightClient(): Promise<QuickSightClient> {
    console.log('Assuming role with ARN:', this.roleArn);
    console.log('Using region:', 'ap-southeast-2');
    const assumeRoleCommand = new AssumeRoleCommand({
      RoleArn: this.roleArn,
      RoleSessionName: 'QuickSightEmbedSession',
    });
    const { Credentials } = await this.stsClient.send(assumeRoleCommand);
    console.log('Credentials received:', Credentials);
    return new QuickSightClient({
      region: 'ap-southeast-2',
      credentials: {
        accessKeyId: Credentials.AccessKeyId,
        secretAccessKey: Credentials.SecretAccessKey,
        sessionToken: Credentials.SessionToken,
      },
    });
  }

  async getEmbedUrl(userContext: any): Promise<string> {
    try {
      const quickSightClient = await this.getQuickSightClient();
      const command = new GetSessionEmbedUrlCommand({
        AwsAccountId: '730109622289',
        EntryPoint: 'analyses/e4fa5030-23fa-2222-ad54-f448a206beb9',
        SessionLifetimeInMinutes: 100,
        UserArn:
          'arn:aws:iam::730109622289:role/Cognito_WebAuth_Role', 
      });

      const { EmbedUrl } = await quickSightClient.send(command);
      return EmbedUrl;
    } catch (error) {
      console.error('Error getting QuickSight Embed URL:', error);
      throw error;
    }
  }
}

ERROR
When i make a request, i got this error

Failed to get QuickSight embed URL: ValidationException: 1 validation error detected: Value ‘arn:aws:iam::730109622289:role/Cognito_WebAuth_Role’ at ‘userArn’ failed to satisfy constraint: Specified resource is not reachable in this region (‘ap-southeast-2’). Please check the arn format and region information

Confirm that everything has been set up in the same region ap-southeast-2’

Any suggestions into how to proceed to debug would be really appreciated. Thank you so muchhhhh!

@cody ,
The UserARN value is wrong. This needs to be QuickSight user who has access to the dashboard.

You can run a AWS SDK for JavaScript v3 and in the response you will find the ARN.

Kind regards,
Koushik

@Koushik_Muthanna

Hi Koushik,

Thank you. Confirm it is working now.
Would you please clarify the following for me as well.
In my setup, all logged-in users view AWS QuickSight dashboards via a single user identity, utilizing the AssumeRole feature to embed these dashboards into the application. This single user identity is used to authenticate and generate QuickSight sessions for any user on my platform.

Given the pay-per-session pricing model of QuickSight, where it is $0.30 per session up to a maximum of $5 per user per month, how does this pricing apply when a single QuickSight user identity is shared across multiple actual users of my application? Specifically, if I have 50 actual users all accessing QuickSight through this single assumed QuickSight user, does the $5 cap still apply, or will I incur additional charges based on the number of distinct sessions these 50 users generate through the single QuickSight user identity?

I’m trying to understand the cost implications of this setup and whether each session initiated by any of the 50 users counts separately towards the billing, despite all using the same QuickSight user identity.

@cody ,

QuickSight has pricing changes and this is effective for new accounts . If you had a QuickSight enterprise edition before April 30th 2024, then you have time until 1st May 2025 when the new pricing will come into effect ( https://www.youtube.com/watch?v=H4jpT6RNZog ) .

For consumption of dashboards by your readers, every user should be a registered in QuickSight user.

Specifically, if I have 50 actual users all accessing QuickSight through this single assumed QuickSight user, does the $5 cap still apply, or will I incur additional charges based on the number of distinct sessions these 50 users generate through the single QuickSight user identity?

You should register all those 50 users in QuickSight. The pricing will be applied for every individual user (Business Intelligence Service – Amazon QuickSight Pricing – AWS).

Once registered you have 2 possibilities :
1/ Either go with individual user pricing or
2/ Capacity pricing

If you decide that you don’t want to manage users in QuickSight, then you can embed as anonymous user ( Embedding QuickSight data dashboards for anonymous (unregistered) users - Amazon QuickSight ) for which capacity pricing is required ( Business Intelligence Service – Amazon QuickSight Capacity Pricing – AWS )

Kind regards,
Koushik