24

I have a client side React application and a Rails API from which the React app is fetching data.

As you would expect I only want my React application to be able to fetch data from the API and the rest of the world shouldn't be able to receive data from it.

Despite much searching I am yet to find the best way to secure the communication between the two applications.

I have read about JWT tokens and cookie based session authentication but the majority of articles seem to be focused on authentication of users (ie sign in/sign out) rather than communication between just the two applications.

The two apps will share the same domain so is it enough to rely on Cross Origin to secure communication?

Any advice really would be much appreciated.

Tom Pinchen
  • 2,467
  • 7
  • 33
  • 53
  • Hey. What's the approach you tool in the end? I am also a bit concerned that anybody can dissect my API with chrome dev tools. Best would be to have api communication encrypted too. – AAverin Apr 18 '20 at 19:16

2 Answers2

17

If I got your question right you want your client(React App) to be the only client who can access your server.

As a solution to that you will have to have a combination of CORS and a JWT authorization, Thus I would suggest having a strict CORS to enable only your react app's domain to make a call to the server. To achieve this, I generally use a CORS npm module and configure the origin on my server or you can do it yourself as well.

var express = require('express')
var cors = require('cors')
var app = express()

var corsOptions = {
  origin: 'http://example.com',
  optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 
}

The above code allows only requests from example.com to be accepted by the server or have a look at this code for more dynamic whitelist & blacklist approach.

Now coming back to JWT, It is just a json encryption and decryption token which can share across API request to Authenticate as well as authorize the user.

For instance, you can store information like email, role, and nickname of the user in JWT and sent this encrypted JWT in each API request, the server authorizes this request and if true forwards to the requested API. This process of authorization and forwarding is generally implemented using an 'Interceptor' pattern wherein a middleware(Passport oAuth) does the check and auth before each API call.


Doing the above 2 things will ensure that only a client which has valid JWT token and domain address which you allowed to talk with the server. And this client will be your react app, as it is the only one with proper JWT and origin address.

So now your react app should just make sure that appropriate JWT token is passed in the API calls (post/get/put), most probably in the header of the API request, you can have an API helper service which does this for you and import that in component where-ever you make an API call. And your node server will implement the passport middleware pattern to authorize this JWT and filter non-authorized requests.

If you react app doesn't have a login, The JWT can be a client ID as well which recognizes your client as being legit. And just like user login, you can have you react app make a call to the server with data like a secret client id. This will return a JWT token. OR you can pre-generate a JWT token and you react app store it when it loads the first time, and by setting TTL and another config you can check if the Client which is making a call to your server is Old or New or some other fake client.

HTH

damitj07
  • 2,689
  • 1
  • 21
  • 40
  • Thanks for your reply! To get passport up and running would the appropriate setup be for my frontend (React) to make a request to it's backend (node server). This backend would then implement passport as middleware and only call the API if the user(ie that application) authenticates successfully? – Tom Pinchen Mar 17 '18 at 12:13
  • Yes, your react app should just make sure that appropriate JWT token is passed in the API calls (post/get/put). And your node server will implement the passport middleware pattern to authorize this JWT and filter non-authorized requests. – damitj07 Mar 17 '18 at 12:16
  • Would the JWT token not be visible when looking at the request in something like chrome dev tools? – Tom Pinchen Mar 17 '18 at 12:19
  • Yes, it will be, but you can set an expiry time(TTL) to the token. and if the user tries to copy it and make API call from his client our CORS should take care of it. – damitj07 Mar 17 '18 at 12:23
  • Thanks so much! One final question. At what point does my react app make a request to get a JWT token in the first place given that it's just application to application and there is no user log in? – Tom Pinchen Mar 17 '18 at 12:26
  • Ok, The JWT can be a client ID as well which recognizes your client as being legit. And just like user login, you can have you react app make a call to the server with data like a secret client id. This will return a JWT token. OR you can pre-generate a JWT token and you react app store it when it loads first time, and by setting TTL and another config you can check if the Client which is making a call to your server is Old or New or some other fake client. – damitj07 Mar 17 '18 at 12:31
  • Great stuff! Thanks for your help :) – Tom Pinchen Mar 17 '18 at 12:35
  • Why do you need JWT if it is visible through the dev tools? Wouldn't CORS be enough by itself? @damitj07 – hcarreras Apr 04 '18 at 20:08
  • Both JWT and CORS serve a different purpose, if CORS does the JOB, JWT is not compulsory. – damitj07 Apr 05 '18 at 05:23
  • 3
    This answer doesn't prevent API endpoints to be called from another application. I could launch Postman and make a request to one of the endpoints, and the server would happily respond. – Simone Jun 25 '19 at 08:06
1

The case of cross origin domains is when you might need to implement CORS and a security like a blacklist. JWT is a little different, as you say authenticating users who need access to your api.

I believe as long as you don't enable CORS on your server, you'll be fine.

Note that this will not stop people from doing things like:

https://example.com/api/blah to access a part of your api if it is public. This is essentially the same as your front end doing the same because the client is served to the user, and the user then has full control over the client. They could change all instances of api calls in your app to a different endpoint and you couldn't stop them, just as they could just type it in the url bar. Any public endpoints on your api have to not share sensitive info.

johnpyp
  • 867
  • 2
  • 7
  • 13
  • Thanks for your reply John. This is the problem I am facing, how can I ensure that only requests from my frontend app are responded to (ie you can't just type the URL into the browser)? Previously people might have used API keys but there is nowhere fully secure to store this on a client side application :) – Tom Pinchen Mar 17 '18 at 11:48
  • In that case you would need to implement some kind of authentication. Jwts are often cited as awesome because they are stateless, however to be able to log someone else you need to hold a state. I prefer passport as my login method – johnpyp Mar 17 '18 at 12:23
  • How do these two answer prevent a user from going into DevTools, seeing the service API and calling it direct? Since it's all from JavaScript from the users browser, that still seems to be still unsecure.. – Chizl Nov 03 '20 at 17:56