Enabling Users to Delete Their Own AWS Cognito Accounts
Introduction
We're using AWS Amplify with AWS Cognito and the Hosted UI to handle Authentication for Cork Hounds. To provide users with the ability to delete their own accounts (along with any/all data that we manage on their behalf), we must interface with Cognito and the Hosted UI. Providing the ability for users to manage their data continues to rise in importance to maintain compliance with new data privacy/ownership laws, including "The California Consumer Privacy Act of 2018 (CCPA)" and "The European Union General Data Protection Regulation of 2016 (GDPR)".
Once a user deletes their account, it is important to sign them out globally. However, at the time of this writing, there is an issue that interferes with signout of the Hosted UI when using just Amplify by itself. I'll provide a work around that's supported by AWS using the new aws-sdk-js-v3 library. Let's dive in!
Overview
The AWS Amplify Auth.currentAuthenticatedUser
returns a CognitoUser with a deleteUser method, which allows you to do the following:
However, Auth.signOut()
doesn't sign the user out of the Hosted UI after using the deleteUser method.This issue is highlighted in ticket 3187 on the aws-amplify github repo. This is not fixed as of AWS Amplify version 3.3.13. While that ticket provides an unsupported workaround, I wanted a solution that would work into the future.
Using the AWS JS SDK seemed to make the most sense. On the GitHub readme for Amplify 3.x.x, it says they've removed the AWS SDK version 2.x in favor of version 3. The AWS JS SDK Version 3 is designed to be modular, to help with reducing package sizes / tree shaking. You can see a list of the modules on AWS' JavaScript SDK documentation.
Assumptions & Getting Started
I am going to assume you are already using AWS Amplify, AWS Cognito and the Hosted UI, and that you are looking for a way to (1) let users delete their own Cognito accounts, and then (2) sign the user out globally. You should be using a 3.x version of AWS Amplify.
To get started, you'll want to install the client-cognito-identity-provider
from the aws js sdk v3:
npm i @aws-sdk/client-cognito-identity-provider --save
I believe you'll also need to enable the aws.cognito.signin.user.admin
OAuth scope. This can be found via the AWS console for Cognito under User Pools > App Integration > App Client Settings > Allowed OAuth Scopes. According to the Cognito App Client Settings:
The aws.cognito.signin.user.admin scope grants access to Amazon Cognito User Pool API operations that require access tokens, such as UpdateUserAttributes and VerifyUserAttribute.
Code
We want to leverage the CognitoIdentityProvider
to perform the deleteUser function. By deleting the user outside of Amplify, it allows the Auth.signOut() function to operate as intended. So lets import the necessary components:
And by the way, if you wanted to leverage other properties/functions from the client-cognito-identity-provider
library, you could simply import everything:
import * as AWS from '@aws-sdk/client-cognito-identity-provider';
When Auth.signOut({ global: true})
is called, it will trigger a signout from the Hosted UI, and return the user to your signout URL. As noted in the AWS Amplify documentation for Signup, Signin, and Signout:
By doing this, you are revoking all the auth tokens (id token, access token and refresh token) which means the user is signed out from all the devices Note: although the tokens are revoked, the AWS credentials will remain valid until they expire (which by default is 1 hour).
Using Modularization to Reduce Bundle Sizes
One of AWS' goals in creating the JS SDK v3 was to make it more modular to help reduce bundle sizes. To achieve this, AWS decomposed the JavaScript SDK core into multiple packages, making it easier to import just the modules you need. After using the code I wrote in the section above, I realized that by importing the entire CognitoIdentityProvider
, the size of my bundle was much larger than it needed to be.
Lets take advantage of this new modularization. Instead of importing CognitoIdentityProvider
, we're going to import the CognitoIdentityProviderClient
and the DeleteUserCommand
separately.
Next, we'll update our code from above to use these new objects.
Now, the production bundle for my use of the CognitoIdentityProvider is within reason:
And that's all there is to it.
Conclusion
Hopefully you found this helpful/useful. Would love your feedback!