Skip to content

Instantly share code, notes, and snippets.

@evgalak
Last active March 13, 2024 12:38
Show Gist options
  • Save evgalak/d0d1adf099e2d7bff741c16a89bf30ba to your computer and use it in GitHub Desktop.
Save evgalak/d0d1adf099e2d7bff741c16a89bf30ba to your computer and use it in GitHub Desktop.
PHP code snippet to generate signed URL of the AWS QuickSight embed dashboard
<?php
// In addition to explanations from:
// https://stackoverflow.com/questions/53773313/how-to-get-generate-aws-quicksight-secure-dashboard-url/54052069#54052069
use Aws\Sts\StsClient;
use Aws\Credentials\Credentials;
use Aws\QuickSight\QuickSightClient;
use Aws\CognitoIdentity\CognitoIdentityClient;
use Aws\QuickSight\Exception\QuickSightException;
class StatisticsController
{
/*
* Generate signed URL of the AWS QuickSight embed dashboard
*
* @param $qsDashboardId
* @return $qsDashboardUrl
* @throws Throwable
*/
function getQsDashboardUrl($qsDashboardId)
{
// $user = Auth::user();
// Authorised user
$user = (object) ['username'=>'***', 'email=>'***']
// Step 0: Get idToken from request headers
// $authorizationHeader = request()->header('Authorization');
// $idToken = trim(str_replace('Bearer', '', $authorizationHeader));
// Here we should have a valid ID tocken (from the cognito user pool, or other provider)
$idToken='******************************************************';
// Step 1: Get identity based on idToken
$cognitoIdentityClient = new CognitoIdentityClient([
'version' => 'latest',
'region' => 'eu-central-1',
'IdentityPoolId' => config('aws-cognito-auth.identity_pool_id') // IDENTITY pool id: 'eu-central-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
]);
$identity = $cognitoIdentityClient->getId([
'IdentityPoolId'=> config('aws-cognito-auth.identity_pool_id') ,
'Logins' => [
config('aws-cognito-auth.user_pool_url') => $idToken,
// USER pool url/id 'cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXXXXXX' => $idToken
// Or other providers like Fb, Google, AD, etc.
]
]);
// Step 2A.1: Get identity credentials
$openIdToken = $cognitoIdentityClient->GetOpenIdToken([
'IdentityId'=> $identity['IdentityId'],
'Logins' => [
config('aws-cognito-auth.user_pool_url') => $idToken,
]
]);
// Step 2A.2: Instantiate StsClient
$stsClient = new StsClient([
'region' => 'eu-central-1',
'version' => 'latest',
'credentials' => false,
]);
// Step 2A.3: Request temporary credentials of role what should have policy with
// quicksight:RegisterUser, quicksight:GetDashboardEmbedUrl actions allowed
$role = $stsClient->assumeRoleWithWebIdentity([
'RoleArn' => config('aws-quicksight.embed_dashboard_role_arn'),
'RoleSessionName' => $user->username,
'WebIdentityToken' => $openIdToken['Token'],
]);
// Step 2A.4: Prepare credentials object
$credentialsObject = new Credentials(
$role['Credentials']['AccessKeyId'],
$role['Credentials']['SecretAccessKey'],
$role['Credentials']['SessionToken'],
(int) $role['Credentials']['Expiration']->format('U')
);
// Alternative scenario. If Quicksight is on a same AWS acc,
// then you can request credentials directly, it will auto assume default identity pool role for authorised users
//
/*
// Step 2B : Get identity credentials
$credentials = $cognitoIdentityClient->getCredentialsForIdentity([
'IdentityId'=> $identity['IdentityId'],
'Logins' => [
config('aws-cognito-auth.user_pool_url') => $idToken,
]
]);
// Step 2B.1: Prepare credentials object
$credentialsObject = new Credentials(
$credentials['Credentials']['AccessKeyId'],
$credentials['Credentials']['SecretKey'],
$credentials['Credentials']['SessionToken'],
(int) $credentials['Credentials']['Expiration']->format('U')
);
*/
// Step 3: Instantiate QS client in us-east-1(QS users repository is there even if your datasets are somewhere else)
$qsClient = new QuickSightClient([
'region' => 'us-east-1',
'version' => 'latest',
'credentials' => $credentialsObject
]);
// Step 3: Load/Register QS user client
$qsRegisterUserParams = [
'AwsAccountId' => config('aws-quicksight.account_id'),
'Email' => $user->email,
'IdentityType' => 'IAM', //| QUICKSIGHT,
'Namespace' => 'default',
'UserRole' => 'READER', //ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
'IamArn' => config('aws-quicksight.embed_dashboard_role_arn'),
'SessionName' => $user->username,
];
$qsUser = null;
// Step 3.1: Try to Load QS user info
try{
$qsUser = $qsClient->describeUser([
'AwsAccountId' => config('aws-quicksight.account_id'),
'Namespace' => 'default',
'UserName'=> config('aws-quicksight.embed_dashboard_role_name').'/'.$user->username,
]);
}
catch (QuickSightException $e){
//If a user is just not found procced to registration, in all other cases - throw an exception
throw_if($e->getAwsErrorCode() !== 'ResourceNotFoundException', $e);
}
// Step 3.1: If user not found - try to register
if(!$qsUser){
$qsUser = $qsClient->registerUser($qsRegisterUserParams);
}
// Step 4.1: Re-Instantiate QS client to region with datasources
$qsClient = new QuickSightClient([
'region' => config('aws-quicksight.datasets_region'),
'version' => config('aws-quicksight.version'),
'credentials' => $credentialsObject
]);
// Step 4.2: Get dashboard URL
$result = $qsClient->getDashboardEmbedUrl([
'AwsAccountId' => config('aws-quicksight.account_id'),
'DashboardId' => $qsDashboardId,
'IdentityType' => 'IAM',
'ResetDisabled' => false,
'SessionLifetimeInMinutes' => config('aws-quicksight.session_duration'),
'UndoRedoDisabled' => false,
'IamArn' => $qsUser['User']['Arn']
]);
return $result['EmbedUrl'];
}
}
?>
@michaeljberry
Copy link

Thank you for this code! It has been very helpful in understanding the workflow of getting this to work in PHP. I was wondering if you could share samples of the variables you pull from config? I've confirmed in Policy Simulator that the roles have the correct policies and my code implementation is almost identical to yours, but I'm still getting errors and I think it's likely because I have the incorrect types of data. So for example, could you share obfuscated values for the following?

aws-cognito-auth.identity_pool_id <-- Is this the IdentityPoolID found here: https://console.aws.amazon.com/cognito/pool/edit/ in this format: us-east-1:xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

aws-quicksight.embed_dashboard_role_arn <--Is this the Dashboard Role ARN as found here: https://console.aws.amazon.com/iam/home in this format: arn:aws:iam::xxxxxxxxxxxx:role/QuickSightEmbed

aws-quicksight.embed_dashboard_role_name <-- Is this the role name as found here: https://console.aws.amazon.com/iam/home#/roles in this format: QuickSightEmbed

@evgalak
Copy link
Author

evgalak commented May 8, 2020

Hi Michael(@michaeljberry)! Great that it was helpful!

Answers:

  1. Yes, exactly. aws-cognito-auth.identity_pool_id is a IdentityPoolID from page what you mention
  2. Yes, this is role ARN in the format as you mention
  3. Yes, this should be a role name, I have experimentally found it after I wasn't able to describeUser by its original username.
    Once you get to this step you can check the exact names in the QS users list.

What errors do you get, and how far in steps have you proceed?

I guess another tricky part also could be an AWS configuration.

I also need to mention that scenario 2B is based on AWS guides, but I haven't tested it since my QS was on a separate account.

Make sure that you also have configured Authenticated role (https://console.aws.amazon.com/cognito/pool/edit/) with assume permissions for the QuickSightEmbed role, like in example here: https://aws.amazon.com/blogs/big-data/embed-interactive-dashboards-in-your-application-with-amazon-quicksight/

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::293424211206:role/QuickSightEmbed"
    }
}

@michaeljberry
Copy link

michaeljberry commented May 8, 2020

Thank you for getting back to me! I originally started with 2B because my AWS and Quicksight were on the same account, but I started from square one with the code again using 2A method. However, I kept getting this error using 2A's method:

The request signature we calculated does not match the signature you provided.

It turns out that I missed the subtle change in the credential Object between 2B and 2A. The secret key in 2B is SecretKey but in 2A it's SecretAccessKey. Copy-pasta... you got me again!

@evgalak
Copy link
Author

evgalak commented May 8, 2020

Great that you have found it! So, does it works now?

@michaeljberry
Copy link

Yes, using method 2A works. Thank you again for documenting this and making it public!

@karki117
Copy link

karki117 commented Aug 6, 2020

Thanks for sharing the php.
I am working on embedding the Quicksight dashboard on Wordpress site.
I have done the following steps:

  1. policy:Quicksightembed: registeruser, stsassume, getembeddedurl
  2. Role: quicksightRole <>>attached the above policy(quicksightembed)
  3. Cognito userpool and identity pool created (this is currently being used on the wordpress site)
  4. built the graph and publish dashboard to all
  5. uploaded your php to the wordpress (parameters changed).

It is not working the webpage is blank. Could you please provide step guide to implement this on wordpress on linux?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment