Cognito - AdminInitiateAuth fails with NotAuthorizedException

We are currently implementing an app and its corresponding backend with three options to login / signup, Google, Apple and via Email/Password.

When a user chooses Apple or Google and the user is not existing in Cognito, we are creating the user with admin_create_user of boto3.client.

So far so good, the user is created.

When it comes to handling the login, we are encountering problems with the CUSTOM_AUTH for admin_initiate_auth:

    try:
        initiate_resp = cognito_client.admin_initiate_auth(
            UserPoolId=settings.AWS_COGNITO_USER_POOL_ID,
            ClientId=settings.AWS_COGNITO_CLIENT_ID,
            AuthFlow="CUSTOM_AUTH",
            AuthParameters={
                "USERNAME": email,
                "SECRET_HASH": get_secret_hash(email)
            }
        )
        print(initiate_resp)
    except ClientError as e:
        return {"error": f"admin_initiate_auth failed: {str(e)}"}, 400

We have added, as per documentation the lambda triggers to for

  • define_auth_challenge:
def lambda_handler(event, context):
    """
    Cognito calls this to decide which challenge to present next (or issue tokens).
    We'll always present a CUSTOM_CHALLENGE if there's no successful challenge yet.
    """

    # event["request"]["session"] contains prior challenge attempts.
    # If empty, it's the user's first attempt this auth session.
    session_list = event["request"].get("session", [])

    if len(session_list) == 0:
        # First time => present the custom challenge
        event["response"]["challengeName"] = "CUSTOM_CHALLENGE"
        event["response"]["issueTokens"] = False
        event["response"]["failAuthentication"] = False
        print("DefineAuthChallenge -> Presenting CUSTOM_CHALLENGE (first attempt)")

    else:
        # Check the last challenge
        last_challenge = session_list[-1]
        if last_challenge.get("challengeResult") is True:
            # If they answered correctly, issue tokens
            event["response"]["challengeName"] = "CUSTOM_CHALLENGE"
            event["response"]["issueTokens"] = True
            event["response"]["failAuthentication"] = False
            print("DefineAuthChallenge -> Last challenge success, issuing tokens")

        else:
            # If they failed or haven't answered, prompt again
            event["response"]["challengeName"] = "CUSTOM_CHALLENGE"
            event["response"]["issueTokens"] = False
            event["response"]["failAuthentication"] = False
            print("DefineAuthChallenge -> Re-presenting CUSTOM_CHALLENGE")

    return event
  • create_auth_challenge:
def lambda_handler(event, context):
    challenge_name = event["request"]["challengeName"]
    print(challenge_name)
    print(event)
    """
    Invoked after Cognito decides to present a CUSTOM_CHALLENGE,
    giving you a chance to store or define challenge parameters.
    For a Google token check, we usually don't need to store anything.
    """
    if challenge_name != "CUSTOM_CHALLENGE":
        return event
        
    event["response"]["publicChallengeParameters"] = {}
    event["response"]["privateChallengeParameters"] = {}
    event["response"]["challengeMetadata"] = "CUSTOM_CHALLENGE_METADATA"
    print("CreateAuthChallenge -> Setting empty challenge parameters for Google token flow")


    return event

and

  • verify_auth_challenge
import json
import urllib.request

def lambda_handler(event, context):
    challengeName = event["request"]["challengeName"]
    print("Verifying started...")
    """
    Verifies the challenge answer (the Google ID token).
    If it's valid, we set 'answerCorrect' to True. Cognito then issues tokens.
    """

    if challengeName == "CUSTOM_CHALLENGE":
        provided_token = event["request"].get("challengeAnswer")
        if verify_google_id_token(provided_token):
            event["response"]["answerCorrect"] = True
            print("VerifyAuthChallenge -> Google token valid, answerCorrect=True")
        else:
            event["response"]["answerCorrect"] = False
            print("VerifyAuthChallenge -> Google token invalid, answerCorrect=False")


    return event


def verify_google_id_token(id_token: str) -> bool:
    """Verify the Google ID token by calling the tokeninfo endpoint."""
    if not id_token:
        return False

    url = f"https://oauth2.googleapis.com/tokeninfo?id_token={id_token}"

    try:
        with urllib.request.urlopen(url) as resp:
            if resp.status != 200:
                return False
            data = json.loads(resp.read())
    except Exception as e:
        print("Error verifying Google token:", e)
        return False

    if "error" in data:
        return False

    # Optionally check 'aud' == your Google client ID, 
    # 'exp' is in the future, 'email_verified' == "true", etc.
    # Example check:
    # if data.get("aud") != "YOUR_GOOGLE_CLIENT_ID":
    #     return False

    return True

When I try to login with my id_token, I receive following error:

[
    {
        "error": "admin_initiate_auth failed: An error occurred (NotAuthorizedException) when calling the AdminInitiateAuth operation: Incorrect username or password."
    },
    400
]

Does anyone has an advice? Would be much appreciated!

Вернуться на верх