Securing my API to only work with my front-end Securing my API to only work with my front-end reactjs reactjs

Securing my API to only work with my front-end


Apply CORS - server specifies domains allowed to request your API.

How does it work?

  1. Client sends special "preflight" request (of OPTIONS method) to server, asking whether domain request comes from is among allowed domains. It also asks whether request method is OKAY (you can allow GET, but deny POST, ...) .
  2. Server determines whether to allow, or deny request. It responds with "OK" response and set special headers that tell what domains/request methods are allowed.
  3. If client is allowed to query your API, it performs intended request, or bails out...

Clients that do respect CORS (browsers do) will be (or will not be if denied) able to connect. If client ignores CORS (REST clients, CLI tools, ...) it will be able to connect no matter what...

Still, require signed requests (authorisation)


This use case is an interesting one and I think a problem for many e-commerce sites. The product I am working on has actually had some conversations with companies trying to handle exactly this sort of thing in the mobile space. User logins can be used to tell you who is using the API, but if you don't want to force people to have a username/login you have to search for alternate solutions. What you seem to want is a way of identifying what software is attempting to use your API.

There are a couple of straightforward ways that are typically used to address this problem:

Embedded Secret

You can add a secret key to your app and require that any access to the API identifies itself using the key. People will tell you not to do this because it is really easy to extract the key. This is true, but with all security there is a cost/benefit analysis to be done to assess how much work you want to put in to protecting your API. The problem with javascript is that it is not that easy to obfuscate or hide secrets because all of the source code is right there.

If you were thinking about an environment where you had other options for your language choice then you can do more to obfuscate the secret in your app (like using the NDK in android for example). Javascript is difficult though.

The important thing to remember with an API key is that you shouldn't ever transmit it in the clear. It is really easy to steal it that way. Instead you would sign your API traffic using the key so that the server can verify that the request came from something that has the key and knows how to sign it.

Rate Limiting

Though not actually a solution to the problem, depending on what you are trying to achieve this is an option. If you are worried about large numbers of requests coming from other applications, you can rate limit to a level above what a genuine app would do and you could further block or rate limit by ip address if too many requests came in.


I took help from the above-mentioned solution by @ThePragmatist.

I have a few environment-based configs on my React website like backend API base URL (ex. staging-api.test.com, dev-api.test.com), the current domain name (ex. staging.test.com, dev.test.com), etc. so I used the variables to create a token to be sent on each public request (from a public request I mean the requests that don't need authorization). So the process I followed:

On Client side:

  • Create a string with user-agent/IP/something else from the header + request timestamp + _ + random 6 digit string
  • Create a JWT token using any environment config (I used the combination of few like backend_api + domain + another config) as the secret key and the string generated in above step
  • Send the generated token in a custom header named token

On server-side to verify:

  • A middleware is used to authenticate any public API request which has the Redis implementation to stop the user to use the same token for a new request.
  • Verify the JWT token received in the header token. If JWT can verify, then process ahead otherwise return with 403
  • Check the timestamp received in the request. If the timestamp is of 2 mins or earlier, reject the request with 524 (or something else according to your need) otherwise move ahead
  • Check if the token has a random 6 digit string at the end. If not, reject the request as the format of the token was wrong
  • Check if a redis-key for the mentioned token header value exists. If it does, that means the same header was used in a request earlier, then simply reject the request with 403 otherwise move ahead
  • Save the token header in Redis with expire time of 2 mins + 10 seconds (took 10 seconds extra just for a buffer)

This way, all the public requests will send a token which is hard enough to guess by the spammer/hacker as we used multiple configs as private-key to sign the header. Also, they won't be able to use the same header in another request. Only the client app will be able to generate the token as we followed multiple things like a header, the timestamp, a random string in the end (just to create some confusion), etc.

I hope it solves someone's query.

EDIT:

The random 6 digit string can be verified too on the server-side if we use TOTP (Time-based OTP) with an expiry time of 2-4 mins. This will make the generated token stronger as we will be able to verify every possible part of the token.