Use Amazon Cognito as Identity Provider

In this article, you will learn the basic setup of Amazon Cognito as an Identity Provider for GoodData. In order to keep this example reproducible and universal, the commands below are written in Bash shell and use AWS CLI version 2. If you use a different deployment tool (like CloudFormation or Terraform), or if you decide to configure your Amazon Congito manually using AWS Console, you’ll need to adapt these scipts.

Set up shared environment

Set the following environment variables in the shell you will later use for running the aws-cli commands. Adjust values according to your conditions.

VARIABLEExample valueExplanation
AWS_REGIONeu-central-1AWS region where Cognito will be deployed
ORG_HOSTNAMEexample.gooddata.comHostname of GoodData organization
ORGANIZATION_IDalphaOrganization ID
API_TOKENYWRtaW46Ym9vdHN0cmFwOmRlbW8xMjM=Token with MANAGE permission on your Organization
ORG_PORT""Set to port number if it differs from 80 or 443
ORG_SCHEMAhttpsOrganization URL schema, http or https
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 HOST_URL="${ORG_SCHEMA}://${ORG_HOSTNAME}${ORG_PORT:+:$ORG_PORT}"

User pool configuration

Create Cognito user pool

The following command creates AWS Cognito User pool. It will be called gdcn-demo and will use simple password policy:

  • minimum 8 characters
  • must use upper case, lower case, and numeric characters
  • assigned temporary password will be valid for 7 days

To make this example simple, new accounts will have their e-mail addresses set as verified, multi-factor authentication will be turned off and e-mail communication will be sent from aws-provided account.

More importantly, name user attribute is set as required to make sure it is always present for every user. This attribute will be retrieved from user id token by GoodData to get information about user that just logged in.

The resulting id of created user pool will be stored in environment variable USERPOOL_ID that will be used later.

USERPOOL_ID=$( \
    aws cognito-idp create-user-pool --pool-name gdcn-demo \
    --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 Cognito domain

Cognito needs a domain where login form UI elements and OAuth2 service will be hosted. We will use AWS-hosted domain. The resulting URL will be https://<<domain>>.auth.<<region>>.amazoncognito.com/. It is also possible to use custom domain, like https://auth.gooddata.com/. Refer to AWS documentation for details.

This command will create domain https://gdcn-demo.auth.eu-central-1.amazoncognito.com/:

aws cognito-idp create-user-pool-domain \
--user-pool-id "${USERPOOL_ID}" --domain gdcn-demo

Create cognito user pool client

In order to use this user pool as a source of truth for user authententication, we need to create OAuth 2.0 client. This command will make such client that will be later configured in GoodData Organization.

read -a POOL_CLIENT < <(aws cognito-idp create-user-pool-client \
    --user-pool-id "$USERPOOL_ID" --client-name gdcn-client --generate-secret \
    --supported-identity-providers COGNITO \
    --callback-urls "$HOST_URL/login/oauth2/code/$ORG_HOSTNAME" \
    --allowed-o-auth-flows-user-pool-client \
    --allowed-o-auth-scopes email openid profile --allowed-o-auth-flows code \
    --query 'UserPoolClient.[ClientSecret, ClientId]' --output text)

echo "Created Client with id='${POOL_CLIENT[1]}' and secret='${POOL_CLIENT[0]}'"

Variable ${POOL_CLIENT[0]} contains client secret and ${POOL_CLIENT[1]} contains client id.

Configure Cognito IdP in Organization

Update GoodData organization with OAuth 2.0 client you just created. You will need to prepare API request payload containing information retrieved in previous steps:

  • oauthClientId - ID of OAuth 2.0 client, stored in ${POOL_CLIENT[1]} variable
  • oauthClientSecret - client secret, stored in ${POOL_CLIENT[0]} variable
  • oauthIssuerLocation - URL of oauth issuer. For Cognito, it has always format https://cognito-idp.<<aws-region>>.amazonaws.com/<<user-pool-id>>
# prepare request payload
read -r -d '' PAYLOAD <<EOF
{
  "data": {
    "id": "$ORGANIZATION_ID",
    "type": "organization",
    "attributes": {
      "name": "Cognito Auth Demo",
      "hostname": "$ORG_HOSTNAME",
      "oauthIssuerLocation": "https://cognito-idp.$AWS_REGION.amazonaws.com/$USERPOOL_ID",
      "oauthClientId": "${POOL_CLIENT[1]}",
      "oauthClientSecret": "${POOL_CLIENT[0]}"
    }
  }
}
EOF

# Update organization
curl -X PUT -H "Authorization: Bearer $API_TOKEN" \
    -H 'Content-Type: application/vnd.gooddata.api+json' -d "$PAYLOAD" \
    $HOST_URL/api/v1/entities/admin/organizations/$ORGANIZATION_ID

Provision user to user pool

There are multiple ways how to add users into Cognito user pool. In this example, we create a new user using admin-create-user API, it’s also possible to import users from CSV file. Refer to AWS documentation for further details.

# Set up variables
USER_EMAIL="john.doe@example.com"
USER_NAME="John Doe"
USER_LOGIN="john.doe"
# Note the password must conform to user pool password policy
USER_PASSWORD="NewRandomPa33word"

# Create first user
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 --output text \
    --query 'User.Attributes[?Name==`sub`].Value[]')

echo "Created user with login='$USER_LOGIN' and password='$USER_PASSWORD'"

Link Cognito user with GoodData user

# prepare request payload
read -r -d '' PAYLOAD <<EOF
{
  "data": {
    "id": "$USER_LOGIN",
    "type": "user",
    "attributes": {
      "authenticationId": "$EXTERNAL_ID"
    },
    "relationships": {
      "userGroups": {
        "data": [{
          "id": "adminGroup",
          "type": "userGroup"
        }]
      }
    }
  }
}
EOF

# Create user entity
curl -X POST -H "Authorization: Bearer $API_TOKEN" \
    -H 'Content-Type: application/vnd.gooddata.api+json' \
    -d "$PAYLOAD" $HOST_URL/api/v1/entities/users

# ... or update existing user
curl -X PUT -H "Authorization: Bearer $API_TOKEN" \
    -H 'Content-Type: application/vnd.gooddata.api+json' \
    -d "$PAYLOAD" $HOST_URL/api/v1/entities/users/$USER_LOGIN

Log in to GoodData

Now you can use web browser to log in to your GoodData Organization. Just navigate to Organization URL (https://example.gooddata.com in previous example) and enter user login and initial password. On the first login, the password needs to be changed by user.

Cleanup

When you’re done with test, you may want to delete AWS resources you created. Use these commands to delete user pool domain and user pool itself.

aws cognito-idp delete-user-pool-domain --user-pool-id $USERPOOL_ID --domain gdcn-demo
aws cognito-idp delete-user-pool --user-pool-id $USERPOOL_ID

Known Limitations

RP Initiated Logout does not work at the moment. We plan to implement the functionality in a future release.