18

I have a Cognito user pool which has MFA set to Required with TOTP only (i.e. no SMS).

My question is how do I reset the MFA for a user? For example what if the user loses his phone so he doesn't have anyway to login.

I have tried reset password but that only resets the password, it doesn't remove the MFA.

At the bottom of this AWS documentation, it says

NOTE A delete TOTP software token operation is not currently available in the API. This functionality is planned for a future release. Use SetUserMFAPreference to disable TOTP MFA for an individual user.

So I tried SetUserMFAPreference and AdminSetUserMFAPreference, they just return 200 OK but doesn't actually disable the MFA. I guess it's due to the user pool has MFA set to Required.

Ermiya Eskandary
  • 15,323
  • 3
  • 31
  • 44
StarCub
  • 4,141
  • 7
  • 41
  • 58
  • Tried the AdminSetUserMFAPreference and didn't work. Were you able to figure this out. I imagine setting `Do you want to enable Multi-Factor Authentication (MFA)?` as `Optional` might be required to allow individual uses to pick preference. – BisonAVC Jul 31 '19 at 06:10

6 Answers6

9

At this point, since AWS does not support resetting the MFA (if your user pool requires MFA - disabling MFA using AdminSetUserMFAPreference will return 200 OK but it will do nothing), the only way to do this is to create a new user pool with optional MFA (you have to create a new one since changing from required to optional is prohibited once the user pool is created). Then with the new user pool, you have to enforce the MFA (if that's something that you want) manually within your code.

For that, once the user logs in successfully and the return object has the tokens in it, you have to call AssociateSoftwareToken instead of returning the tokens and start the MFA registration process. The tokens (like IdToken) will only be returned to the user when they managed to complete the call to AdminRespondToAuthChallenge successfully.

Bottom line, with the optional MFA, the AdminSetUserMFAPreference will work. And this is the only way to reset MFA in a user pool on AWS (at this point).

[UPDATE]

Not that my original answer was invalid, but just to provide more information, here's some extra explanation on how to use AdminSetUserMFAPreference:

First of all, you need a user pool with optional MFA. The optional is the keyword here. This solution does not work on a user pool with required MFA.

Having the user pool setup, I assume there's a user properly signed up to your user pool. This means that they can authenticate themselves against your user pool without any issues. Of course, the whole point is that they need to provide MFA as part of their authentication process. But since we already established, MFA is optional in your user pool so if you insist on enforcing MFA on your users, that means you have to do so within your code manually.

Alright, so far everyone's happy. But just as the reality, come the sad days. Your user loses their MFA codes and cannot generate any new ones. So you would like to provide them with the possibility of resetting their MFA by re-registring a new device. Of course, first, you need to make sure that it's the actual user requesting such a thing. I mean you don't want anyone (but the true user) to be able to make such a request. So you need to authenticate them first. But the fact that they cannot authenticate (due to missing MFA) is the whole reason why they ended up here. So what can you do now? Well, even though this part is out of the scope of this post, but as a small hint, you can send them an email with a code and ask them to return it back to you as a one-time authentication mechanism.

OK, back to the question at hand. Now, you are sure that it's the actual account owner is requesting the MFA reset. And this is how you do it:

async function resetMfa(username) {
    const cognito = new AWS.CognitoIdentityServiceProvider();
    await cognito.adminSetUserMFAPreference({
            UserPoolId: "user pool ID",
            Username: username,
            SoftwareTokenMfaSettings: {
                Enabled: false,
            }
        }).promise();
}

Once their MFA is disabled, in order to re-register a new device, the account owner has to attempt a new login. This attempt will be just like the first time they are logging into their account and they will be asked to register a new MFA device.

I hope that clarifies things a bit more. If you want to know how to use MFA (manually), I have this other post which addresses it.

Mehran
  • 15,593
  • 27
  • 122
  • 221
  • 1
    I follow your instructions, but still AdminSetUserMFAPreference does not work – Juan Luis Dec 05 '21 at 09:12
  • @JuanLuis I'm not sure what you are doing wrong but I'll add more info to my answer and I hope that will help you figure it out yourself. – Mehran Dec 05 '21 at 18:18
  • `Then with the new user pool, you have to enforce the MFA (if that's something that you want) manually within your code.` But how do you enforce this without building an entire new service that wraps Cognito internally? You can't enforce this just on the client side for obvious security reasons. – Andrej Mitrović Mar 13 '23 at 10:33
  • Thank you! It is April 2023 and there is still no way to reset MFA for users in a user pool that has MFA required for all users. Your approach solved it for me. Upvote! – Jan Apr 06 '23 at 21:30
  • 1
    @AndrejMitrović What you said is true. And that's what I ended up doing. I developed my own lambda functions to interact with Cognito and enforce the MFA. Then I used this lambda function as an authorizer in API Gateway. Of course, there are other lambda functions that deal with signing up and logging in. – Mehran Apr 07 '23 at 06:20
  • I also build my own service that calls Cognito. But instead of using Lambda for API Gateway I put it in the Node.js backend of my web application. Whatever way you choose, just make sure it is on the server side. – Jan Apr 13 '23 at 14:16
  • This doesn't work for me. Yes, I set the pool as optional, and then disable the user's MFA and SoftwareTokenMfaSettings. However, after setting the MFA back to required, the old code is requested in the Auth flow. I have to explicitly associate MFA again with AssociateSoftwareToken. Am I doing something wrong? – Climax Sep 01 '23 at 07:26
1

I found that it is possible to change MFA settings to 'Required' even after users are added to the user pool. So, you don't have to create another user pool.

It is impossible to change it from the console, but aws cli (and probably via set_user_pool_mfa_config() API) can change it like:

% aws cognito-idp set-user-pool-mfa-config --user-pool-id <userpool_id> --mfa-configuration ON --software-token-mfa-configuration Enabled=true

Although I couldn't find the way to delete/change the "each user's MFA (OTP)".

tomy
  • 11
  • 1
1

Adding to @mehran's answer.

If it helps...

If the pool has been set to "Optional MFA", resetting the MFA can be done in the AWS Console as well.

  • Verify if "Optional MFA" has been set MFA enforcement setting in AWS

  • Find user and go to "Update MFA configuration" Update MFA Configuration

  • Set "Do not require MFA"

Set do not require MFA

  • Once user has logged in successfully, set it back to "Require MFA"

set to Require MFA

Chuen Lee
  • 353
  • 4
  • 17
0

Actually you need to change user's settings, not preferences.

to remove MFA var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();

    var params = {
        UserPoolId: poolData.UserPoolId,
        Username: userid, /* required */
        MFAOptions: [ /* required */
        ]
    };
    cognitoidentityserviceprovider.adminSetUserSettings(params, function(err, data) {
        if (err) reject(err);       // an error occurred
        else     resolve(data);     // successful response
    });

To Add/Change MFA:

    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();

    var params = {
        UserPoolId: poolData.UserPoolId,
        Username: userid, /* required */
        MFAOptions: [ /* required */
            {
                AttributeName: 'phone_number',
                DeliveryMedium: 'SMS'
            }
        ]
    };
    cognitoidentityserviceprovider.adminSetUserSettings(params, function(err, data) {
        if (err) reject(err);       // an error occurred
        else     resolve(data);     // successful response
    });
  • NOTE: adminSetUserSettings has been deprecated. Use SetUserMFAPreference instead. – flux Jan 14 '20 at 12:18
  • The question explicitly says that they are interested in software MFA (not SMS). And `adminSetUserSettings` does not have the option for software MFA. – Mehran Jan 17 '20 at 21:24
-1

You can give each user a recovery code, and then write a Lambda exposed via an API endpoint that checks to see if they submit the right recovery code. If they do, you can call the following inside the Lambda to disable the user's MFA:

  const result = await cognito
    .adminSetUserMFAPreference({
      UserPoolId: AmplifyConfig.Auth.userPoolId,
      Username: userid,
      SoftwareTokenMfaSettings: {
        Enabled: false,
        PreferredMfa: false,
      },
    })
    .promise();

Be sure to use something like crypto.timingSafeEqual to defend against timing attacks when checking those recovery codes.

Tom Q.
  • 640
  • 5
  • 9
  • How do you actually send users recovery codes. Do you have to do this manually? – Johhan Santana Oct 21 '19 at 16:08
  • The idea is correct but the `adminSetUserMFAPreference` does not reset the MFA for the user. – Mehran Jan 17 '20 at 21:23
  • @Mehran it did reset the MFA at the time of writing. Maybe you can edit the answer with the correct function? – Tom Q. Feb 15 '20 at 23:24
  • There's no correct function. If the user pool is created with required MFA, you can't reset MFA (at least at the time of writing - maybe in future AWS fixes this). And I'm sure that you couldn't reset the MFA even at the time of writing your answer since the OP says that they had the same problem and they must tested before you wrote yours. I don't know what was your test case, but as long as your user pool required the MFA, your function call should have failed silently (it does not say that it failed). I've already provided a solution as an answer. You can refer to that to see how I did it. – Mehran Feb 15 '20 at 23:35
-1

If you are an administrator, you can update it using the following AWS CLI command:

aws cognito-idp admin-set-user-mfa-preference

For more information, have a look at the documentation.

anothernode
  • 5,100
  • 13
  • 43
  • 62
Ak S
  • 97
  • 1
  • 8
  • As per documentation, its working and am not sure what issue you are faced – Ak S Jan 20 '20 at 12:53
  • 2
    As the OP states, calling that API will return 200 OK but it does nothing. I know the documentation says it works, but it does not. I've already opened up a ticket with AWS on this. The reason why OP asked the question in the first place was that the API does not work. – Mehran Jan 20 '20 at 13:39
  • yes, it return 200 OK, so API works correctly but doesn't actually disable the MFA here – Ak S Jan 21 '20 at 09:18