aws / aws-sdk-net-extensions-cognito

An extension library to assist in the Amazon Cognito User Pools authentication process
Apache License 2.0
102 stars 49 forks source link

DEVICE_PASSWORD_VERIFIER - NotAuthorizedException: Incorrect username or password #73

Closed RyanWarwick closed 1 year ago

RyanWarwick commented 3 years ago

Description

Using v2.2.1 of aws-sdk-net-extensions-cognito and attempting to auth using remembered device results in NotAuthorizedException: Incorrect username or password error, at DEVICE_PASSWORD_VERIFIER code segment in CognitoUserAuthentication.cs

Reproduction Steps

  1. Basic login using user credentials
  2. DeviceSecretVerifierConfigType deviceSecretVerifierConfig = user.GenerateDeviceVerifier(deviceGroupKey, devicePass, username);
  3. await user.ConfirmDeviceAsync(accessToken, deviceKey, deviceName, deviceSecretVerifierConfig.PasswordVerifier, deviceSecretVerifierConfig.Salt).ConfigureAwait(false); (returns OK)
  4. CognitoDevice device = new CognitoDevice(new Amazon.CognitoIdentityProvider.Model.DeviceType() { DeviceKey = deviceKey }, user); user.Device = device; InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest() { Password = password, DeviceGroupKey =deviceGroupKey, DevicePass = devicePass, }; AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);

    which internally calls GetDeviceAuthenticationKey() in AuthenticationHelper.AuthenticateDevice()

  5. Returns NotAuthorizedException: Incorrect username or password error

Environment

Resolution

  1. Use deviceKey instead of Username when calling user.GenerateDeviceVerifier()
  2. Use deviceKey instead of Username when internally calling GetDeviceAuthenticationKey in AuthenticationHelper.AuthenticateDevice()

Correct syntax example : DeviceAuthIssue1

Welchen commented 2 years ago

Any update on this? We are waiting on this for a feature.

jimmyherron commented 2 years ago

Can anybody describe in simple terms how this flow is supposed to work and debugging steps for the maths?

I've been struggling for weeks looking for a working version of device auth for python, but even if it worked in .net I could potentially port it. The problem is that the docs are extremely unclear, even following https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/ explicitly always results in an incorrect username or password issue.

mads195 commented 2 years ago

From what we've been able to establish, the DEVICE_SRP_AUTH flow in .net either isn't implemented or is undocumented. We reliably get 1 of 2 outcomes:

  1. NotAuthorizedException
  2. DEVICE_SRP_AUTH challenge

On the former, we confirm the device using the below:

DeviceSecretVerifierConfigType d = user.GenerateDeviceVerifier(vDeviceGroupKey, "A-RANDOM-PASSWORD", vDeviceKey)
ConfirmDeviceResponse r = await user.ConfirmDeviceAsync(vAccessToken, vDeviceKey, "deviceName", d.PasswordVerifier, d.Salt)

On a subsequent sign-in, we then do this:

CognitoDevice device = new CognitoDevice(new Amazon.CognitoIdentityProvider.Model.DeviceType()
                    {
                        DeviceKey = "eu-west-1_2a0ce014-ef11-4209-bae1-69efba93490f"
                    }, user);
                    user.Device = device;

                    AuthFlowResponse context = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest()
                    {
                        Password = password,
                        DeviceGroupKey = "GROUP-KEY-OBTAINED-FROM-INITIAL-SIGN-IN",
                        DevicePass = "PASSWORD-SET-WHEN-GENERATING-DEVICE-VERIFIER",
                    }).ConfigureAwait(false);

The response to the above, is NotAuthorizedException. If you leave out specifying DeviceGroupKey and DevicePass, you get a DEVICE_SRP_AUTH challenge but unlike NEW_PASSWORD_REQUIRED, MFA & Custom responses, there is no handler.

We've probably got to the point that we'll go our own route for managing devices which is a shame but we're losing the will to live with this!!

jimmyherron commented 2 years ago

HI @mads195

After bashing my head against it for far too long, reading all the code examples from both Amplify js and other sdks, I gave up. It seems this simply does not work or there is misinformation in the docs, or a combination of the two.

I was also able to get the DEVICE_SRP_AUTH challenge and then subsequent password verification challenge which always returned Not Authorized, no matter what combination of device key, username, passwords, etc I tried.

At this point, short of raising an AWS support ticket I have to assume the functionality is broken in the cognito service. The JavaScript SDK amplify seems 'closest' to what is required, but JS doesn't support client_secret either (rightly) so with zero working examples and next to no docs, I don't see a way forward until I hear from AWS.

Thanks for following up, appreciate that I'm not the only one stuck here :)

dscpinheiro commented 1 year ago

Hi everyone,

Sorry for the delay in the response, but we're investigating this issue again.

I've tried to reproduce the error using the latest version of this library (https://www.nuget.org/packages/Amazon.Extensions.CognitoAuthentication/2.2.2) and the following code, but it's returning a valid access token (I haven't seen the NotAuthorizedException happen yet).

record UserAuthResult(string RefreshToken, string DeviceGroupKey, string DeviceKey);

var result = await AuthenticateUserWithSrp();
await AuthenticateDeviceWithSrp(result);

async Task<UserAuthResult> AuthenticateUserWithSrp()
{
    using var provider = new AmazonCognitoIdentityProviderClient();
    var userPool = new CognitoUserPool(POOL_ID, CLIENT_ID, provider);
    var user = new CognitoUser(USER_NAME, CLIENT_ID, userPool, provider, CLIENT_SECRET);

    var authResponse = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest
    {
        Password = USER_PWD
    });

    while (authResponse.AuthenticationResult is null)
    {
        if (authResponse.ChallengeName == ChallengeNameType.NEW_PASSWORD_REQUIRED)
        {
            Console.WriteLine("Enter your desired new password:");
            var newPassword = Console.ReadLine();

            authResponse = await user.RespondToNewPasswordRequiredAsync(new RespondToNewPasswordRequiredRequest
            {
                SessionID = authResponse.SessionID,
                NewPassword = newPassword
            });
        }
        else if (authResponse.ChallengeName == ChallengeNameType.SMS_MFA)
        {
            Console.WriteLine("Enter the MFA Code sent to your device:");
            var mfaCode = Console.ReadLine();

            authResponse = await user.RespondToSmsMfaAuthAsync(new RespondToSmsMfaRequest
            {
                SessionID = authResponse.SessionID,
                MfaCode = mfaCode
            });
        }
        else
        {
            Console.WriteLine("Unrecognized authentication challenge.");
            break;
        }
    }

    var accessToken = authResponse.AuthenticationResult.AccessToken;
    var deviceKey = authResponse.AuthenticationResult.NewDeviceMetadata.DeviceKey;
    var deviceGroupKey = authResponse.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey;

    var deviceVerifier = user.GenerateDeviceVerifier(deviceGroupKey, DEVICE_PWD, USER_NAME);
    await user.ConfirmDeviceAsync(accessToken, deviceKey, DEVICE_NAME, deviceVerifier.PasswordVerifier, deviceVerifier.Salt);

    return new UserAuthResult(authResponse.AuthenticationResult.RefreshToken, deviceGroupKey, deviceKey);
}

async Task AuthenticateDeviceWithSrp(UserAuthResult result)
{
    using var provider = new AmazonCognitoIdentityProviderClient();
    var userPool = new CognitoUserPool(POOL_ID, CLIENT_ID, provider);

    var user = new CognitoUser(USER_NAME, CLIENT_ID, userPool, provider, CLIENT_SECRET);
    user.Device = new CognitoDevice(new DeviceType { DeviceKey = result.DeviceKey }, user);

    var authResponse = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest
    {
        Password = USER_PWD,
        DeviceGroupKey = result.DeviceGroupKey,
        DevicePass = DEVICE_PWD,
    });

    Console.WriteLine(authResponse.AuthenticationResult.AccessToken);
}

Can you help me figure out what I'm missing here? Not sure if it matters, but this is the configuration for my test user pool:

Screenshot 2022-07-14 at 15-15-39 demo-user-pool - User pools

github-actions[bot] commented 1 year ago

This issue has not received a response in 5 days. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.

kceb commented 1 year ago

@dscpinheiro in my case, for cognito settings I have remember devices as opt-in and trust remembered devices to suppress MFA as "Yes"

The DEVICE_PASSWORD_VERIFIER flow is not working despite me re-creating the same signatureString (verified via many log statements, hardcoding bigA, littleA, timestamp, etc.) as here: https://github.com/aws-amplify/amplify-js/blob/master/packages/amazon-cognito-identity-js/src/CognitoUser.js#L718

Also, I tried the steps here: https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/

The formula for the PASSWORD_CLAIM_SIGNATURE seems wildly inaccurate and does not match any AWS open source code anywhere online. It also returns a generic invalid password error like others have mentioned.

It seems like responding to DEVICE_PASSWORD_VERIFIER is not functional

Even this python example does not seem to work if you have MFA turned on: https://docs.aws.amazon.com/code-library/latest/ug/cognito-identity-provider_example_cognito-identity-provider_InitiateAuth_section.html

You get the following error:

botocore.errorfactory.NotAuthorizedException: An error occurred (NotAuthorizedException) when calling the RespondToAuthChallenge operation: Incorrect username or password