I have a weird problem. I am trying to allow a user to delete his profile:
<button onClick={() => {
Auth.currentAuthenticatedUser().then(user => {
user.deleteUser((err, result) => {
if (err) {
console.log('User deletion error: ' + err)
return
}
console.log('User deletion result: ' + result)
})
}).catch(e => {
console.log(e)
})
}}>Delete Account</button>
However, the deleteUser method does not finish up the job of signing out the user. It sort of does sign him out, i.e. when I reload the page, the currentAuthenticatedUser method does not find a user object anymore. However, the deleterUser method's signout is not caught by my Hub listening to the signOut event and it will later cause some weird glitch with cookies (both different from the Auth.signOut method). So the cookie glitch is that when the user tries to create a new account by calling Auth.federatedSignIn, he will receive some cookies from the Hosted UI domain and then the browser throws an invalid_token error. This will repeat on retrying and causes you to be stuck and being unable to access the Hosted UI. What helps though is to manually delete those cookies after the first failed signin attempt.
Calling signOut in that state also does not end that state (which is weird because it should delete all cookies I guess? but it fails because there is user signed in anymore). So, adding a signOut like this in the deleteUser button will not help because it is "already too late":
<button onClick={() => {
Auth.currentAuthenticatedUser().then(user => {
user.deleteUser((err, result) => {
if (err) {
console.log('User deletion error: ' + err)
return
}
console.log('User deletion result: ' + result)
Auth.signOut()
})
}).catch(e => {
console.log(e)
})
}}>Delete Account</button>
The other way round (first signing out the user and then calling user.deleteUser) does not work either because the deleteUser method depends on the user object which has already been removed in the signOut.
The "best solution" is a hack where I simultaneously call both methods:
<button onClick={() => {
Auth.currentAuthenticatedUser().then(user => {
Auth.signOut()
user.deleteUser((err, result) => {
if (err) {
console.log('User deletion error: ' + err)
return
}
console.log('User deletion result: ' + result)
})
}).catch(e => {
console.log(e)
})
}}>Delete Account</button>
This signs out the user properly (like when normally calling it without deleting the user) and prevents the signin cookie problem. And it also deletes the user from the user pool (though not logging the result anymore). It is obviously a hacky way to do that and just works "by accident" considering that the two functions work on the same user object at the same time. But it proves that the deleteUser function is bugged in the Amplify context...
Any suggestions how to solve this? (I tried to delete those cookies manually from JS, but couldn't get it to work.) Otherwise, I guess it shows that we need a "native" Amplify method for that as asked for in this issue: https://github.com/aws-amplify/amplify-js/issues/469
Hi @tinymarsracing
This sounds like a bug in the auth category, deleting a user that was signed-in using the hosted UI should be signed-out from the hosted ui first.
We'll take a closer look at it
Hi Manuel, thanks for your reply. Your hint that you have to "sign out from the Hosted UI" gave me an aha moment. Now the behaviour and the cookie domain of these "glitched" cookies all makes sense. ;) I found this other comment and used that workaround and now it works. Thanks!
But I'm guessing a cleaner solution would be something like that (as I already tried to implement):
<button onClick={() => {
Auth.currentAuthenticatedUser().then(user => {
Auth.signOut().then(() => {
user.deleteUser((err, result) => {
if (err) {
console.log('User deletion error: ' + err)
return
}
console.log('User deletion result: ' + result)
})
})
})
}}>Delete Account</button>
But as I said this won't work because the user object will have been deleted by Auth.signOut (and also deleteUser relies on some other objects that will have been deleted as well). Is there a way to make this code work?
@tinymarsracing I am glad you got the cookies thing working!
Regarding the deletion... Can you try something like this? (please note that this uses unsupported private functions, so it is just to explore possible solutions)
<button onClick={async () => {
const user = await Auth.currentAuthenticatedUser();
await new Promise((res, rej) => user.deleteUser((err, result) => err ? rej(err) : res(result)));
Auth._oAuthHandler.signOut();
}}>Delete Account</button>
does that make sense?
Perfect, that is much nicer and avoids redirecting to the Hosted UI URL explicitly.
So, to relate this back to my original post, all we have to change in my second code block is using the private function Auth._oAuthHandler.signOut() instead of the normal Auth.signOut():
<button onClick={() => {
Auth.currentAuthenticatedUser().then(user => {
user.deleteUser((err, result) => {
if (err) {
console.log('User deletion error: ' + err)
return
}
console.log('User deletion result: ' + result)
Auth._oAuthHandler.signOut()
})
}).catch(e => {
console.log(e)
})
}}>Delete Account</button>
This should be the same as your code, just in a different syntax. I tried that in my app and it worked. Thanks! Gonna close this issue now because we already have the other issue asking for a native Amplify deleteUser method that will do that under the hood.
Most helpful comment
Hi Manuel, thanks for your reply. Your hint that you have to "sign out from the Hosted UI" gave me an aha moment. Now the behaviour and the cookie domain of these "glitched" cookies all makes sense. ;) I found this other comment and used that workaround and now it works. Thanks!
But I'm guessing a cleaner solution would be something like that (as I already tried to implement):
But as I said this won't work because the
userobject will have been deleted byAuth.signOut(and alsodeleteUserrelies on some other objects that will have been deleted as well). Is there a way to make this code work?