We are running a hapi JS server which uses #hapi/bell with azure provider strategy to authenticate users on the back-end
Basically, say we have our back-end running on port225.5874.com and there is a login route https://port225.5874.com/api/v2/user/sso. Here are our routes server settings.
routes: {
security: true,
cors: {
origin: [
`${configConst.client.host}:${configConst.client.hostport}`
],
headers: ['Access-Control-Allow-Headers', 'Access-Control-Allow-Origin', 'Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'Accept-language'],
additionalHeaders: ['Access-Control-Allow-Headers: Origin, Content-Type, x-ms-request-id , Authorization'],
credentials: true
}
}
Navigating to that route directly in the browser returns us information from the azure provider. However, if we try to go to that back-end route from a front-end client (i.e. localhost) we are being thrown the following CORS error
Access to XMLHttpRequest at 'https://login.microsoftonline.com/... (redirected from 'https://port225.5874.com/api/v2/user/sso') from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
To us this sounds like (we might be wrong) Access-Control-Allow-Origin is missing from 'https://login.microsoftonline.com/... but we obviously don't have control over it.
Seems like we are missing something on the front-end but unsure what it would be. Any ideas?
The front-end should be using e.g. MSAL.js to authenticate the user and use JWT authentication in the back-end.
Or you have to somehow tell the front-end request not to follow redirects and detect the situation.
Related
I'm trying to access to introspect endpoint in my Keycloak server /openid-connect/token/introspect from my front app, but I get next error:
Access to fetch at 'http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect' from origin 'http://localhost:8080' 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.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Using Postman, curl or Node app this request works fine, but from my front-app using fetch method thows this error. I'm not sure it's possible query for introspect endpoint from front-app in the browser or if it's only possible from server app.
Other endpoints like:
openid-connect/token:
openid-connect/userinfo:
Works fine using the Postman JS code.
Keycloak config
My client in Keycloak has set up Web Origins * and Access Type confidential.
Client Code
My front app is simply the Postman code JS, and I deploy it using node http-server.
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "my-client");
urlencoded.append("client_secret", "my-secret");
urlencoded.append("token", "eyJ...oCA");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Header Response
The header response in userinfo endpoint comes with Access-Control-Allow-Origin and Access-Control-Allow-Credentials but is not present in introspect endpoint.
From the looks of it, the Keycloak server prevents the CORS headers to be set for the introspection endpoint. This could be a bug or by design. I tried it and I get the same error.
If you really want to access the introspect endpoint from the web app, you could set up a NGINX reverse-proxy in front of your Keycloak server and use it to add the missing headers.
That being said, according to oauth.com you should not leave the introspection endpoint available to the public, which is what you are currently doing since anyone can retrieve the client id and secret from your web app.
If the introspection endpoint is left open and un-throttled, it presents a means for an attacker to poll the endpoint fishing for a valid token. To prevent this, the server must either require authentication of the clients using the endpoint, or only make the endpoint available to internal servers through other means such as a firewall.
This could explain the decision not to allow CORS.
Another thing, it looks like you forgot to set the token_type_hint check out this stackoverflow post for more information.
Summary:
I'm doing a fetch request in React to my Node.js server.
Whenever I do NOT include credentials: "include" and in my fetch request, the request is successfully made to the server and returned to the client.
However, when I do include credentials: "include", like the below:
fetch('http://localhost:8000/',
{ method: "GET",
'credentials': 'include',
headers: new Headers({
'Accept': 'application/json',
'Access-Control-Allow-Origin':'http://localhost:3000/',
'Content-Type': 'application/json',
})
}) ....
I get this preflight error:
login:1 Access to fetch at 'http://localhost:8000/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
Context:
Why do I need to include either of those?
I think it's obvious why I need to include the "headers", I'm using cors and if I don't include 'Access-Control-Allow-Origin':'http://localhost:3000/' then the server will not accept the request.
Why do I need to include the "credentials" if it works without it? Because if I do not include "credentials" while the fetch request executes correctly, the session cookie will not be sent to the server from my client UNLESS I include credentials: "include". If I delete all the headers and include mode: 'no-cors', then the fetch request executes and the session cookie is sent to the server, but obviously I get an opaque response, and I need to be using cors anyways.
Attempts:
There are a lot of stack overflow questions SIMILAR to this, but not exact, thus their solutions don't work.
Here are some things I have tried that didn't work:
This is already on my server, but someone suggested trying it on the client side so I did: 'Access-Control-Request-Method': 'GET, POST, DELETE, PUT, OPTIONS',
'Access-Control-Allow-Credentials': 'true',
'withCredentials': 'true',
Origin: 'http://localhost:3000/auth',
crossorigin: true,
And yes, I've already set up a proxy (which helped solve a prior issue) as such: "proxy": "http://localhost:8000"
I've tried many more other solutions to no avail, I'm certain I've read, if not all, the vast majority of all questions relating to do with this issue and the corresponding answers.
My server is setup correctly, which is why I didn't include any code from it.
In an ideal world I wouldn't need to use credentials: "include" for the session cookie to be sent back to my server, but that is the cause of another solution I had to implement.
If anyone could help me, I would be very grateful.
TLDR:
My preflight request does pass whenever I do NOT include credentials: "include", but the session cookie is not passed.
The session cookie is passed when I do include credentials: "include" and mode: 'no-cors', however, I receive an opaque response and I need to use cors.
Finally, when I combine the two (cors and credentials), I my preflight request fails with the below error:
login:1 Access to fetch at 'http://localhost:8000/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
this most likely comes from your server.
Do you have cors npm package installed in the backend ?
https://www.npmjs.com/package/cors
You will need ton configure it aswell.
Most likely in your index.js file.
const express = require("express")
const cors = require("cors");
const app = express();
app.use(cors({
origin : http://localhost:3000 (Whatever your frontend url is)
credentials: true, // <= Accept credentials (cookies) sent by the client
})
app.use("/api/whatever/the/endpoint", yourRouter);
This has to be set before any route.
Origin can be an array of whitelisted (allowed) domains to communicate with your backend api.
The correct explanation here is that the server was sending back the header Access-Control-Allow-Origin: * in the response (as described in the error message).
Without credentials this is acceptable. However, to quote the Mozilla CORS documentation,
When responding to a credentialed request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard.
Furthermore, if you were already using the npm cors module to handle setting the response headers, note that
The default configuration is the equivalent of:
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
So you have to explicitly configure it. This is why #yeeeehaw's answer worked - they suggested explicitly setting the origin option which translates into setting Access-Control-Allow-Origin behind the scenes.
Note that as an alternative solution, instead of explicitly setting origin (i.e. Access-Control-Allow-Origin) you can reflect the request's origin back as its value. The cors middleware conveniently provides for this through its configuration.
Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS.
origin: true
On Stack Overflow this has also been described here, and on the reverse proxy level here (for NGINX). Maybe the most similar question is here.
If you want to accept requests from multiple different domains you could do something like this also:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
As documented here: https://www.zigpoll.com/blog/cors-with-express-and-fetch
I am trying to use the following api endpoint using fetch:
https://api.guerrillamail.com/ajax.php?f=check_email&ip=${ip}&agent=${agent}
(outdated documentation)
When I set credentials: 'include' I get the following error:
Access to fetch at 'https://api.guerrillamail.com/ajax.php?...' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
I have to set the flag in order to submit a cookie for authentication.
After googling this problem for 1++ hour, my understanding is the following:
CORS has to be server side allowed to be able to make "none simple" request to an other domain e.g. localhost => guerrillamail.com to prevent abuse, a variable Access-Control-Allow-Origin has to be set to the domains that should be allowed to send requests. A valid option is "*", which means that ALL origins are ok.
For some reason it is not ok though in combination with the credentials: 'include flag.
Do you have any ideas why this wouldnt be allowed?
Do you know what I have to do, in order to to do the request?
And is my understanding about this correct?
The documentation you're referencing no longer applies. In that old documentation, the API was made available over HTTP rather than HTTPS. CORS doesn't apply to HTTP and wouldn't have been a problem.
In the latest documention, that API is provided over HTTPS. To deal with the CORS requirement, they also removed the need for cookies, changing it to subscr_token and sid_token parameters sent as part of the request:
version 1.5, 30th May 2011
- Removed the requirement for cookies, added subscr_token and sid_token parameters
I am using the imgur api to upload images via a node js app.
I am converting images to base64 strings and sending them via Postman works great.
I use node-fetch to make api calls.
const fetch = require('node-fetch')
...
async uploadImage(base64image) {
try {
const url = 'https://api.imgur.com/3/image'
const res = await fetch(url,
{
method: 'POST',
body: { image: base64image },
headers: {
'content-type': 'application/json',
'Authorization': 'Client-ID [my-client-id]',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Access-Control-Allow-Headers',
'Access-Control-Allow-Methods': 'POST',
}
}
)
console.log(res)
} catch(err) {
console.log(err)
}
}
Error:
Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers in preflight response.
I have tried many 'Access-Control-Allow-xxx' headers but none of them worked..
I assume it must be something simple that I am missing. I have been stuck on this for hours please help me.
Browser restricts HTTP requests to be at the same domain as your web page, so you won't be able to hit imgur api directly from the browser without running into CORS issue.
I am converting images to base64 strings and sending them via Postman
works great.
That's because Postman is not a browser, so is not limited by CORS policy.
I have tried many 'Access-Control-Allow-xxx' headers but none of them
worked..
These headers must be returned by the server in response - in your case by the imgur server. You can't set them in the request from browser, so it'll never work.
Error: Access to fetch at 'https://api.imgur.com/3/image' from origin
'http://localhost:3000' has been blocked by CORS policy: Request
header field Access-Control-Allow-Headers is not allowed by
Access-Control-Allow-Headers in preflight response.
Possible solutions to your problem:
If you have access to the backend api you can set the "Access-Control-Allow-Origin" header on the server and let your app access the api - but as you won't have access to the imgur server - you probably can't do that.
Disable CORS in the browser - you can use a plugin like: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en. This workaound should be fine for development. The plugin will disable your CORS settings and you will be able to hit imgur apis.
The third solution is using a proxy. You can setup a small node server using express. You will then hit your own node server, which in turn will hit the imgur api. As node server is not a browser environment, it won't have any CORS issue and you will be able to access imgur API that way. This is also the reason you were able to hit the API from Postman without any issues. As Postman is not a browser environment, it's not limited by CORS policy.
That's because Access-Control-Allow-Headers, Access-Control-Allow-Methods are the headers that is used by the server. The server appends the header by a middleware.
Now, imagine in the server(in this below example an express server) with CORS enabled this kind of (default) headers are getting set:
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With, Accept');
});
And you are sending Access-Control-Allow-Headers from the client side, and server sees that as a header that is not whitelisted.
So, in headers just use these:
headers: {
'content-type': 'application/json',
'Authorization': 'Client-ID [my-client-id]'
}
It should work fine.
Btw, I think it is working with postman because:
Postman cannot set certain headers if you don't install that tiny postman capture extension.
Browser security stops the cross origin requests. If you disable the chrome security it will do any CORS request just fine.
Also, according to this:
I believe this might likely be that Chrome does not support
localhost to go through the Access-Control-Allow-Origin -- see
Chrome issue
To have Chrome send Access-Control-Allow-Origin in the header, just
alias your localhost in your /etc/hosts file to some other domain,
like:
127.0.0.1 localhost yourdomain.com
Then if you'd access your script using yourdomain.com instead of
localhost, the call should succeed.
Note: I don't think the content type should be application/json it should be like image/jpeg or something. Or maybe don't include that header if it doesn't work.
I have some few observations in my own app that helped me solve this issue. I have a node app as a backend api service and a VueJS built front end. I set my node app with cors with a list of endpoints that are allowed to access my node app. Working on my local machine doesn't give me any errors until I upload it to my server.
here are my environments
Local Environment
Nodejs: 12.16.1
OS: Windows 10
DB: MySQL
NodeJS Server Framework: ExpressJS
Upload Module: Multer
Production Environment
Nodejs: 12.19.0
OS: Ubuntu 20.0.4
DB: MySQL
NodeJS Server Framework: ExpressJS
Upload Module: Multer
nginx
Here are my observations based on my production built app.
When I upload a form with image [500kb and above] [post or put], the cors error shows up but less than that, it all went fine.
If I use form data to send data to the server, I see 2 requests in my network tab, the OPTIONS and the actual request.
The actual request failed but I saw that my content-length is very high which leads me to the conclusion that my request is rejected due to the large amount of data that the client sent which my server may have limited. I know that may be misleading but the solution I did works so I don't know why cors issue is popping up even though the data limit is the issue.
MY SOLUTION:
In my nginx config file, I increased my client_max_body_size to 100M. I believe that nginx has a default of 1MB
Open /etc/nginx/sites-available/your-server-file where your-server-file can be like www.example.com or default.
Add the following line inside the server block. You can set it to any amount you want other than 100M.
server {
client_max_body_size 100M;
...
}
type in sudo systemctl restart nginx to restart nginx.
type in sudo nginx -t to check if change is successful.
Reload your app if you are using pm2 and you are done.
According to this article I used this command in linux and SOME OF(!) cross-origins fixed.
google-chrome --disable-web-security --user-data-dir="/tmp/YOUR_TEMPORARY_PATH"
This will not work if you pass headers from frontend. CORS policy is enabled by browsers. Browser blocks the response when they don't found the headers in response.
Possible Solutions:
You can pass the headers in response (If you have the access of backend or ask the API provider for this)
You can setup a middleware to resolve this.
You can get information from here
I am working HapiJs Restful web service and trying to enable cors so any client even from different domain can consume my services. I tried cors=true in server connection object but didn't work.
Where did you put cors=true? Could you add some code?
Without know exactly where you've put cors = true, this bit of code may help you:
server.connection({ routes: { cors: true } })
Or try adding the allowed cors in the config section of your route.
server.route({
config: {
cors: {
origin: ['*'],
additionalHeaders: ['cache-control', 'x-requested-with']
}
},
Take a look at this question: hapi.js Cors Pre-flight not returning Access-Control-Allow-Origin header
Addition to #James111's answer,
Even if that answer doesn't work for you. check for additional Auth headers that you are sending.
In my case it was X_AUTH_TOKEN, so in additionalHeaders you might want to add your custom header as well.
e.g.
additionalHeaders: ['cache-control', 'x-requested-with', 'X_AUTH_TOKEN']