Search code examples
reactjsaws-api-gatewayamazon-cognito

Secure storage of access tokens - React, API gateway, cognito


I’m curious how one should use AWS Congnito in a minimalist but secure fashion.

Suppose your stack is React (hosted on S3), API Gateway and Lambda.

From what I’ve read, this can be done without all the bloat of AWS Amplify by importing cognito directly. Great!

const { CognitoUserPool, CognitoUser, AuthenticationDetails } = require('amazon-cognito-identity-js') Likewise, I’ve read that API Gateway can expect a “access tokens” provided by Cognito upon user authentication. Again, great!

But where are these tokens stored? Is it browser agnostic and is it secure?

From what I can tell, your three options are in-memory (least secure), session variable (semi secure) and cookie (most secure).

Are there any recommendations for storage of these access tokens that balance security with simplicity and minimal dependencies?


Solution

  • But where are these tokens stored? Is it browser agnostic and is it secure?

    Usually, in your browser's or mobile application's memory. Some client-side libraries also save them in the session storage, because it facilitates its reuse by different JS scripts and lets it survive browser refreshes without the need to re-authenticate.

    From what I can tell, your three options are in-memory (least secure), session variable (semi secure) and cookie (most secure).

    I would say that's exactly the other way around (if by "session variable" you mean session storage).

    The flow is like this:

    1. You pass your credentials (username, password, MFA response, whatever) to Cognito in some way. Maybe directly if you're using hosted UI, maybe using SRP, maybe using a refresh token.

    2. When you convince Cognito that you are who you say you are, it gives you back a bunch of tokens. Among them, there's access_token which you will need to present to API Gateway. Usually, it's good for a relatively short period of time measured in minutes or low hours.

    3. You present this access token to API Gateway, usually by putting it in Authorization header.

    4. API Gateway checks the signature, the timestamp and the scopes of your token, and if they all match the authorizer settings, it forwards your request to the integration. The integration also has access to the token data and can perform additional authentication and authorization steps.

    You app has to store the token somewhere, to be able to pass it from step 2 to step 3.

    As long as the token is valid (which is, again, usually measured in minutes), it can be used to do stuff on your behalf. Attackers would very much like to know the value of this token.

    Some common attack vectors are:

    1. Script injection in your browser page (which could get access to the session storage). This can be done through buggy front-end libraries, by social engineering (asking you to run some code in your browser's console) and in several other ways. "Facilitating reuse by different scripts" means malicious scripts as well.

    2. Confused deputy attacks (which can force your browser to send a malicious request to your API Gateway, accompanied with a valid cookie). Such requests can be initiated from attacker-controlled websites other than yours, if the victim can be lured there. They can exploit bugs in your Gateway integration code.

    If you only store the access token in JS memory, the attack surface is much smaller.