Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a SECRET_HASH for AWS Cognito using boto3?

I want to create/calculate a SECRET_HASH for AWS Cognito using boto3 and python. This will be incorporated in to my fork of warrant.

I configured my cognito app client to use an app client secret. However, this broke the following code.

def renew_access_token(self):
    """
    Sets a new access token on the User using the refresh token.

    NOTE:
    Does not work if "App client secret" is enabled. 'SECRET_HASH' is needed in AuthParameters.
    'SECRET_HASH' requires HMAC calculations.

    Does not work if "Device Tracking" is turned on.
    https://stackoverflow.com/a/40875783/1783439

    'DEVICE_KEY' is needed in AuthParameters. See AuthParameters section.
    https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
    """
    refresh_response = self.client.initiate_auth(
        ClientId=self.client_id,
        AuthFlow='REFRESH_TOKEN',
        AuthParameters={
            'REFRESH_TOKEN': self.refresh_token
            # 'SECRET_HASH': How to generate this?
        },
    )

    self._set_attributes(
        refresh_response,
        {
            'access_token': refresh_response['AuthenticationResult']['AccessToken'],
            'id_token': refresh_response['AuthenticationResult']['IdToken'],
            'token_type': refresh_response['AuthenticationResult']['TokenType']
        }
    )

When I run this I receive the following exception:

botocore.errorfactory.NotAuthorizedException: 
An error occurred (NotAuthorizedException) when calling the InitiateAuth operation: 
Unable to verify secret hash for client <client id echoed here>.

This answer informed me that a SECRET_HASH is required to use the cognito client secret.

The aws API reference docs AuthParameters section states the following:

For REFRESH_TOKEN_AUTH/REFRESH_TOKEN: USERNAME (required), SECRET_HASH (required if the app client is configured with a client secret), REFRESH_TOKEN (required), DEVICE_KEY

The boto3 docs state that a SECRET_HASH is

A keyed-hash message authentication code (HMAC) calculated using the secret key of a user pool client and username plus the client ID in the message.

The docs explain what is needed, but not how to achieve this.

like image 650
nu everest Avatar asked May 29 '17 14:05

nu everest


People also ask

How do I use AWS Cognito in Python?

Sign-up using AWS Cognito, Python SDK Boto3 In the current directory where you have the above code, create a file called . env-sample . In this file, you should have the macro COGNITO_USER_CLIENT_ID with the client ID from General Settings > App Client > App client id. The above will be picked using the dotenv module.

How do I customize my AWS Cognito login page?

To specify app UI customization settingsSign in to the Amazon Cognito console . In the navigation pane, choose User Pools, and choose the user pool you want to edit. Choose the App integration tab. To customize UI settings for all app clients, locate Hosted UI customization and select Edit.

Can Cognito be an IdP?

However, a Cognito user pool is its own IdP. If an identity pool is configured correctly, it can use the app's user pools as an IdP. This way, users authenticate via user pools and are assigned IAM roles via identity pools.


1 Answers

The below get_secret_hash method is a solution that I wrote in Python for a Cognito User Pool implementation, with example usage:

import boto3
import botocore
import hmac
import hashlib
import base64


class Cognito:
    client_id = app.config.get('AWS_CLIENT_ID')
    user_pool_id = app.config.get('AWS_USER_POOL_ID')
    identity_pool_id = app.config.get('AWS_IDENTITY_POOL_ID')
    client_secret = app.config.get('AWS_APP_CLIENT_SECRET')
    # Public Keys used to verify tokens returned by Cognito:
    # http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-identity-user-pools-using-id-and-access-tokens-in-web-api
    id_token_public_key = app.config.get('JWT_ID_TOKEN_PUB_KEY')
    access_token_public_key = app.config.get('JWT_ACCESS_TOKEN_PUB_KEY')

    def __get_client(self):
        return boto3.client('cognito-idp')

    def get_secret_hash(self, username):
        # A keyed-hash message authentication code (HMAC) calculated using
        # the secret key of a user pool client and username plus the client
        # ID in the message.
        message = username + self.client_id
        dig = hmac.new(self.client_secret, msg=message.encode('UTF-8'),
                       digestmod=hashlib.sha256).digest()
        return base64.b64encode(dig).decode()

    # REQUIRES that `ADMIN_NO_SRP_AUTH` be enabled on Client App for User Pool
    def login_user(self, username_or_alias, password):
        try:
            return self.__get_client().admin_initiate_auth(
                UserPoolId=self.user_pool_id,
                ClientId=self.client_id,
                AuthFlow='ADMIN_NO_SRP_AUTH',
                AuthParameters={
                    'USERNAME': username_or_alias,
                    'PASSWORD': password,
                    'SECRET_HASH': self.get_secret_hash(username_or_alias)
                }
            )
        except botocore.exceptions.ClientError as e:
            return e.response
like image 129
afilbert Avatar answered Oct 20 '22 09:10

afilbert