Security question re SPA and Api Gateway

I want to develop a react.js single page app with a serverless backend similar to the serverless stack arch( i.e. no middleware server in the middle).

Users of the app will initially authenticate from another website which will then forward them with oauth data to a callback url i.e. a path in my react app with some oauth data including user info and a token.

At this stage I will want my react app to make a api gateway call to create or update this user/token info in my aws database e.g. createOrUpdateUser(userinfo, token).
The data in the database I would assume would then be used by a custom authorizor to validate each subsequent api gateway call which would be passed the token

My question though is this: How do I guard the createOrUpdateUser() api to stop it being maliciously getting called by anyone other than the react app ?

If you can use Amplify or Auth0 then your life will be a lot easier.

I think you’ve simplified OAuth too much. Part of the process involves validating the temporary token you receive in the callback while you exchange it for a one you can use with API calls. During this step you can issue your own session token to the React app. The API to exchange the token doesn’t really need protecting. There are a bunch of checks you need to do before calling the OAuth provider and the OAuth provider will return a failure if the code is wrong.

What’s the product, do you have its website for reference?

@bill It’s an open source project from AWS that integrates a number of existing products - Cognito, Pinpoint, S3, etc - so that they’re easy to use in a serverless environment.

1 Like

Thanks a lot, @buggy

yeah I think I had it wrong a bit. Actually I cannot use Auth0 in this case I think. Basically a site called BigCommerce authenticates the user and then forwards them to my app via a callback url with a oauth token… so I think this is how it shoudl basically work now…

  1. User authenticates with BigCommerce
  2. Success auth forwards user to a calback url (my single page app) with oauth token. Single page app stores the oauth token in browser storage
  3. Now each time SPA needs to invoke a api gateway it will supply the oauth token in the headers. A configured lambda custom authorizer will intercept the request and verify the oauth token and then return an IAM deny or allow policy which will dictate whether the api gateway can be proceed to be invoked or not.

Some where along the way I will need to store user info from the oauth token in my own database. I guess I could do some sort of createUserIfNotExists() logic in my authorizer. It does sound like a lot of logic to have to execute each time in an authorizer but if I use the time to live caching feature then it may not be so bad ?

There is AssumeRoleWithWebIdentity
This requires a supported identity provider. Maybe your site is implementing OIDC? It so, you can set it up in IAM as an Identity provider.

There are also Developer Authenticated Identities
With developer authenticated identities you can store the tokens in a Cognito Identity Pool.

This is a fully working react + serverless app with auth0 for auth

I gave a talk on it recently (no video yet) here are the slides with further explanation of the setup

@walshe I’ve done something similar with Shopify and it required building two API to start and complete the auth process in order meet all of the validation requirements and keep my Shopify API secret from users. I see BigCommerce now uses OAuth 2.0 so I imagine their process is similar. I would strongly suggest that you start with pen & paper then draw the authentication process on paper along with what information flows in each direction and what information you need to keep secret. Once you’ve done that I think you’ll have a very different view of how it works.

thanks for link @DavidWells

thanks @buggy I’ll have a look…

authentication always such a bugbear of mine :frowning:

BigCommerce doesn’t support OIDC

@buggy just one more on this…

In my oauth callback (the lambda), and after I do all the handshake process with the external provider I need to redirect to my react app obviously.

Since we cant use a session in a serverless architecture, I am therefore creating a customjwt token and passing it to the react app as a query param over https in the redirect 302.

callback(null, {
          statusCode: 302,
          headers: {
              Location: `${process.env.HTTPS_APP_URL}?token=${token}`
      }) ;

All subsequent requests to my other apig/lambdas use the jwt in the header and are checked by a custom authorizer where the jwt in the header is verified.

My question is if the passing of the token in the redirect is safe ? whats a better way?

Currently my callback page is in the SPA. After loading it calls my API using POST to get an access token. This avoids needing to pass tokens as query string parameters which I don’t like doing because the token is publicly displayed and potentially it appears in cache logs.

Lately I’ve been looking at Cognito, Amplify and AppSync so my approach might change in the next few days as I get more serious about that combination.

ah - so you are saying you get the external auth provider to callback to the SPA rather than a serverless apig, and from there you do a post to your apig where the handshake is completed and then you just return the token back in the post response ? @buggy

@walshe Correct. My complete process is this

/login is part of the SPA. It makes an API call that returns a JWT token which is stored in the browser and a redirect URL that the user is sent to.

/callback is part of the SPA. It passes the query string parameters plus the JWT token from the first step to another API which handles the authentication and returns a session token generated by the API to authenticate future API calls (a different JWT).

This approach uses the JWT token like a session storage during the OAuth process and means the browser doesn’t get access to anything it wouldn’t have access to during the regular OAuth flow.