I'm building a node/express backend. I want to create an API that only work with my reactjs frontend (private API).
Imagine if this is an e-commerce website, my users will browse products and will then choose what to buy and at the time of order they might or might not login.
What is the best practice to make sure my APIs will only work with my reactjs frontend?
What happens when users decide to login or if they remain as guests?
Apply CORS - server specifies domains allowed to request your API.
How does it work?
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:
user-agent/IP/something else from the header
+ request timestamp
+ _
+ random 6 digit string
backend_api
+ domain
+ another config
) as the secret key and the string generated in above steptoken
On server-side to verify :
Redis
implementation to stop the user to use the same token for a new request.token
. If JWT can verify, then process ahead otherwise return with 403
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 aheadtoken
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 aheadtoken
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.
So, this might be a slightly lengthy answer -- but you've posted a rather interesting and important question.
As someone who spends a majority of my time writing security libraries in Node and Python to handle this exact sort of thing, I figured I'd jump in here.
The protocol you want to use to protect your React app and backend API is the OAuth2 Password Grant flow. The way it works in theory is quite simple.
On your React app, you collect a user's username/password (this could also be an email/password if that's how you've structured your application).
You then send a POST request to your backend API that looks something like this:
POST api.myapp.com/oauth/token
grant_type=password&username=USERNAME&password=PASSWORD
Make sure you use the application/x-www-form-urlencoded
content type when posting to your server.
Your server will then take this request, run it through an OAuth2 library, and generate two tokens: and Access and Refresh token.
Once you've got the tokens generated on your server side API, you'll then store those tokens in a cookie which will then be stored by the user's browser.
From this point on: everything should be automatic. When your React server makes API requests to your backend, the browser will identify the user via that cookie containing those two tokens automatically.
You'll need to use an OAuth2 library for your server-side, as this will handle things like:
There's quite a lot more to it, but this is the basic, high level idea.
As you'll notice: there are no API keys involved here. When you're working with untrusted environments (eg: mobile apps, or client side javascript apps) it is completely unsafe to store permanent API tokens -- the reason is that they can be easily extracted from source code, or javascript.
Using the flow mentioned above instead is much safer, as you get a lot of protection:
Anyhow: hope this helps!
And, if you're looking for some tools, any oauth library (server-side) should help you with this stuff. If you're looking for a service that can do this for you, you might want to check out the product I work on ( Stormpath ). It's a paid service, but handles a lot of this complexity on your behalf.
As of today, any user can see what is being passed to your backend by inspecting the network tabs in your browser console. The only way to ensure your api is secured is through user's authentication using JWT or the likes. If your app is opened to guest users, then cors cannot really help because all a user has to do is make requests identical to what they've seen in the browser's console to your apis through curl or postman.
I know this is late. But just putting my thoughts here.
Use of reCAPTCHA V3 might help in your case as the reCAPTCHA tokens can be created only in browser(depends on your configuration) and bound to a domains mentioned while creating the keys. When ever a request is made to backend, send the short-lived token created on the frontend. Backend can cross check the token and verify its validity. Below code provides token using Google reCAPTCHA
<script src="https://www.google.com/recaptcha/enterprise.js?render=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"></script>
<script>
grecaptcha.enterprise.ready(function() {
grecaptcha.enterprise.execute('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', {action: 'login'}).then(function(token) {
...
});
});
</script>
So one of way of doing While sending any AJAX requests to your server, send a header which contains somekey like apiKey : "some key". In server whenever you receive it and authenticate the domain on which your react app is getting served. If not you don't need to respond. Something like
app.get('/route', function(req, res) {
if(req.headers.apiKey == "some key) // respond with success
// respond with 401 Forbidden
});
So any request which hits the server should have apiKey in order to make i work.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.