AWS Cognito Limitations

• 6 min read
Hunter Fernandes

Hunter Fernandes

Software Engineer


When we were initially rolling out user accounts, we decided to go with AWS Cognito. It has been incredibly frustrating to use and I need to rant about it.

tl;dr Don’t use Cognito.

Cognito & JWTs

AWS released Cognito in 2014, and its goal is to serve as the authentication backend for the system you write. You integrate with Cognito by setting up a Cognito User Pool and by accepting user tokens signed by Amazon.

At a high level, the user authenticates against Cognito and receives three JSON Web Tokens that are signed by Amazon:

  1. an Access token that holds very few claims about an account. Essentially just a User ID. This is supplied to our API to prove it’s you.
  2. an ID token which contains all attributes for an account. Think email, phone, name, etc.
  3. a Refresh token to get more access tokens once they expire.

When users call one of our APIs, they supply the access token in the Authorization header. It looks something like this:

GET /identity/v1/users/$me/ HTTP/1.1
Host: api.carium.com
Authorization: Bearer the-very-long-access-token-goes-here

On seeing this Authorization header, our API verifies the token is signed (correctly) by Amazon and that the token is not expired among other things. If the signatures match and other criteria are met, then we know that you are you and we can give you privileged access to your account!

But access tokens only last for an hour. We don’t want the user to have to log in every hour. They continue to use the authentication after an hour by using the refresh token to acquire another access token. This is done via a Cognito API. Therefore, the client will be refreshing the access token every hour for the duration of the session. (If the client misses a refresh period that’s fine. There is no continued-refresh requirement).

That is the gist of JWTs. Now, back to Cognito.

The Good

Why did we go with Cognito in the first place?

  1. It’s not our core competency. Using Cognito allows us to offload complex identity management to a team of experts that live and breathe identity. And because it’s their core focus, you get cool things like…

  2. Secure Remote Password (SRP) protocol. Instead of us offloading password-derivatives onto AWS, with SRP even AWS doesn’t know the password! With SRP, the actual password is never transferred to a remote server. That is super cool.

  3. Handles registration for us. Cognito will take care of verifying email addresses and even phone numbers so that you don’t need to implement that flow.

The Bad

Now that I’ve listed all the reasons we started going with Cognito, here are all the pain points we’ve felt along the way. Some of them are very painful.

  1. No custom JWT fields. Cognito does not allow you to store custom fields on the access tokens. We have some information that we really want to stick on the stateless tokens.

  2. Cognito doesn’t let us issue tokens for a user without their password. I will be the first to admit that this is really nice from a security perspective. But we are a healthcare app and we need to be able to help our users when they are in technical trouble.

    One of the most powerful tools we can give to our support staff

    is impersonating patients to see the issue they are seeing. We can’t do that without either a) issuing tokens for the user or by b) adding a custom field on JWTs (along with some logic in our apps to recognize this new fields). But neither is possible in Cognito!

  3. There is no easy email templates differentiation between events like verify email and forgot password. You have to give them just a single giant email template file and have a bunch of crazy if-else blocks in it to render parts differently based on event parameters. This should be a lot easier.

  4. No multipart email support. That means your emails (for sign up, resetting a password, etc) can either be plaintext or html, but not both. If you want to include links in your mail then simple email clients won’t be able to render your message at all (they would normally render the plaintext version).

  5. cognito:GetUser has a permanently-low rate limit. That’s right, the only API that will give you all user attributes (as well as verifying the token) can’t be called that often. And it’s a hard limit, too. It does not scale up as the number of users in your pool increases (confirmed with AWS Support).

    What this means is that you have to build your own storage to mirror the attributes in the pool. If you have

    to do that, then why are you using Cognito at all?

  6. Cognito does not allow passwords with spaces. Yes, really. That means the old “correct horse battery staple” advice is not allowed. That is insane. And further than that, it’s an insane requirement that we need to justify to our customers. We are not a bank from the 90s but that’s the first impression our users get of us.

    banned correct horse battery staple.

  7. No SAML integration. As a business that interfaces with large health systems, we will need to support SAML at some point in the future. Cognito does not support this at all.

  8. And the biggest one of all: no ability to backup a user pool. If you accidentally delete your user pool or errant code goes rogue, then your business is over. You don’t get to take backups. That’s it. Done. You can’t even move users from one pool to another (I expect this has something to do with SRP keys).

    As a workaround, you can collect all attributes of your users and store that list somewhere as a crap backup.

    But you can’t automatically create restored users in a new pool (they need to verify their email first). Furthermore, your list would still require users to reset their password because Cognito cannot give you a hash (or, instead, their secret half of the SRP).

    All of the other problems with Cognito make me annoyed, but an inability to backup my user list legitimately terrifies me.

The Ugly

So due to Cognito limitations, we will have to implement our own user store and authentication service.

I think we ultimately failed because we tried to bend Cognito to fit our needs and Cognito is not designed for that. Cognito demands that your app bend to Cognito’s auth flows. That’s fine for mobile app du jour, but it just doesn’t work for the enterprise software half of our business.

Writing an authentication backend is hard, the risks are high, and the user migration will be long.

Oh well.