Authentication middleware blocks OPTIONS preflight request due to custom header absence - javascript

I have a React application using axios as HTTP library, express server that uses http-proxy-middleware package and API express server that contains APIs.
React application should communicate with API server through proxy authentication server like this:
Within my React application I created this test method:
testReq(){
axios.get('http://localhost:5000/applicationData/checkMe', {
withCredentials: true,
headers: {
'x-access-token': '...'
}
})
.then(response => console.log(response.status))
.catch(e => console.log(e))
}
This is how my proxy method looks like:
server.use('/applicationData', authenticate, proxy({
target: 'http://localhost:4000',
changeOrigin: false,
onProxyReq(proxyReq, req) {
// proxyReq.setHeader('x-access-identity-email', req.decodedToken.email)
},
}))
and this is authenticate middleware function used above:
module.exports = (req, res, next) => {
const token = req.headers['x-access-token']
console.log('token', token)
if (token) {
verifyToken(token, global.config.secret).then((verificationResponse) => {
const { decoded, message } = verificationResponse
if (!decoded) return res.status(401).json({ message })
req.decoded = decoded
return next()
})
.catch((err) => {
console.error(err)
res.status(500).json({ message: 'Internal error' })
})
} else return res.status(401).json({ message: 'Missing authentication token' })
}
I enabled CORS on both API and Proxy servers like this:
server.all('/*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
res.header('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, x-access-token')
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('Access-Control-Allow-Credentials', 'true')
next()
})
The problem is that I get this response when request is sent:
I assume it's due to authentication middleware trying to access x-access-token header from OPTIONS request which doesn't exist and therefore returns 401. If I remove authentication middleware from proxy method then requests go through.
How do I make x-access-token present in OPTIONS requests? Or else, what is the proper way to handle this situation?

in facebook not allowing to paste code not sure, why, so pasting here:
in your middleware missing is (for options part as you have commented):
if (req.method == 'OPTIONS') {
res.send(200);
}
else {
next();
}
for error part, as you have said authentication triggering before cors part, so you may have to set that code before authentication (security part I am not much sure)

I think the line:
res.header('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, x-access-token')
should be:
res.header('Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, x-access-token')

Related

Express Redirection

I'm trying to redirect the user after the signup has been validated. See the code below, it never worked with everything i have tried. I also tried on the front end part with
<Redirect=to"/login" />
it wasn't a success either. I'm trying to redirect to : http://localhost:3000/login, the backend is currently on port 5000.
I also that those errors coming in the console :
Access to XMLHttpRequest at 'http://localhost:3000/login' (redirected from 'http://localhost:5000/users/signup') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET http://localhost:3000/login net::ERR_FAILED
createError.js:16 Uncaught (in promise) Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:99)
My first thought was that the header is not set, but it is in my app.js
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content, Accept, Content-Type, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, PATCH, OPTIONS"
);
next();
});
Thanks in advance
exports.signup = (req, res) => {
bcrypt.hash(req.body.password, 10).then((hash) => {
const user = {
lastName: req.body.lastName,
firstName: req.body.firstName,
email: req.body.email,
password: hash,
};
User.create(user)
.then(() => {
res.redirect(301, 'http://localhost:3000/login');
})
.catch((error) => res.status(500).json({ message: "Impossible de créer l'utilisateur. " + error }));
});
};
res.status(200).redirect('login');
(EDIT) This will return 200 and redirect the user from the backend.
This is my solution from the front-end, if you'd like it.
On the frontend, you could redirect to your desired location using window.location.replace('/login') after the express returns a value.
What I mean by this is after you've validated the user on your Express API, return 200 via res.status(200).send() to let your front end know that this user is allowed to be redirected.
An example with jQuery:
Front-End
<script>
$.get('http://localhost:3000/login', function(data, status, jqXHR) {
if (status === 200) {
window.location.replace('redirect-location.html')
} else {
// What to do if the status isn't 200
}
})
</script>
Back-End:
app.get('/login', (res, req, err) => {
// Validate
const result = validatefunction() // true if login matches, false otherwise
if (result) {
res.status(200).send()
} else if (!result) {
res.status(400).send()
} else {
res.status(500).send()
}
})
Hope I've helped.

Firebase authorization bearer token not registering

Error message: No Firebase ID token was passed as a Bearer token in the Authorization header. Make sure you authorize your request by providing the following HTTP header: Authorizaiton: Bearer or by passing a "__session" cookie
There actually is a valid token. This same setup works in other functions but not here. The main difference is that this is a delete instead of a post.
firebase
.auth()
.currentUser.getIdToken(true)
.then((token) => {
console.log(token)
return axios.delete(
`${FunctionsDir}/deleteMessage`,
{
messageID: messageID,
},
{
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/octet-stream',
},
}
)
})
and the cors setup...
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
},
allowedHeaders: 'Content-Type,Authorization',
methods: 'GET,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 200,
}
app.options('*', cors())
app.use(cors(corsOptions))
Update: Fixed, turned out to be the cors thing plus the axios.delete() sig was wrong.
This is likely a CORS issue that needs to be solved on the server.
This is because DELETE requests need to make a pre-flight request to the server asking what is acceptable to send. Because you got a response from your server, it is likely that you have already added DELETE to your Access-Control-Allow-Methods header. However, Authorization isn't considered a "safe header" by default. So you must also explicitly allow it, otherwise browsers will remove it from the request as they send it off. You can do this by adding Authorization to your Access-Control-Allow-Headers header.
If you are using Express on your server, you can allow the cors package to do this for you:
import express from "express";
import cors from "cors";
const app = express();
app.options("/deleteMessage", cors()); // enable preflight request for DELETE
app.use(cors({ origin: ["https://yourapp.com"] })); // enable CORS for all routes
// ...
If you don't like making use of third-party dependencies, you can manually add the headers as shown in this answer.
For non-express requests, like a HTTP Request Cloud Function, you can use the following code:
import express from "express";
import cors from "cors";
const corsMiddleware = cors({ origin: ["https://yourapp.com"] }); // or your manual (req, res, next) function
export const someFunction = functions.https.onRequest((req, res) => {
corsMiddleware(req, res, (err) => {
if (err) {
res.status(500).send('CORS check failed');
return;
}
// your code here
}
})

javascript fetch never reaches the POST request on nodejs server

I am trying to post some data to my nodejs server using fetch api but it seems the fetch request never reaches my server..
Fetch code
const resp = await fetch("http://localhost:5000/api/students/", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: `{
"name": "Ahmed",
"seat": 4
}`
});
console.log(resp);
const json = await resp.json();
return json;
nodeJS post body
route.post("/", CORS, async (req, res) => {
console.log('abc', req.body);
const {
error
} = validateStudent(req.body);
if (error) return res.status(400).send(error.details[0].message);
const result = await addStudent(req.body);
if (!result) return res.status(400).send("Student cannot be added");
res.status(200).send(result);
});
code of CORS middleware
console.log('avcx');
res.header("Access-Control-Allow-Origin", "*").header(
"Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
console.log('acvced');
next();
As you can see I've done some logs on my server but nothing shows up... BTW eveything is working fine with get request..
sending the same request with postman works fine.
I don't know why I'm getting this error I tackled this error for GET requests by creating a middleware 'CORS' but I'm still getting this error for POST request:-
Thanks in advance :)
I solved this problem. I wasn't handling the incoming requests correctly because I didn't know that fetch sends a preflight request with an OPTION method before sending the actual POST request. So I solved it by adding this line in my index file above every other request which will be executed every time a request (with any method) is made.
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*").header(
"Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", " GET, POST, PUT, PATCH, POST, DELETE, OPTIONS");
next();
});

URL not fetching the data in React

I wanted to give a post request to the required URL but without proxy setting it was giving cors error, I have gone through and end up with setting the proxy but still it is taking the localhost as the URL. I have attached my proxyfile.js and my code snippet with the error below.
export function PostData(userData) {
var targetUrl = "/downloadableReport";
return new Promise((resolve, reject) => {
fetch(targetUrl, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
Accept: "application/json"
},
body: JSON.stringify({
requestData: {
userName: userData.userName,
password: userData.password
}
})
}).then(response => response.json());
});
}
This is the setupProxy.js code:
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
proxy("/downloadableReport", {
target: "http://192.168.1.220:28080/xms/abc",
changeOrigin: true
})
);
};
And this is the error:
If CORS is the problem and you are using express as the backend server,
then
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,PATCH,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With, Role');
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.sendStatus(200);
} else {
next();
}
};
and then add
app.use(allowCrossDomain);
Ditch the proxy. To me, your problem looks like, you're POSTing data to the React App itself. If you are indeed having both the API and React, in the same project, I would suggest that you split them.
If they are not indeed together, update the targetUrl to a proper url with the protocol and the domain. Like var targetURl = 'http://localhost:3000/downloadableReport.
UPDATE: I read your comment reply to Sudhir. Edit the target Url as the full path to the API var targetUrl = "http://192.168.1.220:28080/xmsreport/report/downloadableReport" and add the CORS code I have provided above to the API at 192.168.1.220:28080

Angular 5 node js express POST JSON Obj. to Amazon Aws [External API] (formerly CORS issue)

SOLVED, see my answer below
My server runs on localhost:3000
I develop on localhost:4200
I am creating something and trying to post it on an Amazon API
Angular side code:
sendSomething(something) {
const body = JSON.stringify(something);
// const headers = new Headers({'Content-Type': 'application/json'});
const headers = new Headers({'Access-Control-Allow-Origin': '*'});
return this.http.post('http://Amazon-API:port/send', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.error.handleError(error.json());
return Observable.throw(error.json());
});
}
Server side:
//////////////app.js//////////////
app.use(cors());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", '*'); //<-- you can change this with a specific url like http://localhost:4200
// res.header("Access-Control-Allow-Credentials", true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header("Access-Control-Allow-Headers", 'Origin,X-Requested-With,Content-Type,Accept,content-type,application/json');
next();
});
app.use('http://Amazon-API:port', engineRoutes);
//////////////routes/engine.js//////////////
router.post('/send', engine_controller.send_something);
//////////////controllers/engine.controller.js//////////////
exports.send_something = function (req, res, next) {
const somethingID = req.body.something;
Something.findById(somethingID, function(err, something) {
if (err) {
res.status(404).json({
title: 'Something not found',
error: {message: 'Something went wrong'}
});
}
console.log(something);
if (something) {
res.status(200).json({
message: 'Something successfully sent',
something: something
});
}
})
};
I have tried posting to that API with cors, without cors and with the res.headers appended, and every other variation I could think of
I still get this error which I've seen so common around here, but still, their solutions don't seem to work for me. Still getting this error...
Failed to load http://Amazon-API:port/send: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access. The response had HTTP status code 403.
That's from NETWORK tab:
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:he-IL,he;q=0.9,en-US;q=0.8,en;q=0.7
Access-Control-Request-Headers:access-control-allow-origin
Access-Control-Request-Method:POST
Connection:keep-alive
Host:Amazon-API:port
Origin:http://localhost:4200
Any kind of help would be so much appreciated
I see you added this code but I can't post comment yet, you may try to add this code before other routes
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With,
Content-Type, Accept');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PATCH, DELETE,
OPTIONS');
next();
});
Solved,
What I did was routing back to the server on the front:
sendSomething(something) {
const body = JSON.stringify(something);
const headers = new Headers({'Content-Type': 'application/json'});
return this.http.post('http://localhost:3000/api/somethings/send-something', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.error.handleError(error.json());
return Observable.throw(error.json());
});
}
And then accepting this route as it is on the back:
//////////////app.js//////////////////
app.use('/api/somethings/send-something', engineRoutes);
/////////////routes/engine.js/////////
router.post('/', engine_controller.send_something);
And most importantly, in the controller itself I used the newly downloaded request lib to send my json data to my external API:
////////////controlllers/engine.controller.js////////////
const request = require('request');
exports.send_something = function (req, res, next) {
const SomethingID = req.body.something;
Something.findById(SomethingID, function(err, something) {
if (err) {
res.status(404).json({
title: 'Something not found',
error: {message: 'Something went wrong'}
});
}
request({
url: app.get('amazon-API:port/method'),
method: "POST",
json: something
}, function (error, response, body) {
// console.log(error) <--- returns null or error
// console.log(response.statusCode <--- returns 200 / 403 / w.e
// console.log(body) <--- returns response pure html
res.status(200).json({
message: 'Something successfully sent',
response: body,
status: response.statusCode
});
});
})
};
Now as a response I'm getting what the server which I posted to sends me back, which is exactly what I need.
Ultimately I figured my way thanks to many other questions posted here
So thank you SOF once again!

Categories

Resources