I'm currently building an app with a React/Next.js frontend (deployed on Vercel) and a Django (Rest Framework) backend (deployed on Heroku).
The frontend fetches some information from the backend to populate the pages, but I also want users to be able to call the API directly. I want them to be able to call it through the frontend URL for consistency (e.g. calling https://www.frontend.com/api/some_endpoint).
I'm wondering a bit about the mechanics of porting the Django backend through Next's API Routes. For example, in the Next project I could make a file ./pages/api/some_endpoint.js as such:
export default function handler(req, res) {
res.status(200).json(
fetch("http://www.backend.com/api/some_endpoint/", {
mode: "cors",
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: (JSON.stringify(req.body, null, 2),
}).then((res) => res.json())
);
}
Questions
My questions relating to the above are as follows:
Will this cause issues with authentication? In particular, the Django API currently requires users to supply a user-specific API token. I've temporarily shut this off so the frontend can fetch the data to populate the pages, but was intending on devising a way to only require an API token for requests that don't originate from frontend.com. If all requests are "ported" through frontend.com in this fashion, though, will that make authentication impossible?
Related, currently the list of allowed CORS origins is only frontend.com. Will this need to change in light of the answer to the above? Are there any other security considerations that stem in particular from this API porting?
What performance hit will there be in terms of speed with this setup? I'm sure this question is not easy to answer, and very fast speeds aren't necessary for this application, but I'm just curious. Would it be better to alternatively put the API on a subdomain or something? I'm not very experienced in networking so I'm not sure how this would work.
Should there be error handling implemented in the Next API, or should it just be a "pass through"? I suspect the answer is that there should be, but I'm not sure where this would be implemented.
Any guidance or advice is greatly appreciated!
Related
Apologies if I sound uneducated on the matter, I have never used JS until a few days ago and don't have much server/API experience.
Essentially I am using nextauth.js to authenticate a users twitter account and allow them to sign into an app. Next.js is providing them with a sign in option and once they sign in and authorize the app I now have access to their api key/ secret.
I also created an app in node.js that will allow me to change their twitter header for them. This runs on a separate port. I need to be able to send the key/secret I get from the next.js app to the node server so that I can use these keys to make the changes to their account. I am unsure of how to do this and what type of requests are required.
Ive spend a good amount of time looking into online resources but I haven't been able to conceptualize it fully. Any help/resources/explanations would be very appreciated! Thank you in advance!
if you have access to secret key you wanna send on the client-side you can simply use fetch
const response = await fetch('https://yourbackend.url/here', {
method: 'POST', // make sure the method is set to POST
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({secretKey: "yourseceretkeyhereaslkdaasldkaslkdjlask"}) // send the secret as payload in the body
}).then(res => res.json())
.catch(console.log);
catching the payload on your server will totally depend on what kind of server you have setup.
we are trying to call POST api to generate certificate for renewal of certification. However as per the 3rd party API reqirement, we need to validate Kerberos authentication. We have tried many thhings with no luck.
We are getting 401 unauthorized error when we are trying to run it.
Tech stack: Node.js, Javascript, Kerberos auth, Linux OS.
Code snippet:
const axios = require('axios');
const data = {
Request: "-----BEGIN CERTIFICATE REQUEST-----<csr key>-----END CERTIFICATE REQUEST-----",
CertificateTemplateName: "DBAPI1Y",
PrimaryEmail: "test#test.com"
};
axios.post('http://dummyurl.com/webapi/SubmitRequest', data, "Negotiate")
.then((res) => {
console.log(`Status: ${res.status}`);
console.log('Body: ', res.data);
}).catch((err) => {
console.error(err);
});
Tried this approach with no luck: How do I use Negotiate or Kerberos authentication with axios?
Can someone please help?
I was unable to find any traces of Negotiate support within axios. Your linked post says that in browsers it would work automatically, which is indeed true for any in-browser JavaScript (that uses Fetch or XMLHTTPRequest behind the scenes) – but the node CLI is a different world. When run through Node CLI, axios will use the HTTP client provided by Node, which doesn't do Kerberos.
It seems that you will need to implement this manually, by using the krb5 module to get a token, then telling axios to send it in the Authorization header – similar to Bearer tokens.
The following example seems to kind of work, although not very pretty – it cannot cope with '30x' redirects:
const axios = require("axios");
const krb5 = require("krb5");
// The service is "HTTP" (uppercase), regardless of the URL being http or https.
token = await krb5.spnego({hostbased_service: "HTTP#dummyurl.com"});
resp = await axios.post("https://dummyurl.com/webapi/SubmitRequest",
data,
{
headers: {
"Authorization": `Negotiate ${token}`,
},
// SPNEGO tokens are single-use, so if Axios tries to
// follow redirects, the 2nd request will be rejected
// as a "replay". So tell it to not even try.
maxRedirects: 0,
});
(I have no experience with writing "proper" Node.js code, especially async-based code, and while in theory it should be possible to use axios' transformRequest to dynamically get a new token for each request, I was unable to figure out how to do it within a sync function.)
In general, I would probably do the task in Python instead (which has well-maintained Kerberos integration using requests-gssapi or httpx-gssapi for Requests or httpx respectively).
Note that Kerberos doesn't ensure data integrity for HTTP, so you must still use HTTPS with it, otherwise someone could still simply MitM the requests.
Apart from the above approach suggested by #user1686, I have solved the problem using NTLM authentication. Specifically, I have used the httpntlm module.
By using the httpntlm package, we don't need to deal with kerberos packages as all the kerberos packages have many dependencies on node-gyp.
Thus, it's better to use some other solution apart from the kerberos related packages.
Note: This solution worked for me, but it will vary from use case to use case.
I am writing a headless solution for a WordPress website and noticed that for one particular endpoint, I need to authenticate to pull some data that will be used publicly. But, I'm concerned that where I'm using it will expose it to the web.
In my store/index.js I use the nuxtServerInit action method to execute some actions and I pass them some objects they need to fulfill their tasks:
async nuxtServerInit ({ dispatch }, { $axios, app }) {
await dispatch('initialize', { $axios, app })
},
$axios is passed because it will be used to query the API, and app is passed to help build the options to authenticate the request.
Is this a security vulnerability in Nuxt SSR? I think it is. If so, where are the only valid areas you can use secrets? asyncData ()?
If you're using SSR, you can use the privateRuntimeConfig runtime object and pass your secret in the nuxt.config.js file
export default {
privateRuntimeConfig: {
apiSecret: process.env.API_SECRET
}
}
If you read the documentation of nuxtServerInit, you can see that
Vuex action that is called only on server-side to pre-populate the store
Since this method is server-side only, you can use apiSecret (in my example) and it should be totally fine security-wise.
PS: Keep in mind that everything beyond what is generated on the server (hence, with NodeJS or nuxtServerInit) is "public". So your VueJS's client code lifecycle hooks are public: mounted(), fetch(), asyncData() because they will be visible on your browser's devtools.
Also, should your endpoint be that critical? If so, nuxtServerInit is the good way to go. If you need to fetch some more data in a "private way", you'll need to proxy it through some backend to hide the sensitive info and retrieve only the useful public data.
Okay, so atm i have a frontend application built with Nuxt JS using Axios to do requests to my REST API(separate).
If a user does a search on the website the API URL is visible in XMLHttprequests so everyone could use the API if they want to.
What is the best way of making it so that only users that search through my website gets access to the API and people that just directly to the URL gets denied. I suppose using some sort of token system, but what is the best way to do it? JWT? (Users never log in so there is no "authentication")
Thanks!
IMO, you CANNOT block other illegal clients accessing your
backend as you describe that the official client and other illegal have the same knowledge about your backend.
But you can make it harder for illegal clients to accessing your backend through some approach such as POST all requests, special keys in header, 30-minutes-changed token in header and server-side API throttling by client IP.
If the security of the search API is really important, authenticate it by login; if not, just let it go since it is not in your critical path. Let's focus on other important things.
I'm in the same "boat" and my current setup is actually in VueJs but before even come to StackOverflow I developed a way to actually, the frontend calls the server and then the server calls the API, so in the browser, you will only see calls to the server layer that, the only constraint is that the call must come from the same hostname.
backend is handled with expressJs and frontend with VueJs
// protect /api calls to only be originated from 'process.env.API_ALLOW_HOST'
app.use(api.allowOnlySameDomainRequests());
...
const allowHostname = process.env.API_ALLOW_HOST ||'localhost';
exports.api = {
...
allowOnlySameDomainRequests: (req, res, next) => {
if(req.url.startsWith('/api') && req.hostname === allowHostname) {
// an /api call, only if request is the same
return next();
} else if (!req.url.startsWith('/api')) {
// not an /api call
return next();
}
return res.redirect('/error?code=401');
},
...
};
In our case, we use Oauth2 (Google sign through passportJs) to log in the user, I always have a user id that was given by the OAuth2 successful redirect and that user id is passed to the API in a header, together with the apikey... in the server I check for that userid permissions and I allow or not the action to be executed.
But even I was trying to find something better. I've seen several javascript frontend apps using calls to their backend but they use Bearer tokens.
As a curious user, you would see the paths to all the API and how they are composed, but in my case, you only see calls to the expressJs backend, and only there I forward to the real API... I don't know if that's just "more work", but seemed a bit more "secure" to approach the problem this way.
I need to make an API request to an external API using an API Key. I know how to make this API request in React by writing a onSubmit function. But since I have an API key that I want to keep a secret I am going to write a simple Node app to house env variables.
Besides messing around in node this is my first production experience with Node and I am wondering if my thought process is correct and if not, the better way to do this.
Most of this question will be pseudo code since I haven't started with the Node portion yet.
The idea is that from within the React component it would call the Node app who in turn would call the external API.
React -> Node -> External API
So the React component would be something like so:
handleSubmit: function() {
var data = this.refs.testData.getDomNode().value;
$.ajax({
url: '/my-node-endpoint',
dataType: 'json',
type: 'POST',
data: { test: data },
success: function(data) {
// Whatever success call I want to make
}.bind(this)
})
}
And then in my Node app it would like something like this:
app.post('/my-node-endpoint', function(req, res) {
// Store the values we are posting as JSON
// Start the post request
// On End tell the React component everything is ok
// Prosper
});
As always, thanks for any help that is offered.
Your thought process looks right to me.
If the API you are calling is from a different domain, you will have to build a wrapper on your node server like you did here. Unless the external API supports cross-origin requests with no domain restrictions (such as MapBox web services), you will have to do this.
Several improvements to your code:
As far as I know, you can use React.findDOMNode(this.refs.testData).value instead of this.refs.testData.getDomNode().value. getDomNode() is deprecated in v0.13.
For all the AJAX calls, you can use the Store concept in Flux. The store keeps the states of the data, including updating data through AJAX request. In your React UI code, you just need to call the methods of the store, which makes your UI code clean. I usually create a store class myself without using Flux.