aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.38k stars 2.1k forks source link

How to disable or delete user #469

Closed nnurmano closed 4 years ago

nnurmano commented 6 years ago

I could not find info on how to disable or delete user. Could you please provide a documentation link or update documentation?

ashleywnj commented 6 years ago

Through the console:

  1. Select the User Pool
  2. left menu: Users & Groups
  3. Select the user
  4. Select Disable
  5. Delete is an option - see image
user_pools_-_amazon_cognito
nnurmano commented 6 years ago

Sorry, I was not specific. I wanted to do it with the help of aws-amplify API. The user should be able to delete his account.

elorzafe commented 6 years ago

Hi @nomadus currently is not posible delete a user using aws-amplify. I know is possible using amazon-cognito-identity-js sdk. Take a look on this https://github.com/aws/aws-amplify/blob/master/packages/amazon-cognito-identity-js/src/CognitoUser.js#L948 I never tried, but I will do it in the next couple of days.

nnurmano commented 6 years ago

Thank you @elorzafe!

baseten commented 6 years ago

@elorzafe how about disabling a user?

We're having an issue with an app where users may only use their email address as a username to log in. We also allow them to update this email address. When a user does this they are left in a strange state where the user is confirmed, but the email address isn't verified. The user can log in to the app successfully with the new email address (unlike when they first create an account but have yet to complete verification), so there's nothing to stop them from continuing to use the app without verifying this email address.

This leaves the user in a strange state and ideally we'd like to disable them via the API to force them through the verification flow again.

jliebrand commented 6 years ago

Hi there - any update on this?

We used the amazon-cognito-identity-js to do this, but this has recently started to fail because crypto is no longer defined. I've been digging in to this, but it would seem amazon-cognito-identity-js is simply outdated and it looks like all your efforts are now on amplitude (?)

Should amazon-cognito-identity-js still work? it seems it needs to update the crypto-browserify it uses?

Or is there a different way to achieve this? (We need to be able to delete a cognito user for our integration tests, but also in order to comply with the new GDPR rules (and we'd rather avoid having to do things manually))

jliebrand commented 6 years ago

Friendly ping

with GDPR deadline looming, I'd love to understand if there is at lease some thoughts on an ETA on this issue?

buggy commented 6 years ago

The aws-sdk includes an adminDeleteUser and deleteUser that might help with some of the use cases mentioned in this thread.

sielay commented 6 years ago

deleteUser just does the job, no need to overcomplicate it.

jliebrand commented 6 years ago

Thanks @buggy and @sielay - I know you can use the aws-sdk itself directly, or indeed by using cognito JS

My understanding was that AWS wants amplify to be the new client people use though - so rather than using multiple sdks, I'm curious to know when support for deleteUser will be built in to amplify so that we dont have one way to do one thing and another to do something else...

baseten commented 6 years ago

@jliebrand the CognitoUser object you get back from a successful sign in (https://aws.github.io/aws-amplify/api/classes/authclass.html#signin) via the aws-amplify AuthClass, is the cognito-js SDK. cognito-js seems to be a dependency of aws-amplify. In fact if you take a look through the code for AuthClass in a lot of cases it acts as a facade for cognito-js.

You can therefore use the deleteUser method on the CognitoUser object.

jliebrand commented 6 years ago

Great, thanks @baseten - what about for returning users? We use the session to determine if they are still logged in:

let session = Auth.currentSession();
// CognitoUserSession => { idToken, refreshToken, accessToken }

Can we get the CognitoUser from the token somehow?

sielay commented 6 years ago

@jliebrand ss for me it's close enough to use Amplify (my code that works)


import { CognitoUser } from "amazon-cognito-identity-js";
import { Auth } from 'aws-amplify';

...

public onRemoveAccount() {
        Auth
            .currentAuthenticatedUser()
            .then((user: CognitoUser) => new Promise((resolve, reject) => {
                user.deleteUser(error => {
                    if (error) {
                        return reject(error);
                    }
                    if (this.props.onSessionChange) {               
                        this.props.onSessionChange();
                    }
                    document.location.href = "/login";

                    resolve();
                });
            }))
            .catch(this.onError);
    }
jliebrand commented 6 years ago

Aha! That's the missing piece! The api guide only talks about the currentSession and not the currentAuthenticatedUser.... should have done a bit more digging in the API - apologies...

That sorts me, thanks @sielay

baseten commented 6 years ago

@jliebrand was beaten to it, but yes currentAuthenticatedUser is the key :)

jliebrand commented 6 years ago

👍

weibeld commented 5 years ago

This is a solution with observables (used in Angular):

import { Auth } from 'aws-amplify';

public deleteUser(): Observable<any> {
  return from(Auth.currentAuthenticatedUser()).pipe(
    mergeMap(user => {
      return new Observable<any>(observer => {
        user.deleteUser((err, data) => {
          if (err) observer.error(err);
          else observer.next(data);
          observer.complete();
        });
      });
    })
  );
}
johanrin commented 5 years ago

What about only disabling the user? I don't find a way to do it with Amplify. Is it possible to do it like deleteUser?

elorzafe commented 5 years ago

@JohanRin Currently there is not an option to disableUser on Amplify. We will keep this marked as feature request

Jun711 commented 5 years ago

@elorzafe this is a related issue.

Hopefully this feature request will be able to achieve what is described in https://github.com/aws-amplify/amplify-js/issues/606: to disable user and user's access to API Gateway endpoint authorized by Cognito token immediately? It seems that the tokens are valid for at least an hour which I saw on IAM Roles console.

maybe Amplify can expose a function to check if user is disabled

undefobj commented 5 years ago

@Jun711 could you take a look at the RFC we have posted for the Amplify CLI here: https://github.com/aws-amplify/amplify-cli/issues/766 The "Admin Queries" might be what you are looking for. If so could you comment in the RFC with more details of how you might like this DX to work?

apertureless commented 5 years ago

Disabling the user would be the proper way to go. You have to keep in mind, that if you're just deleting the user from the cognito pool (over the dashboard or API) this does not delete the data associated with the user.

If you have somewhere @auth() rules for the owner (multi-user app) you'll get in trouble if you delete the user. Because if someone else register then the same username, he will have access to all the data.

tinymarsracing commented 5 years ago

I first wanted to write this as a comment here, but then decided to open it as a new issue:

https://github.com/aws-amplify/amplify-js/issues/3187

janhesters commented 5 years ago

I think this is still a problem. There should be a method Auth.deleteUser that:

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

janhesters commented 5 years ago

Stalebot that's a bad idea, because this issue still persists.

Crisp3333 commented 4 years ago

This should not be closed. There is a big short coming with AWS amplify when it comes to disabling/deleting users.

AWS deleteUser() function should automatically sign users out globally and clear all cache data. Because of this I have to be wire-rigging a userpool object from AWSConfiguration and even with userpool.getCurrentUser().deleteUserInBackground(genericHandler) data of the previous logged in user is still on the device. AWSMobileClient needs some form of "delete user" of its own.

Crisp3333 commented 4 years ago

A way I came up with for those using AWSMobileClient; do the following in the same order:

AWSConfiguration awsConfiguration = AWSMobileClient.getInstance().getConfiguration(); userpool = new CognitoUserPool(this, awsConfiguration); // CognitoUserPool object userpool.getCurrentUser().globalSignOutInBackground(genericHandler); userpool.getCurrentUser().deleteUserInBackground(genericHandler);

This way it signs the user out globally from all devices after which it deletes the user. Using Android/Java of course.

dmdmd commented 4 years ago

Is it possible to add a layer of security to the deleteUser? Something like a "confirm password before deletion"?

kirantpatil commented 4 years ago

How about "DisableUser" support ?

I would want to disable a cognito user for temporary purpose. How to do it, since I couldn't find a API to achieve that goal.

Thanks.

kirantpatil commented 4 years ago

Is there a API/function/method to disable/enable user of userpool ? So that I can disable/enable user using the webapp using API


 userpool.getCurrentUser().disableUserInBackground(genericHandler);

 userpool.getCurrentUser().enableUserInBackground(genericHandler);
undefobj commented 4 years ago

Please look at the Admin Actions functionality that was added last year to Amplify CLI to provide this capability: https://docs.amplify.aws/cli/auth/admin

You can add this to any Amplify project or use it as a template for non-Amplify CLI projects.

kimfucious commented 4 years ago

Using cognito.adminLinkProviderForUser in a preSignUp lambda, can result in multiple accounts for the same user.

For example, if there is federated sign-in with Google, an account such as Google_1012514589345043xxxxx will be present.

Amazon, Facebook, and Apple, will add their own with a potential of 4, including the original, email/username, based user entry.

There really should be an easy, straight-forward way to remove all entries for a user programmatically via an Amplify Auth method.

kimfucious commented 4 years ago

Having said that, the below is pretty straight-forward for deleting a single user, and does not require amazon-cognito-identity-js to work.

const handleDeleteCognitoUser = async () => {
  const user = await Auth.currentAuthenticatedUser();
  user.deleteUser((error, data) => {
    if (error) {
      throw error;
    }
  // do stuff after deletion
  });
};
Jun711 commented 3 years ago

@kimfucious It doesn't seem to remove a social IdP linked user (for example, user who logs in with Google)

@mauerbac how to delete a social IdP linked user?

kimfucious commented 3 years ago

Hi @Jun711,

My use case may be different from yours, as I require--in this case--users to first have a user/password Cognito user before they can create a social/federated user.

Regardless, how I've been able to delete a social/federated user is by using the below (node.js) in a lambda function (as part of a larger function that does other stuff).

In short, the adminDeleteUser() method is your friend. You just need to pass it the correct params. This can't be run on the client side, as far as I ken.

const deleteLinkedProviderUser = async (user) => {
  const params = { Username: user, UserPoolId: userPoolId };
  const result = await new Promise((resolve, reject) => {
    cognito.adminDeleteUser(params, (err, data) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(data);
    });
  });
  return result;
};
Jun711 commented 3 years ago

@kimfucious thanks for replying. I figured it out after checking other issues. I had to go to my user pool on AWS Console for Cognito > "App client settings" > "Allowed OAuth Scopes" and check "aws.cognito.signin.user.admin" to authorize delete account action.

Jun711 commented 3 years ago

@kimfucious Could you try deleting a cognito account + linked google account? I found out that it only deletes the cognito account from user pool in this case.

Considering these 3 cases in user pool

  1. Google login (EXTERNAL_PROVIDER) account
  2. Cognito account
  3. Cognito account + linked Google login (EXTERNAL_PROVIDER) account When I run the same code, it can delete case 1 and 2 from user pool. For case 3, it would only delete the Cognito account, the linked Google login (EXTERNAL_PROVIDER) account would still exist in the user pool.

https://github.com/aws-amplify/amplify-js/issues/6121#issuecomment-649790071

kimfucious commented 3 years ago

Hi Jun,

For me, it's a two step process. Here's a code block with some annotation, which may help.

This particular app is using Redux, which is what all the dispatching is about, and I'm too lazy to clean this up for you.

It's ain't pretty, could use a refactor but it works 😉

I hope this helps.

const handleRemoveUser = async (e) => {
  e.preventDefault();  
  try {
    setIsDeleting(true);
    dispatch({ type: "DELETE_USER_START" });
    const cognitoAppleMusicUsername = identities
      ? JSON.parse(identities)
          .filter((item) => item.providerName === "SignInWithApple")
          .map((item) => "SignInWithApple_" + item.userId)[0]
      : null;

    const cognitoGoogleUsername = identities
      ? JSON.parse(identities)
          .filter((item) => item.providerName === "Google")
          .map((item) => "Google_" + item.userId)[0]
      : null;

    const providerUsernames = [];
    if (cognitoAppleMusicUsername) {
      providerUsernames.push(cognitoAppleMusicUsername);
    }
    if (cognitoGoogleUsername) {
      providerUsernames.push(cognitoGoogleUsername);
    }

    const cognitoAppleMusicUserId = identities
      ? JSON.parse(identities)
          .filter((item) => item.providerName === "SignInWithApple")
          .map((item) => item.userId)[0]
      : null;

    const cognitoGoogleUserId = identities
      ? JSON.parse(identities)
          .filter((item) => item.providerName === "Google")
          .map((item) => item.userId)[0]
      : null;

    const users = [];
    if (cognitoAppleMusicUserId) {
      users.push(cognitoAppleMusicUsername);
    }
    if (cognitoGoogleUserId) {
      users.push(cognitoGoogleUsername);
    }

    // This is clearing the db data, not the Cognito stuff
    await dispatch(deleteUserAccountData());

    // Deleting Cognito accounts
    dispatch({
      type: "UPDATE_APP_LOADING_STATUS",
      payload: "Removing user account..."
    });
    const user = await Auth.currentAuthenticatedUser();
    // **STEP ONE:  Delete Cognito username/password account**
    user.deleteUser(async (error) => {
      if (error) {
        throw error;
      }
       // **STEP TWO:  Delete Cognito (via lambda) linked accounts if they exist**
      if (users.length) {
        dispatch({
          type: "UPDATE_APP_LOADING_STATUS",
          payload: "Final cleanup..."
        });
        try {
          const response = await API.post("myAPI", `/cognito`, {
            body: {
              users: JSON.stringify(users)
            }
          });
          console.log("🗑️", response);
        } catch (error) {
          dispatch({
            type: "UPDATE_APP_LOADING_STATUS",
            payload: "Something's not right..."
          });
          throw error;
        }
      }
      const stream = primaryMusicStream;
      Auth.signOut({ global: true });
      dispatch({ type: "DELETE_USER_SUCCESS" });
      history.replace("/aloha", { primaryMusicStream: stream });
    });
    return;
  } catch (error) {
    setIsDeleting(false);
    console.warn(error.response ? error.response.data.error.message : error);
    if (error.Error) {
      dispatch({ type: "DELETE_USER_FAIL", payload: new Error(error.Error) });
    } else {
      dispatch({ type: "DELETE_USER_FAIL", payload: error });
    }
  }
};
Jun711 commented 3 years ago

@kimfucious I see. In short, it is not totally done using Amplify then as it would involves sending a request if the account is linked.

Jun711 commented 3 years ago

@kimfucious I noticed that you use Auth.signOut({ global: true });. Could you tell me how to set up the scope for this?

I got this error when I used it for linked google account logout.

Access Token does not have required scopes

Screen Shot 2020-06-30 at 1 12 35 PM

Ladvace commented 3 years ago

Hi @Jun711,

My use case may be different from yours, as I require--in this case--users to first have a user/password Cognito user before they can create a social/federated user.

Regardless, how I've been able to delete a social/federated user is by using the below (node.js) in a lambda function (as part of a larger function that does other stuff).

In short, the adminDeleteUser() method is your friend. You just need to pass it the correct params. This can't be run on the client side, as far as I ken.

const deleteLinkedProviderUser = async (user) => {
  const params = { Username: user, UserPoolId: userPoolId };
  const result = await new Promise((resolve, reject) => {
    cognito.adminDeleteUser(params, (err, data) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(data);
    });
  });
  return result;
};

Can I know what the var cognito is?

kimfucious commented 3 years ago

Hi @Ladvace,

That code block is from a lambda using AWS SDK for Node.js.

In this case, cognito is a variable assigned to the CognitoIdentityServiceProvider. You can name it anything you want, really, when declaring it.

This can be configured like the below in the lambda function:

const AWS = require("aws-sdk");
const cognito = new AWS.CognitoIdentityServiceProvider();

I hope that helps.

kimfucious commented 3 years ago

Hi @Jun711,

I've never come across that error, and I've not done anything (intentionally) to affect the scopes.

I believe this is referring to the settings found in User Pool => App Client Settings => OAuth 2.0 => Allowed OAuth Scopes.

In my case, related to the examples I've shared prior, these are all checked (phone, email. openid, aws.cognito.signin.user.admin, and profile), and these have been set when I initially ran amplify add auth.

Try to have a look at yours to see if you've possibly unchecked something.

Again, my situation may differ from yours, as I require users to create a password/username user in Cognito before allowing them to create a federated user (which gets linked to the password/username user).

Jun711 commented 3 years ago

Thanks @kimfucious I will look into it more.

My app client has all scopes but phone checked. https://github.com/aws-amplify/amplify-js/issues/5744#issuecomment-652021202

nihp commented 3 years ago

Here I have disabled a user in cognito userpool. But the user still access the app.

Auth.currentAuthenticatedUser(); not returns any error immediately the user is disabled? How can I know the user is disabled? How long the user will access the app after disabled in this case?

Hub.listen('auth', (data) => {
            console.log('Hub.auth event:', data);
            const { payload } = data;
            Auth.currentAuthenticatedUser({ bypassCache: true })
                .then(u => {
        }, err => {
    }

I am using aws-amplify-react-native and aws-amplify.

kimfucious commented 3 years ago

Hi, @nihp

I'd recommend doing a global signOut and redirecting the user to a page that is public, if you're using protected routes.

Also, do note the "Note" in the link I cited, as it says:

Note: although the tokens are revoked, the AWS credentials will remain valid until they expire (which by default is 1 hour)

nihp commented 3 years ago

@kimfucious Thanks for your reply. I have done global signout as you mentioned. But still i needs to reload the app to go for login screen.

  1. Can I able to get any not authenticated message or user is disabled immediately?
  2. Can I able to make the global signOut immediately if the user is disabled in cognito.

I am using mobile app. Here the user may be in any of the screen.

Admin will disable the user at any time. So i need a quick response. Then only I can able to make the globalsignOut.

kimfucious commented 3 years ago

Hi @nihp ,

I don't quite follow exactly what you're saying, but I have a feeling that you want the user to be re-directed to the login page automatically upon signOut.

It's important to note that signing out does not tell your app what to do, if simply defines what your app has access to (e.g. back-end resources).

That said, you app will not go to the login page by itself upon signOut. You need to tell it how to behave.

My recommendation would be for you to do one of the following:

  1. Use react-router to redirect the user to the login screen after you call the global signOut (e.g. history.push("/login").
  2. (Better) Setup protected routes (see here), using react-router, to ensure that signedIn/signedOut users are redirected to private/public pages accordingly.
  3. You may also want to take a look at Hub, to perform specific actions, when auth events occur.

I hope that helps.

nihp commented 3 years ago

@Kim ford

My question is after disabling the user, I need to get any response from amplify.

Then only I can immediately make the user to logout the app. Else he can able to access the app up to 1 hour.

So I need any error response immediately after disabling the user.

On Sun, Jul 12, 2020 at 12:41 AM Kim Ford notifications@github.com wrote:

Hi @nihp https://github.com/nihp ,

I don't quite follow exactly what you're saying, but I have a feeling that you want the user to be re-directed to the login page automatically upon signOut.

It's important to note that signing out does not tell your app what to do, if simply defines what your app has access to (e.g. back-end resources).

That said, you app will not go to the login page by itself upon signOut. You need to tell it how to behave.

My recommendation would be for you to do one of the following:

  1. Use react-router to redirect the user to the login screen after you call the global signOut (e.g. history.push("/login").
  2. (Better) Setup protected routes (see here https://reactrouter.com/web/example/auth-workflow), using react-router, to ensure that signedIn/signedOut users are redirected to private/public pages accordingly.
  3. You may also want to take a look at Hub https://docs.amplify.aws/lib/utilities/hub/q/platform/js, to perform specific actions, when auth events occur.

I hope that helps.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/aws-amplify/amplify-js/issues/469#issuecomment-657113859, or unsubscribe https://github.com/notifications/unsubscribe-auth/AI2HKUXSRARAUGZ5N54A7HDR3C2MLANCNFSM4EV3S4VQ .