I want to develop a react.js single page app with a serverless backend similar to the serverless stack arch( serverless-stack.com 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.
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…
User authenticates with BigCommerce
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
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 ?
@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.
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
/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.