Axios CORS issue Consuming API - vue.js

I'm having a problem with cors. I don't have access to the server, providing the 3rd party API, but it does use the right headers to provide me access. I know, because a native XHR request works, with just putting the authorization and client_id headers, which are required from the api to be set.
I couldn't anyhow make it work with Axios, spent 3 days on this. I'll be really glad if someone helps me out! Please look at the code I made some comments there.
This is the native XHR request, which works:
var data = "{\"birthday\":\"1981-07-07\",\"email\":\"asdiiii#mail.com\",\"phone\":\"1234578901\"}";
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.response);
}
});
xhr.open("POST", "cross-url/api/detail");
xhr.setRequestHeader("authorization", "fake");
xhr.setRequestHeader("client_id", "fake");
xhr.setRequestHeader("content-type", "application/json");
xhr.send(data);
Axios code, which doesn't work:
axios.defaults.headers.common['Accept'] = 'application/json, text/plain'
const instance = axios.create({
baseURL: 'cross-url',
// crossdomain:true, // this doesn't help
//mode:'cors', // this doesn't help too
/*
headers: {
'content-type':'application/json',
'client_id':'client_id_here',
'access-control-allow-origin':'*', // if I put this I get an error it's denied by 'access-control-allow-headers'
'Access-Control-Allow-Headers':
'Accept,Origin,Authorization,client_id,content-type,x-requested-with', // If I put this I get still an error that the header doesn't allow origin'
'Access-Control-Allow-Credentials': 'true',
},
*/
headers: {
'client_id':'fake',
},
transformRequest: [
(data,headers) => {
delete headers.common['X-CSRF-TOKEN']
console.log(data)
// return JSON.stringify(data) // this also doesn't work'
return data
},
],
});
instance.defaults.headers.common['authorization'] = 'fake';
const postData3 = {
email:'fake',
phone:'123123123',
birthday:'1981-07-07',
}
instance.post('/api/detail', postData3).then((response) => {
console.log(response)
}).catch((e) => {
console.log(e)
console.log(e.request)
})

The server determines what headers are allowed, what methods are allowed, and what hosts are allowed.
access-control-allow-xxx are a server-to-client headers, and for all practical purposes, no servers will accept them.
Concerning CORS
Remove your access-control.xxx headers and then look at the response. If denied, the server will let you know why.
If you do not have access to the server, and your host, method, and/or client-headers are denied, all you can do is use a proxy (forward your calls from the browser to an intermediate server). You will need access to some server for that however.

Related

Invalid csrf token with NestJS

I would like to implement Csrf protection with NestJS and Quasar.
But I think I misunderstand something...
btw I'm not doing SSR, so I don't send the form from the back to the view.
Here is the NestJs back-end code:
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: true,
bodyParser: false,
});
console.log(`your App is listening on port ${PORT}`);
// Added Cookie-parser to user csurf packages
// Prevent CSRF attack
app.use(cookieParser());
app.use(csurf({ cookie: true }));
await app.listen(PORT);
}
bootstrap();
So I'm just using CookieParser and csurf package.
On my login page I call a "csrf endpoint" just to send a cookie to the view, to send it back with the post call (login).
I still get the "invalid csrf token" AND a CORS error and don't know why....(see screen below), any suggestions to make it works ?
When I try to login, error in the browser:
And error in the back-end:
Same error if I try a request with insomnia.
I thought that the CSRF token is attached to the "web browser" to go back to the back-end with nest request, so why I'm still getting this error ?
Insomnia send the cookie automatically with the right request so the token should go back to the back-end.
Any idea ?
Regards
EDIT:
After many times reading docs, It seems that CSRF protection is for SSR only ? No need to add csrf security with SPA ? Could anyone can confirm ?
EDIT: Here's another work:
The purpose here is to send a request before login to get a csrf token that I can put into a cookie to resend when I login with a POST method.
Here is my endpoint:
import { Controller, Get, Req, Res, HttpCode, Query } from "#nestjs/common";
#Controller("csrf")
export class SecurityController {
#Get("")
#HttpCode(200)
async getNewToken(#Req() req, #Res() res) {
const csrfToken = req.csrfToken();
res.send({ csrfToken });
}
}
Here is what I've done into my main.ts file (I'll explain below):
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: {
origin: "*",
methods: ["GET,HEAD,OPTIONS,POST,PUT"],
allowedHeaders: [
"Content-Type",
"X-CSRF-TOKEN",
"access-control-allow-methods",
"Access-Control-Allow-Origin",
"access-control-allow-credentials",
"access-control-allow-headers",
],
credentials: true,
},
bodyParser: false,
});
app.use(cookieParser());
app.use(csurf({ cookie: true }));
console.log(`your App is listening on port ${PORT}`);
await app.listen(PORT);
}
bootstrap();
And here my axiosInstance Interceptors of the request in my VueJS frontend:
axiosInstance.interceptors.request.use(
(req) => {
const token = Cookies.get('my_cookie')
if (token) {
req.headers.common['Authorization'] = 'Bearer ' + token.access_token
}
req.headers['Access-Control-Allow-Origin'] = '*'
req.headers['Access-Control-Allow-Credentials'] = 'true'
req.headers['Access-Control-Allow-Methods'] = 'GET,HEAD,OPTIONS,POST,PUT'
req.headers['Access-Control-Allow-Headers'] =
'access-control-allow-credentials,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-csrf-token'
const csrfToken = Cookies.get('X-CSRF-TOKEN')
if (csrfToken) {
req.headers['X-CSRF-TOKEN'] = csrfToken
console.log(req)
}
return req
},
(err) => {
console.log(err)
},
Here the same for repsonse:
axiosInstance.interceptors.response.use(
(response) => {
if (response?.data?.csrfToken) {
const {
data: { csrfToken },
} = response
Cookies.set('X-CSRF-TOKEN', csrfToken)
}
return response
},
And inside my login I make a call on the mounted function of my login component:
async mounted() {
const result = await securityService.getCsrf()
},
So now to explain:
As I said I'm not building a SSR project, that's why I want to send the token into a classic axios reponse and store it in a Cookie (this part is for test I heard that storing a csrf token into a classic cookie is not the right way.)
And for each next request I get the csrf token and "attach" it to the request into the headers, making my headers "custom".
Here is a problem I don't know how to make custom headers works with nestJS and CORS, that's why I try many thing with CORS options in NestJS and writte some custome header before the request go to the back-end but without success, I've got the same error message:
I'm a bit confuse about this problem and CORS/CSRF is a big deal for spa, my questions still the same, with CORS and SameSite cookie attributes, and my api is in a subdomain of my front-end, is it really necessary to make a anti-csrf pattern ?
Btw how can I make my custom headers working and why CORS say to me there is no "Access-Control-Allow-Origin" header but there is:
try to generate csrf token and pass to front on each petition
// main.ts - from NestJs - Backend
// after app.use(csurf({ cookie: true }))
app.use((req: any, res: any, next: any) => {
const token = req.csrfToken()
res.cookie("XSRF-TOKEN", token)
res.locals.csrfToken = token
next()
})
from: https://github.com/nestjs/nest/issues/6552#issuecomment-1175270849

Vuejs axios get request always fetch new values [duplicate]

I am trying to query a quote API for a freeCodeCamp project I'm updating to React.js. I am now trying to use Fetch or Axios to query the API but it's caching the response in the browser. I know in $ajax there is a { cache: false } that would force the browser to do a new request.
Is there some way I will be able to do the same with Fetch or Axios?
The cache-control setting seems to be already set to max-age: 0 by Axios.
This is my code I have that is querying the API.
generateQuote = () => {
axios.get('https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1')
.then(response => {
const { title, content, link } = response.data[0];
console.log(title, content, link)
this.setState(() => ({ title, content, link }));
})
.catch(err => {
console.log(`${err} whilst contacting the quote API.`)
})
}
Okay so I found a solution. I had to set a timestamp on the API url to get it to make a new call. There doesn't seem to be a way to force axios or fetch to disable cache.
This is how my code now looks
axios.get(`https://quotesondesign.com/wp-json/posts?filter[orderby]=rand&filter[posts_per_page]=1&timestamp=${new Date().getTime()}`)
.then(response => {
const { title, content, link } = response.data[0];
console.log(title, content, link)
this.setState(() => ({ title, content, link }));
})
.catch(err => {
console.log(`${err} whilst contacting the quote API.`)
})
I added these headers to all axios requests and it's working well.
axiosInstance.defaults.headers = {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0',
};
If you do not want to disable caching for all axios requests, you can disable caching for only one request by using the following parameters in the axios call:
axios.get(
'https://YOUR-URL.com',
{
// query URL without using browser cache
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Expires': '0',
},
}
)
It seems, adding timestamp is the only always working way.
If you're using Vue, for example:
const api = axios.create({
baseURL: 'https://example.com/api',
params: {
t: new Date().getTime()
}
})
Vue.prototype.$api = api
So you can use it with:
this.$api.get('items')
And it will always add different timestamp to the url, depending on current request time.
I think you just need to make the url different each time you make the axios call. Timestamp is just one way to do so. Also consider disabling or filtering service workers caching method if you are developing a PWA.
Create an instance of axios and then add timestamp to every request.
const axiosInstance = axios.create({})
axiosInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
config.params = { ...config.params, timestamp: Date.now() };
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);

CORS blocked even when using CORS allow extension on browser

I'm trying to recover some data from a government api from my country.
But i'm having a issue with CORS.
Access to XMLHttpRequest at 'http://www.portaltransparencia.gov.br/api-de-dados/bolsa-familia-por-municipio?mesAno=202004&codigoIbge=5300108&pagina=1' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
That's the error and this line of code is my axios code:
let headers = {
'Accept': '*/*',
'chave-api-dados': 'cec73fb24c54ff134d2053da6b471467'
}
axios
.get(url, { headers })
.then((response) => {
const { benefits } = response.data;
this.gov_benefitsF = benefits;
console.log(benefits);
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
What should I do to make this working? I'm using this extension CORS Allow Access and there's nothing working here
Browser extensions which add CORS headers in responses don’t generate responses to pre-flight OPTIONS requests.
You need the server to grant you permission, or to use a proxy.

How to properly use passport-github for REST API authentication?

I am building a vue.js client which needs to be authenticated through github oauth using an express server. It's easy to do this using server side rendering but REST API has been troublesome for me.
I have set the homepage url as "http://localhost:3000" where the server runs and I want the authorization callback url to be "http://localhost:8080" (which hosts the client). I am redirecting to "http://localhost:3000/auth/github/redirect" instead, and in its callback redirecting to "http://localhost:8080". The problem I am facing is that I am unable to send user data to the vuejs client through res.redirect. I am not sure if I am doing it the right way.
router.get("/github", passport.authenticate("github"));
router.get(
"/github/redirect",
passport.authenticate("github", { failureRedirect: "/login" }),
(req, res) => {
// res.send(req.user);
res.redirect("http://localhost:8080/"); // req.user should be sent with this
}
);
I have implemented the following approach as a work around :-
A route that returns the user details in a get request :
router.get("/check", (req, res) => {
if (req.user === undefined) {
res.json({});
} else {
res.json({
user: req.user
});
}
});
The client app hits this api right after redirection along with some necessary headers :
checkIfLoggedIn() {
const url = `${API_ROOT}auth/check/`;
return axios(url, {
headers: { "Content-Type": "application/json" },
withCredentials: true
});
}
To enable credentials, we have to pass the following options while configuring cors :
var corsOption = {
origin: true,
credentials: true
};
app.use(cors(corsOption));

handle network request failed in react native

I'm facing an issue while using react native fetch api. many times request got failure . I have a high speed connection. but many times it got failed.
that issue is happening In android,ios both.
const shoppingApi = 'myserverlink';
async function Sendshoppinapi(data) {
try {
let response = await fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
});
let responseJson = await response.json();
return responseJson;
}
catch (error) {
Alert.alert(error.toString())
}
}
export {Sendshoppinapi};
data that I sending server as post request
add_to_wishlist = (item,index) => {
{
let data = new FormData();
data.append('methodName', 'add_to_wishlist');
data.append('user_id', global.userid)
data.append('item_id', this.props.navigation.state.params.itemid.toString())
Sendshoppinapi(data).then((responseJson)=>{
console.warn(responseJson);
if(responseJson.responseCode == '200'){
this.setState({fav:false})
Alert.alert('SHOPPING','Item added to wishlist successfully.',[{text: 'OK',},],{ cancelable: false })
}
else{
this.setState({fav:false})
Alert.alert('SHOPPING','Item already .',[{text: 'OK',},],{ cancelable: false })
}
})}
}
Error that when request got failed
I've quoted an answer I used for another post - however I have added await.
You can check the status of the call, to determine perhaps why the network call failed. Try using fetch's ok to check whether the response was valid, for example:
.then(function(response) {
if (!response.ok) {
//throw error
} else {
//valid response
}
})
Using await:
let response = await fetch(url)
if (response.ok) return await response.json()
You can also access the response's status like:
response.status;
or also, statusText such as:
response.statusText;
checkout the below:
https://developer.mozilla.org/en-US/docs/Web/API/Response/statusText
https://developer.mozilla.org/en-US/docs/Web/API/Response/status
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Use then() function with promises. (Requested code snippet)
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
//resp contains your json data
});
You also can make your function returns a Promise, and use it with then():
function sendShoppingApi(data) {
return new Promise((resolve, reject) => {
fetch(shoppingApi, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'multipart/form-data'
},
body: data
})
.then((resp) => {
return resp.json();
})
.then((resp) => {
resolve(resp);
/*
you should also check if data is valid, if something went wrong
you can reject the promise:
if(!dataOK)
reject("error message");
*/
});
});
}
So now you can do something like this:
sendShoppingApi(data)
.then((resp) => {
//do stuff with your data
})
.catch((err) => {
//handle error
});
UPDATE
could be a duplicate of this: React Native fetch() Network Request Failed
For the case when you are running the app on the android device, the API is on a computer and both of them are on the same network I have added some possible things to check. I haven't detailed specific solutions since there are many answers on each topic.
Do a quick check with ngrok https://ngrok.com/ on the free plan to see if that works. If yes:
Make sure the API is accessible by trying to access it on the device browser (most important is to check if you allow the port at inbound rules, firewall).
If you are using HTTPS, you might get an error if your react native env is not properly configured to accept not trusted certificates, assuming you are using a non trusted one. Do a check without HTTPS, only with HTTP, to see if it's the case. https://github.com/facebook/react-native/issues/20488