Use Amazon Cognito as Identity Provider
GoodData uses OAuth 2.0 and OpenID Connect (OIDC) to handle authentication. This article shows a worked example of setting up Amazon Cognito as a custom OIDC identity provider for GoodData.
The example uses Bash and AWS CLI version 2. If you use a different deployment tool, such as CloudFormation or Terraform, or if you configure Amazon Cognito manually in the AWS Console, adapt the steps accordingly.
Follow these steps:
- Set up shared environment variables.
- Create the Amazon Cognito resources.
- Update the OIDC settings of the organization.
- Map users to the organization.
- Log in to GoodData.
For a general overview of OIDC authentication in GoodData, see Set Up Authentication Using OpenID Connect Identity Provider.
Set Up Shared Environment
Set the following environment variables in the shell that you will later use for the aws and curl commands.
| VARIABLE | Example value | Explanation |
|---|---|---|
AWS_REGION | eu-central-1 | AWS region where Cognito will be deployed |
ORG_HOSTNAME | example.gooddata.com | Hostname of the GoodData organization |
ORGANIZATION_ID | alpha | Organization ID |
API_TOKEN | YWRtaW46Ym9vdHN0cmFwOmRlbW8xMjM= | Token with MANAGE permission on the organization |
ORG_PORT | "" | Set to a port number if it differs from 80 or 443 |
ORG_SCHEMA | https | Organization URL schema, http or https |
IDP_ID | cognito | ID of the identity provider object in GoodData |
IDP_IDENTIFIER | example.com | Email domain of users that should authenticate through this IdP |
IDP_ISSUER_ID | cognito | Suffix used in the OAuth callback URL |
COGNITO_POOL_NAME | gdcn-demo | Name of the Cognito user pool |
COGNITO_DOMAIN_PREFIX | gdcn-demo | Prefix of the Cognito hosted UI domain |
COGNITO_CLIENT_NAME | gdcn-client | Name of the Cognito app client |
export AWS_REGION="eu-central-1"
export ORG_HOSTNAME="example.gooddata.com"
export ORGANIZATION_ID="alpha"
export API_TOKEN="YWRtaW46Ym9vdHN0cmFwOmRlbW8xMjM="
export ORG_PORT=""
export ORG_SCHEMA="https"
export IDP_ID="cognito"
export IDP_IDENTIFIER="example.com"
export IDP_ISSUER_ID="cognito"
export COGNITO_POOL_NAME="gdcn-demo"
export COGNITO_DOMAIN_PREFIX="gdcn-demo"
export COGNITO_CLIENT_NAME="gdcn-client"
export HOST_URL="${ORG_SCHEMA}://${ORG_HOSTNAME}${ORG_PORT:+:$ORG_PORT}"
export CALLBACK_URL="${HOST_URL}/login/oauth2/code/${IDP_ISSUER_ID}"Use an Email Domain as the Identifier
Set IDP_IDENTIFIER to a domain name that matches your users’ email addresses, for example example.com. GoodData uses this identifier to decide which identity provider should authenticate a user.
Create the Amazon Cognito Resources
Create the Cognito User Pool
The following command creates a Cognito user pool with the following settings:
- Minimum password length of 8 characters
- Uppercase, lowercase, and numeric characters required
- Temporary passwords valid for 7 days
- Email addresses verified automatically
- Multi-factor authentication disabled
GoodData expects the name claim to be present in the ID token. This example makes the name attribute required in the user pool schema.
The ID of the created user pool is stored in USERPOOL_ID and used later in the configuration.
USERPOOL_ID=$(
aws cognito-idp create-user-pool \
--pool-name "$COGNITO_POOL_NAME" \
--policies 'PasswordPolicy={MinimumLength=8,RequireUppercase=true,RequireLowercase=true,RequireNumbers=true,RequireSymbols=false,TemporaryPasswordValidityDays=7}' \
--auto-verified-attributes email \
--alias-attributes email \
--verification-message-template 'DefaultEmailOption=CONFIRM_WITH_CODE' \
--mfa-configuration OFF \
--user-attribute-update-settings 'AttributesRequireVerificationBeforeUpdate=email' \
--schema \
Name=name,AttributeDataType=String,Mutable=true,Required=true \
Name=email,AttributeDataType=String,Mutable=true,Required=true \
--email-configuration 'EmailSendingAccount=COGNITO_DEFAULT' \
--admin-create-user-config 'AllowAdminCreateUserOnly=true' \
--username-configuration 'CaseSensitive=False' \
--account-recovery-setting 'RecoveryMechanisms=[{Priority=1,Name=verified_email}]' \
--query 'UserPool.Id' \
--output text
)
echo "Created user pool '$USERPOOL_ID'"Create the Cognito Domain
Cognito needs a domain for the hosted sign-in UI and OAuth 2.0 endpoints. This example uses the AWS-hosted Cognito domain.
The resulting hosted UI URL has this format:
https://<domain-prefix>.auth.<aws-region>.amazoncognito.com/
aws cognito-idp create-user-pool-domain \
--user-pool-id "$USERPOOL_ID" \
--domain "$COGNITO_DOMAIN_PREFIX"Create the Cognito App Client
Create an OAuth 2.0 client that GoodData will use for the authorization code flow.
read COGNITO_CLIENT_ID COGNITO_CLIENT_SECRET < <(
aws cognito-idp create-user-pool-client \
--user-pool-id "$USERPOOL_ID" \
--client-name "$COGNITO_CLIENT_NAME" \
--generate-secret \
--supported-identity-providers COGNITO \
--callback-urls "$CALLBACK_URL" \
--allowed-o-auth-flows-user-pool-client \
--allowed-o-auth-scopes openid profile email \
--allowed-o-auth-flows code \
--query 'UserPoolClient.[ClientId,ClientSecret]' \
--output text
)
echo "Created client with id='$COGNITO_CLIENT_ID'"Callback URL in This Example
This example sets oauthIssuerId to cognito, so the callback URL registered in Amazon Cognito must be:
https://<organization-hostname>/login/oauth2/code/cognitoThe CALLBACK_URL variable above uses this exact format.
Update the OIDC Settings of the Organization
GoodData configures identity providers as separate entities and then links the active identity provider to the organization. In this example, you create a Cognito identity provider object with the Identity Providers API and switch the organization to it with the switch endpoint.
Create the Identity Provider Object
Use the client ID and client secret from the previous step to create a custom identity provider in GoodData.
PAYLOAD=$(cat <<EOF
{
"data": {
"id": "$IDP_ID",
"type": "identityProvider",
"attributes": {
"identifiers": ["$IDP_IDENTIFIER"],
"idpType": "CUSTOM_IDP",
"oauthClientId": "$COGNITO_CLIENT_ID",
"oauthClientSecret": "$COGNITO_CLIENT_SECRET",
"oauthIssuerId": "$IDP_ISSUER_ID",
"oauthIssuerLocation": "https://cognito-idp.$AWS_REGION.amazonaws.com/$USERPOOL_ID"
}
}
}
EOF
)
curl --request POST \
--header "Authorization: Bearer $API_TOKEN" \
--header 'Content-Type: application/vnd.gooddata.api+json' \
--data "$PAYLOAD" \
"$HOST_URL/api/v1/entities/identityProviders"Use the Cognito Issuer URL
For Amazon Cognito, set oauthIssuerLocation to the issuer URL in this format:
https://cognito-idp.<aws-region>.amazonaws.com/<user-pool-id>Do not use the Cognito hosted UI domain, such as https://<domain-prefix>.auth.<aws-region>.amazoncognito.com/, as the issuer location.
Cognito Uses the sub Claim by Default
GoodData uses the sub claim for user mapping by default. Because Amazon Cognito includes sub in the ID token, you do not need to set oauthSubjectIdClaim unless you intentionally want to use a different claim.
Check the Linked Identity Provider
To see which identity provider is currently linked to the organization, use:
GET /api/v1/entities/admin/organizations/{id}?include=identityProvidersExample:
curl --request GET \
--header "Authorization: Bearer $API_TOKEN" \
"$HOST_URL/api/v1/entities/admin/organizations/$ORGANIZATION_ID?include=identityProviders"In the response, look for this section:
"identityProvider": {
"data": {
"id": "cognito",
"type": "identityProvider"
}
}Switch the Active Identity Provider
Switch the organization to the Cognito identity provider:
curl --request POST \
--header "Authorization: Bearer $API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"idpId": "'"$IDP_ID"'"
}' \
"$HOST_URL/api/v1/actions/organization/switchActiveIdentityProvider"Provision a User in Cognito
There are multiple ways to add users to a Cognito user pool. This example uses the admin-create-user API.
USER_EMAIL="john.doe@example.com"
USER_NAME="John Doe"
USER_LOGIN="john.doe"
USER_PASSWORD="NewRandomPa33word"
EXTERNAL_ID=$(
aws cognito-idp admin-create-user \
--user-pool-id "$USERPOOL_ID" \
--username "$USER_LOGIN" \
--user-attributes \
Name=email,Value="$USER_EMAIL" \
Name=email_verified,Value=True \
Name=name,Value="$USER_NAME" \
--temporary-password "$USER_PASSWORD" \
--message-action SUPPRESS \
--desired-delivery-mediums EMAIL \
--query 'User.Attributes[?Name==`sub`].Value[]' \
--output text
)
echo "Created user with login='$USER_LOGIN' and password='$USER_PASSWORD'"
echo "Cognito sub claim is '$EXTERNAL_ID'"Map users to the organization
This example uses API-based provisioning. Map the user created in Cognito to a GoodData user by storing the Cognito sub claim in the authenticationId attribute.
Avoid Assigning the Same AuthenticationId to Multiple Users
Although the API allows multiple users to share the same authenticationId, only the alphabetically first userId will be able to log in.
Create a New User
PAYLOAD=$(cat <<EOF
{
"data": {
"id": "$USER_LOGIN",
"type": "user",
"attributes": {
"authenticationId": "$EXTERNAL_ID"
},
"relationships": {
"userGroups": {
"data": [
{
"id": "adminGroup",
"type": "userGroup"
}
]
}
}
}
}
EOF
)
curl --request POST \
--header "Authorization: Bearer $API_TOKEN" \
--header 'Content-Type: application/vnd.gooddata.api+json' \
--data "$PAYLOAD" \
"$HOST_URL/api/v1/entities/users"Update an Existing User
If the user already exists in GoodData, update it instead:
curl --request PUT \
--header "Authorization: Bearer $API_TOKEN" \
--header 'Content-Type: application/vnd.gooddata.api+json' \
--data "$PAYLOAD" \
"$HOST_URL/api/v1/entities/users/$USER_LOGIN"For more information about users and user groups, see Manage Users.
Log In to GoodData
Open your organization URL in a web browser and start the sign-in flow.
GoodData redirects the user to the Cognito hosted sign-in page. Log in with the Cognito username and temporary password. On the first successful login, Cognito prompts the user to change the password.
Cleanup
When you are done testing, you can remove the AWS resources created in this example:
aws cognito-idp delete-user-pool-domain \
--user-pool-id "$USERPOOL_ID" \
--domain "$COGNITO_DOMAIN_PREFIX"
aws cognito-idp delete-user-pool \
--user-pool-id "$USERPOOL_ID"If you also want to remove the GoodData identity provider object, first switch the organization to a different active identity provider, and then delete the Cognito identity provider:
curl --request DELETE \
--header "Authorization: Bearer $API_TOKEN" \
"$HOST_URL/api/v1/entities/identityProviders/$IDP_ID"