Fetch - respond to preflight response - javascript

I am struggling with fetch over CORS, with authorization:
const token = 'this.is.secret!';
fetch('http://corsserver/api/hello', {
method: 'get',
credentials: 'include',
mode: 'cors',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(response => {
console.log(response);
}).catch(error => {
console.log(error);
});
When I run this request, Chrome sets the header as:
Request Method:OPTIONS
I look this up and it's a preflighted request.
I mean, that's really cool and stuff. However I can't figure out how to send the actual request after the preflight comes back okay! What's the next step? How do I send the GET request?
I'm missing something very basic here.

Browsers will automatically send the actual GET request if the OPTIONS request is successful. But if the OPTIONS request isn’t successful, browsers will never make the GET request.
And there’s no way make that GET request without the browser doing the OPTIONS request.
So if the browser isn’t doing the GET, it can only mean the OPTIONS must be failing, and you need to find out why. The browser should be logging a message with the reason to its devtools console, so you should start by checking there (and then either edit/update the question to add that info, or post a new separate more-specific question with the error message).
One guess for what the problem could be: Maybe the server is requiring authentication for the OPTIONS request. If so, you need to fix it so that the server doesn’t—because when the browser makes the OPTIONS request, it doesn’t send the Authorization header+value from your code.
Instead in fact the whole purpose of the OPTIONS request in this case is for the browser to ask, Are you OK with getting cross-origin requests that have an Authorization request header?, and for the server to respond in way that indicates if it allows the Authorization header.
So because of that, the server must be configured to respond to any OPTIONS requests (from allowed origins at least) with a 2xx success response, without requiring authentication.
The way you’d know whether the server is requiring authentication for that OPTIONS request is if the CORS message your browser is logging shows a 401 status for the OPTIONS response.
Example code for handling the OPTIONS in a Node.js server environment:
if (req.method === 'OPTIONS') {
res.send();
return;
}
…to make the server send a 200 response with no response body, which is what you want for this. Do note that this explicitly allows all OPTION requests.

Related

How can I bypass CORS to make a call to Firestore from Blazor WebAssembly? (call is in in JavaScript) [duplicate]

This question already has answers here:
CORS error even after setting Access-Control-Allow-Origin or other Access-Control-Allow-* headers on client side
(2 answers)
Closed 1 year ago.
I am building a simple login page using Blazor WebAssembly and utilizing JSInterop to write JS functions in order to work with Firebase/Firestore. I have a function that signs the user in through a simple custom form using Firebase methods, then grabs the id token and user id to then pass into another function that requests access to Firestore. Everything works perfectly through Postman, but when I run through this process in my app, I am blocked by a CORS error:
Access to fetch at 'https://firestore.googleapis.com/v1/projects/trailblazor-
5ba6f/databases/(default)/documents/users/JJNlKtvGMcUm7MjfkuJy6WdlMiO2' from origin
'https://localhost:44318' has been blocked by CORS policy: Response to preflight request doesn't pass
access control check: It does not have HTTP ok status.
Here is my code for the request to access user data in Firestore (passing in the id token and user id):
async function sendToken(idToken, userId) {
try {
const response = await fetch(`https://firestore.googleapis.com/v1/projects/trailblazor-5ba6f/databases/(default)/documents/users/${userId}`, {
headers: new Headers({
'x-api-key': '**omitted for security**',
'Authorization': 'Bearer ' + idToken,
'Access-Control-Allow-Origin': 'https://localhost:44318',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Headers': 'Content-Type',
'Content-Type': 'application/json'
}),
mode: 'cors'
});
const receivedResponse = await response.json();
if (receivedResponse != null) {
console.log(receivedResponse);
}
} catch (error) {
return error.message;
}
}
As you can see, I've tried loads of headers and nothing has worked so far. Even the CORS-enabling Chrome extension I have enabled is not helping. I have thought about making a proxy server, but I'm not sure if that makes sense quite yet. I've reviewed the Firebase docs and this should be how I can access Firestore, and like I said, everything works flawlessly in Postman (of course).
Firstly, CORS is managed by the server. The fact that you add some header in your request doesn't help. It the server who, based on the domain of incoming requests, sets appropriate headers if CORS requests are allowed.
You're right about making a proxy server. You can use https://cors-anywhere.herokuapp.com/corsdemo, which is a proxy server for frontend developers to make CORS requests. The github page is here

Access to fetch at _ from origin _ blocked by CORS policy: Response to preflight request doesnt pass access control check: doesnt have HTTP ok status

I'm trying to make authorized requests to LinkedIn's API after using OAuth2.0 to sign in users. I keep getting errors regarding the CORS policy and need help in sending the correct headers so that I no longer get these errors.
I've tried including a couple different 'Access-Control-Request' headers but I keep getting the same error. I'm pretty sure it has something to do with the 'Authorization' header I'm sending to the API, but this header is required from the documentation to receive the correct information.
https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow?context=linkedin/context#step-1-configure-your-application
The code I'm using to make this call is shown below...
async function getProfileData(accessToken, expiresIn) {
const requestUrl = `https://api.linkedin.com/v2/me`;
const response = await fetch(requestUrl, {
method: 'GET',
headers: {
'Host': 'api.linkedin.com',
'Connection': 'Keep-Alive',
'Authorization': `Bearer ${accessToken}`,
'Origin': REDIRECT_URI,
'Access-Control-Request-Method': 'GET',
'Access-Control-Request-Headers': 'Content-Type, Authorization'
}
});
return response;
}
The accessToken, in this case, comes from a previous network call using an authorization code from a previous step. Everything before this is working and I'm getting a response with what I assume to be a valid access token.
Previously, I was getting an error saying that my access token wasn't valid even though I followed the documentation step-by-step to retrieve it. I'm not sure if this is the same issue or not, but I can't see a reason why I would have an invalid token (the token wasn't expired and I never rejected the permissions on sign-in).
The response I'm expecting to get back from the call is the basic profile data from the user that is signed in but instead, I get this:
OPTIONS https://api.linkedin.com/v2/me 401
getProfileData # app.js:74
(anonymous) # app.js:50
Access to fetch at 'https://api.linkedin.com/v2/me' from origin 'https://simple-linkedin-login2.netlify.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

Fetch request doens't return the requested data

I'm trying to get XML data with a fetch() request and the appropriate header.
The request succeeds but the reponse is empty. The weird part is that the browsers networking tab in the debugger gives me a 200 OK and has the requested Response payload but I can't seem to get this data even though it's present. Any clues as to why?
Snapshot of the debugger
data = {
get:(url)=>{
return new Promise(resolve => {
let auth = new Headers({
"username":key.user,
"password":key.password,
"Accept":"application/xml"
})
fetch(url,{
method:"get",
mode:"no-cors",
headers: auth,
credentials:"include"
})
.then(response => console.log(response))
//empty response
})
}
}
You are making a cross-origin request but you said mode:"no-cors",.
mode:"no-cors", means "If I need permission to do something cross-origin, then don't ask for it, and don't throw an error".
no-cors — Prevents the method from being anything other than HEAD, GET or POST, and the headers from being anything other than simple headers. If any ServiceWorkers intercept these requests, they may not add or override any headers except for those that are simple headers. In addition, JavaScript may not access any properties of the resulting Response. This ensures that ServiceWorkers do not affect the semantics of the Web and prevents security and privacy issues arising from leaking data across domains.
Since you need permission to read data from a cross-origin request, it is failing silently (because you explicitly told it to do so).
Use mode: "cors".

302 Response Error

I am making a GET request to my local webservice which I am expecting a 302 response to be returned with a location in the header. However, I get an undefined response back and a network error even though I can see locally that the request is being served and response is being created without any errors in the webservice.
I have tried in Postman and Chrome, and it receives the redirect response and redirects accordingly.
I'm not sure if this is a CORS problem and if so, how can I solve this?
I've already added in the response header for CORS filter
Access-Control-Expose-Headers: Location, [own headers]
Access-Control-Allow-Origin: '*'
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Max-Age: [some age]
Access-Control-Allow-Headers: [own headers]
And the location is present in the header when I use Postman
The request I am making using Axios and the config is
const config = {
url: [someURL],
method: 'GET',
headers: {
'customHeader':'token',
},
params: {
[params]
},
maxRedirects: 0,
validateStatus: status => (status >= 200 && status < 300) || status === 302,
};
Any help would really be appreciated as to why the response is undefined when it reaches my JS code, but works fine in Postman and Chrome.
A way I could resolve this is to use HTTP status code 200 and get the location header to redirect, but I want to avoid this because it is technically a redirect response.
The 'customHeader':'token' part of your request triggers your browser to first send a CORS preflight OPTIONS request. Any headers you add to a request other than headers defined as CORS-safelisted request-headers trigger browsers to send a CORS preflight OPTIONS request.
The reason you don’t get this from Postman is that unlike browser engines, Postman doesn’t implement CORS, so it doesn’t send the OPTIONS request. (Postman does not operate under the same-origin Web-security model that browsers enforce for Web applications.)
If the server doesn’t respond in the right way to CORS preflight OPTIONS requests, your request will fail and the only workaround is to not add that 'customHeader':'token' part to your request, or otherwise construct your request in any way that triggers your browser to do CORS preflight.

Preflight OPTIONS request returns status 405

I am currently developing a dashboard which consumes the Mention API. However, I am having difficulties due to the CORS policy.
If the dashboard is opened on Chrome or another browser that has a strict CORS policy, they make a preflight OPTIONS request each time, but these appear to not be supported by the Mention API.
Therefore, every time I do something like:
this.mentionAPI = axios.create({
baseURL: 'https://web.mention.net/api/accounts/my-account-id',
headers: {
'Authorization': 'Bearer my-access-token',
}
});
this.mentionAPI.get('/alerts')
.then((response) => {
console.log(response);
})
.catch((response) => {
console.log(response);
});
I get a response with the status 405 Method Not Allowed. This suggests that the OPTIONS requests are not handled by the Mention API at all.
Naturally, in my case I can just make sure that the browser is configured to not perform preflight requests. After all, my use case prescribes just one client, which I control completely. Alternatively, I could build a server-side service to make the requests on my behalf, however it seems like hunting a fly with cannon, since client side JavaScript is more than capable to perform everything else.
Any help on this issue would be really appreciated. Perhaps I'm misunderstanding how the API is intended to be consumed?

Categories