phpamazon-web-servicesamazon-cognitounauthorized

Cognito php password SRP verifier challenge returns incorrect username and password


I have a PHP webpage in which I have configured the aws sdk and instantiated the cognito client The client is configured with a client secret as well and the auth flows USER_PASSWORD_AUTH, USER_SRP_AUTH are enabled. The initiateAuth with the basic auth flow type USER_PASSWORD_AUTH works fine for me. Below is the sample usage I tried -

$result = $cognito->initiateAuth([
'AuthFlow' => 'USER_PASSWORD_AUTH',
'ClientId' => '<client_id>',
'UserPoolId' => '<pool_id>',
'AuthParameters' => [
'USERNAME' => '<username>',
'PASSWORD' => "<password>",
'SECRET_HASH' => base64_encode(hash_hmac('sha256', '<username>' . '<client_id>', '<client_secret>', true))
]
]);

With this we are getting a successful response with access and id token, but when we try with the same parameters with the USER_SRP_AUTH method like this -

$srp = new srp();
$a = $srp->getRandomSeed();
$A = $srp->generateA($a);
$result = $cognito->initiateAuth([
'AuthFlow' => 'USER_SRP_AUTH',
'ClientId' => '<client_id>',
'UserPoolId' => '<pool_id>',
'AuthParameters' => [
'USERNAME' => '<username>',
'SRP_A' => $A,
'SECRET_HASH' => base64_encode(hash_hmac('sha256', '<username>' . '<client_id>', '<client_secret>', true))
]
]);
$date = date('D M d H:i:s')." UTC 2021";
$challengeParameters = $result->get("ChallengeParameters");
$s = $srp->getRandomSeed();
$x = $srp->generateX($s, $challengeParameters['USER_ID_FOR_SRP'], '<password>');
$S = $srp->generateS_Client($A, $challengeParameters['SRP_B'], $a, $x);
$K = $srp->generateK($S);
$response = $cognito->respondToAuthChallenge([
"ChallengeName" => "PASSWORD_VERIFIER",
"ClientId" => '<client_id>',
"ChallengeResponses" => [
"TIMESTAMP" => $date,
"USERNAME" => $challengeParameters['USER_ID_FOR_SRP'],
"PASSWORD_CLAIM_SECRET_BLOCK" => $challengeParameters['SECRET_BLOCK'],
"PASSWORD_CLAIM_SIGNATURE" => hash_hmac('sha256', $K, $challengeParameters['SALT']),
'SECRET_HASH' => base64_encode(hash_hmac('sha256', $challengeParameters['USER_ID_FOR_SRP'] . '<client_id>', '<client_secret>', true))
]
]);

But with this approach I always get this error -

NotAuthorizedException Error executing "RespondToAuthChallenge" on "https://cognito-idp.us-east-1.amazonaws.com"; AWS HTTP error: Client error: `POST https://cognito-idp.us-east-1.amazonaws.com` resulted in a `400 Bad Request` response: {"__type":"NotAuthorizedException","message":"Incorrect username or password."} NotAuthorizedException (client): Incorrect username or password. - {"__type":"NotAuthorizedException","message":"Incorrect username or password."}

Now I have also tried replacing challengeParams['USER_ID_FOR_SRP'] with <username> but still I get the same error.

So, can anyone here help me figure out what the issue is and probably try to help solve it as well ?


Solution

  • The problem with SRP in Cognito is that it's not the SRP according to RFC5054.

    If you are using any standard library to deal with SRP for Cognito purposes, it's not going to work. Also, the PHP's SDK does not have support fro SRP.

    Here is how SRP according to RFC5054 works (very simplified):

    Client:

    Server:

    Client:

    Server:

    Client:

    When both parties are able to confirm that the session key they have is valid, the user is assumed to be authenticated.

    And here is how it looks like in Cognito:

    Client:

    Server:

    At this point we can already see this is different than the RFC. This secret block is something specific to Cognito and not to RFC. And therefore any calculations that will follow need to be done differently.

    The implementation is rather lengthy so I'm going to paste a link to Gist. It's a port by Lynh from Python which in turn is based on AWS SDK sample for Java:

    https://gist.github.com/jenky/a4465f73adf90206b3e98c3d36a3be4f