Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to authenticate against an AWS Cognito User Pool in Python?

I have a static serverless website that allows authentication with Javascript using an AWS Cognito User Pool.

Now I'm trying to enable some programmatic access so I need to do this same authentication via a Python script. Is this possible? The docs don't provide any code examples for Python.

I'm just trying to find some way for Python to issue a GET or POST request against an AWS URL, passing it a username and login, and getting back the signed cookies verifying authentication.

The closest example I've found is this code, which references the cognito-idp API. I've modified to:

import boto3
client_id = '<my_app_client_id>'
region_name = 'us-east-1'
auth_data = { 'USERNAME':'myusername' , 'PASSWORD':'mypassword' }
provider_client = boto3.client('cognito-idp', region_name=region_name)
resp = provider_client.initiate_auth(AuthFlow='USER_PASSWORD_AUTH', AuthParameters=auth_data, ClientId=client_id)
print('resp:', resp)

However, even though I use the same credentials as through the Javascript API, this fails to authenticate and simply returns the error:

botocore.exceptions.NoCredentialsError: Unable to locate credentials

This this the correct Python equivalent as the Javascript Cognito API?

like image 910
Cerin Avatar asked Dec 18 '25 19:12

Cerin


1 Answers

The following snippet shows a complete authentication workflow with Cognito using boto3.

def get_secret_hash(username):
    msg = username + CLIENT_ID
    dig = hmac.new(
        str(CLIENT_SECRET).encode('utf-8'),
        msg=str(msg).encode('utf-8'),
        digestmod=hashlib.sha256
    ).digest()
    d2 = base64.b64encode(dig).decode()
    return d2

def initiate_auth(client, username, password):
    secret_hash = get_secret_hash(username)
    try:
        resp = client.admin_initiate_auth(
            UserPoolId=USER_POOL_ID,
            ClientId=CLIENT_ID,
            AuthFlow='ADMIN_NO_SRP_AUTH',
            AuthParameters={
                'USERNAME': username,
                'SECRET_HASH': secret_hash,
                'PASSWORD': password,
            },
            ClientMetadata={
                'username': username,
                'password': password, })
    except client.exceptions.NotAuthorizedException:
        return None, "The username or password is incorrect"
    except client.exceptions.UserNotConfirmedException:
        return None, "User is not confirmed"
    except Exception as e:
        return None, e.__str__()
    return resp, None

@app.route('/auth/login', methods=['POST'])
def login():
    event = auth.current_request.json_body
    client = boto3.client('cognito-idp')
    username = event['username']
    password = event['password']
    for field in ["username", "password"]:
        if event.get(field) is None:
            return {"error": True,
                    "success": False,
                    "message": f"{field} is required",
                    "data": None}
    resp, msg = initiate_auth(client, username, password)
    if msg != None:
        return {'message': msg,
                "error": True, "success": False, "data": None}
    if resp.get("AuthenticationResult"):
        return {'message': "success",
                "error": False,
                "success": True,
                "data": {
                    "id_token": resp["AuthenticationResult"]["IdToken"],
                    "refresh_token": resp["AuthenticationResult"]["RefreshToken"],
                    "access_token": resp["AuthenticationResult"]["AccessToken"],
                    "expires_in": resp["AuthenticationResult"]["ExpiresIn"],
                    "token_type": resp["AuthenticationResult"]["TokenType"]
                }}
    else:  # this code block is relevant only when MFA is enabled
        return {"error": True,
                "success": False,
                "data": None, "message": None}

Here's the important part parsed from the functions.

       resp = client.admin_initiate_auth(
            UserPoolId=USER_POOL_ID,
            ClientId=CLIENT_ID,
            AuthFlow='ADMIN_NO_SRP_AUTH',
            AuthParameters={
                'USERNAME': username,
                'SECRET_HASH': secret_hash,
                'PASSWORD': password,
            },
            ClientMetadata={
                'username': username,
                'password': password, })

The examples were taken from a four part tutorial that unfortunately didn't help me integrate this with the Chalice CognitoUserPoolAuthorizer but otherwise seems to work well. Here are the tutorials if you can't find better code examples.

  • part 1
  • part 2
  • part 3
  • part 4
like image 66
infosmith Avatar answered Dec 20 '25 12:12

infosmith



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!