Closed efimenkop closed 2 years ago
Hi @efimenkop,
Good morning.
Thanks for posting guidance question.
For CognitoUser. StartWithSrpAuthAsync(), the logic seems to automatically create RespondToAuthChallengeRequest
and responding to auth challenge. And you are right, for this scenarios, the library calculates the values for properties PASSWORD_CLAIM_SECRET_BLOCK
and PASSWORD_CLAIM_SIGNATURE
internally.
For your case, you could take some cue from CreateSrpPasswordVerifierAuthRequest on how the PASSWORD_CLAIM_SECRET_BLOCK
and PASSWORD_CLAIM_SIGNATURE
are calculated.
Thanks, Ashish
Hello, @ashishdhingra!
Maybe it worth making CreateSrpPasswordVerifierAuthRequest
public (or expose the logic for creating PASSWORD_CLAIM_SECRET_BLOCK
and PASSWORD_CLAIM_SIGNATURE
in some other way)?
Otherwise I have to copy-paste big piece of library's logic.
@efimenkop Thanks for your reply. Please advise if it would be possible for you to test the extracted code for your scenario and if it works, may be submit a PR to refactor existing code as contribution.
This issue has not recieved a response in 1 week. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.
Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.
This should not be closed @efimenkop. It is still a problem to implement Custom Flow with SRP. Even copy pasting a lot of the code still gives an error: Amazon.CognitoIdentityProvider.Model.NotAuthorizedException: "Incorrect username or password".
After 6 hours of debugging I can't find any mistakes. SRP normal flow is working as expected, custom + SRP is not.
It funny because it is even one of the scenarios described in the official documentation:
But the SDK fails to use SRP on custom authentication flows.
I get:
I came here looking for a .NET solution to integrate the email MFA I setup from this guide
Which is very similar to the OP.
I have a solution that integrates with the above custom authentication implementation if anyone is interested.
I included a new property IsCustomAuthFlow to class InitiateSrpAuthRequest:
/// <summary>
/// Class containing the necessary properities to initiate SRP authentication flow
/// </summary>
public class InitiateSrpAuthRequest
{
/// <summary>
/// The password for the corresponding CognitoUser.
/// </summary>
public string Password { get; set; }
/// <summary>
/// The password for the device associated with the corresponding CognitoUser
/// </summary>
public string DevicePass { get; set; }
/// <summary>
/// The device password verifier for the device associated with the corresponding CognitoUser
/// </summary>
public string DeviceVerifier { get; set; }
/// <summary>
/// The Device Key Group for the device associated with the corresponding CognitoUser
/// </summary>
public string DeviceGroupKey { get; set; }
/// <summary>
/// Use the custom auth flow with this SRP request
/// </summary>
public bool IsCustomAuthFlow { get; set; }
}
I then modified the method StartWithSrpAuthAsync on class CognitoUser to set the AuthFlow to CUSTOM_AUTH and included an AuthParameter of CHALLENGE_NAME = SRP_A on the initiateRequest, when the new property IsCustomAuthFlow is true.
/// <summary>
/// Initiates the asynchronous SRP authentication flow
/// </summary>
/// <param name="srpRequest">InitiateSrpAuthRequest object containing the necessary parameters to
/// create an InitiateAuthAsync API call for SRP authentication</param>
/// <returns>Returns the AuthFlowResponse object that can be used to respond to the next challenge,
/// if one exists</returns>
public virtual async Task<AuthFlowResponse> StartWithSrpAuthAsync(InitiateSrpAuthRequest srpRequest)
{
if (srpRequest == null || string.IsNullOrEmpty(srpRequest.Password))
{
throw new ArgumentNullException("Password required for authentication.", "srpRequest");
}
Tuple<BigInteger, BigInteger> tupleAa = AuthenticationHelper.CreateAaTuple();
InitiateAuthRequest initiateRequest = CreateSrpAuthRequest(tupleAa);
// change this to custom
if (srpRequest.IsCustomAuthFlow)
{
initiateRequest.AuthFlow = AuthFlowType.CUSTOM_AUTH;
initiateRequest.AuthParameters.Add("CHALLENGE_NAME", "SRP_A");
}
InitiateAuthResponse initiateResponse = await Provider.InitiateAuthAsync(initiateRequest).ConfigureAwait(false);
UpdateUsernameAndSecretHash(initiateResponse.ChallengeParameters);
RespondToAuthChallengeRequest challengeRequest =
CreateSrpPasswordVerifierAuthRequest(initiateResponse, srpRequest.Password, tupleAa);
bool challengeResponsesValid = challengeRequest != null && challengeRequest.ChallengeResponses != null;
bool deviceKeyValid = Device != null && !string.IsNullOrEmpty(Device.DeviceKey);
if (challengeResponsesValid && deviceKeyValid)
{
challengeRequest.ChallengeResponses[CognitoConstants.ChlgParamDeviceKey] = Device.DeviceKey;
}
RespondToAuthChallengeResponse verifierResponse =
await Provider.RespondToAuthChallengeAsync(challengeRequest).ConfigureAwait(false);
var isDeviceAuthRequest = verifierResponse.AuthenticationResult == null && (!string.IsNullOrEmpty(srpRequest.DeviceGroupKey)
|| !string.IsNullOrEmpty(srpRequest.DevicePass));
#region Device-level authentication
if (isDeviceAuthRequest)
{
if (string.IsNullOrEmpty(srpRequest.DeviceGroupKey) || string.IsNullOrEmpty(srpRequest.DevicePass))
{
throw new ArgumentNullException("Device Group Key and Device Pass required for authentication.", "srpRequest");
}
#region Device SRP Auth
var deviceAuthRequest = CreateDeviceSrpAuthRequest(verifierResponse, tupleAa);
var deviceAuthResponse = await Provider.RespondToAuthChallengeAsync(deviceAuthRequest).ConfigureAwait(false);
#endregion
#region Device Password Verifier
var devicePasswordChallengeRequest = CreateDevicePasswordVerifierAuthRequest(deviceAuthResponse, srpRequest.DeviceGroupKey, srpRequest.DevicePass, tupleAa);
verifierResponse = await Provider.RespondToAuthChallengeAsync(devicePasswordChallengeRequest).ConfigureAwait(false);
#endregion
}
#endregion
UpdateSessionIfAuthenticationComplete(verifierResponse.ChallengeName, verifierResponse.AuthenticationResult);
return new AuthFlowResponse(verifierResponse.Session,
verifierResponse.AuthenticationResult,
verifierResponse.ChallengeName,
verifierResponse.ChallengeParameters,
new Dictionary<string, string>(verifierResponse.ResponseMetadata.Metadata));
}
Inside your code you will need to respond to the custom auth with a response like the following:
var challengeResponses = new Dictionary<string, string>();
challengeResponses.Add("ANSWER", authenticationCode);
challengeResponses.Add("USERNAME", userName);
var request = new RespondToCustomChallengeRequest()
{
ChallengeParameters = challengeResponses,
SessionID = sessionID
};
await user.RespondToCustomAuthAsync(request).ConfigureAwait(false);
The Question
I'm looking for a best way to implement a Custom Authentication Flow which consists of two steps:
The problem is that SRP Password Verification requires next parameters:
PASSWORD_CLAIM_SIGNATURE
,PASSWORD_CLAIM_SECRET_BLOCK
andTIMESTAMP
, but seems like library calculates these properties internally and there is no way to get them from the caller code. Am I missing something?