Related
I'm building a frontend app with React and Redux and I'm using axios to perform my requests. I would like to get access to all the fields in the header of the response. In my browser I can inspect the header and I can see that all the fields that I need are present(such as token, uid, etc...), but when I call
const request = axios.post(`${ROOT_URL}/auth/sign_in`, props);
request.then((response)=>{
console.log(response.headers);
});
I get just
Object {content-type: "application/json; charset=utf-8", cache-control: "max-age=0, private, must-revalidate"}
Here my browser network tab,as you can see all the other fields are present.
Bests.
In case of CORS requests, browsers can only access the following response headers by default:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
If you would like your client app to be able to access other headers, you need to set the Access-Control-Expose-Headers header on the server:
Access-Control-Expose-Headers: Access-Token, Uid
This really helped me, thanks Nick Uraltsev for your answer.
For those of you using nodejs with cors:
...
const cors = require('cors');
const corsOptions = {
exposedHeaders: 'Authorization',
};
app.use(cors(corsOptions));
...
In the case you are sending the response in the way of res.header('Authorization', `Bearer ${token}`).send();
I was facing the same problem. I did this in my WebSecurity.java, it's about the setExposedHeaders method in the CORS configuration.
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Arrays.asList(FRONT_END_SERVER));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With","Origin","Content-Type","Accept","Authorization"));
// This allow us to expose the headers
configuration.setExposedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Authorization, x-xsrf-token, Access-Control-Allow-Headers, Origin, Accept, X-Requested-With, " +
"Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
I hope it works.
Faced same problem in asp.net core
Hope this helps
public static class CorsConfig
{
public static void AddCorsConfig(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.WithExposedHeaders("X-Pagination")
);
});
}
}
According to official docs:
This may help if you want the HTTP headers that the server responded with. All header names are lower cased and can be accessed using the bracket notation. Example: response.headers['content-type'] will give something like: headers: {},
There is one more hint that not in this conversation.
for asp.net core 3.1
first add the key that you need to put it in the header, something like this:
Response.Headers.Add("your-key-to-use-it-axios", "your-value");
where you define the cors policy (normaly is in Startup.cs) you should add this key to WithExposedHeaders like this.
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.WithExposedHeaders("your-key-to-use-it-axios"));
});
}
you can add all the keys here.
now in your client side you can easily access to the your-key-to-use-it-axios by using the response result.
localStorage.setItem("your-key", response.headers["your-key-to-use-it-axios"]);
you can after use it in all the client side by accessing to it like this:
const jwt = localStorage.getItem("your-key")
Custom HTTP headers can not be accessed on client-side due to CORS restrictions. You need to add Access-Control-Expose-Headers setting on the server-side.
What are Access-Control-Expose-Headers?
Please go to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
By default only these HTTP headers are exposed:
Cache-Control
Content-Language
Content-Length
Content-Type
Expires
Last-Modified
Pragma
For custom HTTP headers, you need to customize Access-Control-Expose-Headers in response headers.
If you are using Django on the server side you can use django-cors-headers (https://pypi.org/project/django-cors-headers/) for CORS settings management.
For example, with django-cors-headers you can add a list of HTTP headers that are to be exposed to the browser by CORS_ALLOW_HEADERS setting
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = list(default_headers) + [
'my-custom-header',
]
In case you're using Laravel 8 for the back-end side with CORS properly configured, add this line to config/cors.php:
'exposed_headers' => ['Authorization'],
In axios CORS requests, browsers can access only few headers by default.
But if you need to access a custom header from response, you have to send response with Access-Control-Expose-Headers form your backend server.
Hare is a example for Nodejs backend and Reactjs front end:
res.header('Access-Control-Expose-Headers', 'x-xsrf-token');
return res.header("x-xsrf-token", token).status(200)
.send({
id: user.id,
email: user.email,
});
res.header('Access-Control-Expose-Headers', 'x-xsrf-token');
for this line I can log my custom header like
axios.post("/login", {
email: emailInput.current.value,
password: passwordInput.current.value,
})
.then(function (response) {
console.log(response.headers["x-xsrf-token"]);
});
Without Access-Control-Expose-Headers in your response you will get undefine in console log. Check your response header in network tab that it contains the header with your custom name with it.
For the SpringBoot2 just add
httpResponse.setHeader("Access-Control-Expose-Headers", "custom-header1, custom-header2");
to your CORS filter implementation code to have whitelisted custom-header1 and custom-header2 etc
try like this
.then(res =>{
console.log(res);
console.log(res.headers['x-total-count']);
setTotalRecords(res.headers['x-total-count']);
setTableData(res.data);
});
for django help
CORS_EXPOSE_HEADERS = [
'your header'
]
For Spring Boot 2 if you don't want to use global CORS configuration, you can do it by method or class/controller level using #CrossOrigin adnotation with exposedHeaders atribute.
For example, to add header authorization for YourController methods:
#CrossOrigin(exposedHeaders = "authorization")
#RestController
public class YourController {
...
}
If you are using Django without django-cors-headers, you can write custom middleware.
class CustomCorsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response['Access-Control-Expose-Headers'] = 'MY-HEADER, ANOTHER-HEADER'
return response
You can also set other CORS heades here.
Then you should register your middleware by inserting it at the beggining of the MIDDLEWARE list in your project's settings.py file.
MIDDLEWARE = [
'myapp.middleware.CustomCorsMiddleware',
...
]
[expanding on what #vladimir said]
if you're using Django
and django-cors-headers to allow/control CORS,
you should set the following, in your settings.py
CORS_EXPOSE_HEADERS = ['yourCustomHeader']
I have the following problem:
In my Angular 4 App I want to get (GET Request) data from a public API.
Just calling the URL from Browser or via Postman works perfectly but when I make an HTTP Get request from Angular I get that error:
XMLHttpRequest cannot load https://api.kraken.com/0/public/AssetPairs.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'localhost:4200' is therefore not allowed
access.
I found solutions for that but none of them resolved my issue as it is a third party API and not under my control...
I also tried setting headers but it didn't work:
let headers = new Headers({ 'Access-Control-Allow-Origin': 'true' , 'Content-Type':'application/json', 'crossDomain':'true'});
let options = new RequestOptions({ headers: headers, withCredentials: false});
return this.http.get('https://api.kraken.com/0/public/AssetPairs' , options)
.map(
(response: Response) => {
console.log(response);
const markets = response.json();
return markets;
},
(error: Error) => {
console.log('error');
console.log(error);
}
);
Thanks in advance for advice!
I suggest you to use proxy-server. You can access your third part resources though proxy server. For instance you put nginx server and add cors configuration in the nginx.conf. Angular request direct to nginx which then reroute to your third part resources.
Yes, I know what you are thinking - yet another CORS question, but this time I'm stumped.
So to start off, the actual error message:
XMLHttpRequest cannot load http://localhost/Foo.API/token. 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'. Origin 'http://localhost:5000' is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
I'm not sure what is meant by credentials mode is 'include'?
So when I perform the request in postman, I experience no such error:
But when I access the same request through my angularjs web app, I am stumped by this error. Here is my angualrjs request/response. As you'll see the response is OK 200, but I still receive the CORS error:
Fiddler Request and Response:
The following image demonstrates the request and response from web front-end to API
So based on all the other posts I've read online, it seems like I'm doing the right thing, that's why I cannot understand the error. Lastly, here is the code I use within angualrjs (login factory):
CORS Implementation in API - Reference purposes:
Method 1 used:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
EnableCrossSiteRequests(config);
}
private static void EnableCrossSiteRequests(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*")
{
SupportsCredentials = true
};
config.EnableCors(cors);
}
}
Method 2 used:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
The issue stems from your Angular code:
When withCredentials is set to true, it is trying to send credentials or cookies along with the request. As that means another origin is potentially trying to do authenticated requests, the wildcard ("*") is not permitted as the "Access-Control-Allow-Origin" header.
You would have to explicitly respond with the origin that made the request in the "Access-Control-Allow-Origin" header to make this work.
I would recommend to explicitly whitelist the origins that you want to allow to make authenticated requests, because simply responding with the origin from the request means that any given website can make authenticated calls to your backend if the user happens to have a valid session.
I explain this stuff in this article I wrote a while back.
So you can either set withCredentials to false or implement an origin whitelist and respond to CORS requests with a valid origin whenever credentials are involved
If you are using CORS middleware and you want to send withCredentials boolean true, you can configure CORS like this:
var cors = require('cors');
app.use(cors({credentials: true, origin: 'http://localhost:5000'}));
Customizing CORS for Angular 5 and Spring Security (Cookie base solution)
On the Angular side required adding option flag withCredentials: true for Cookie transport:
constructor(public http: HttpClient) {
}
public get(url: string = ''): Observable<any> {
return this.http.get(url, { withCredentials: true });
}
On Java server-side required adding CorsConfigurationSource for configuration CORS policy:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
// This Origin header you can see that in Network tab
configuration.setAllowedOrigins(Arrays.asList("http:/url_1", "http:/url_2"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
configuration.setAllowedHeaders(Arrays.asList("content-type"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
Method configure(HttpSecurity http) by default will use corsConfigurationSource for http.cors()
If you're using .NET Core, you will have to .AllowCredentials() when configuring CORS in Startup.CS.
Inside of ConfigureServices
services.AddCors(o => {
o.AddPolicy("AllowSetOrigins", options =>
{
options.WithOrigins("https://localhost:xxxx");
options.AllowAnyHeader();
options.AllowAnyMethod();
options.AllowCredentials();
});
});
services.AddMvc();
Then inside of Configure:
app.UseCors("AllowSetOrigins");
app.UseMvc(routes =>
{
// Routing code here
});
For me, it was specifically just missing options.AllowCredentials() that caused the error you mentioned. As a side note in general for others having CORS issues as well, the order matters and AddCors() must be registered before AddMVC() inside of your Startup class.
If it helps, I was using centrifuge with my reactjs app,
and, after checking some comments below, I looked at the centrifuge.js library file, which in my version, had the following code snippet:
if ('withCredentials' in xhr) {
xhr.withCredentials = true;
}
After I removed these three lines, the app worked fine, as expected.
Hope it helps!
I have application where I getting code from stash raw file. Scrapping from public repositories is simple, it looks like this:
public getRawFile(rawLink: string) {
return this.http.get(rawLink).map((res: Response) => res.text());
}
But now I would like to get code from stash raw file, but from private repository. If user have access(is logged into stash) than source code from raw file is loaded.
If I trying same way, I getting respone:
XMLHttpRequest cannot load 'private_stash_file_link'. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
EXCEPTION: Response with status: 0 for URL: null
Uncaught Response with status: 0 for URL: null
How can I handle this, cookies, specific options for get request, is it even possible?
EDIT 1.
Tried:
public getRawFile(link: string) {
let headers = new Headers();
headers.append('Access-Control-Allow-Headers', 'Content-Type');
headers.append('Access-Control-Allow-Methods', 'GET, OPTIONS');
headers.append('Access-Control-Allow-Origin', '*');
let options = new RequestOptions({headers: headers, withCredentials: true});
return this.http.get(link, options).map((res: Response) => res.text());
}
but same result for private repository..
plunker
The server that you are making the request to has to implement CORS to grant JavaScript from your website access (Cross Origin Resource Sharing (CORS)). So if you have access to the place where you are scraping, then add the following HTTP headers at the response of the receiving end:
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *.example.com
Make sure to replace "*.example.com" with the domain name of the host that is sending the data/getting the data. Doing this should fix your problem.
My Vertx Server resides in server A and client resides in server B. When i tried to access vertx server, CORS error pops in. I added some server side code to handle CORS issue but it's not working. Do we need to add some header in client side. what am i missing here? Can anyone help
Vertx Server Side:
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("*")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowedHeader("Access-Control-Request-Method")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Headers")
.allowedHeader("Content-Type"));
Client implementation:
function(url, user) {
eventBus = new EventBus(url);
eventBus.onopen = function() {
//Do Something
}
}
Update:
I removed the withCredential attribute in header.Now my code looks like
if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close();
but still following error pops up. Can you help?
XMLHttpRequest cannot load https://192.168.1.20:7070/Notify/571/rn4nh0r4/xhr_send?t=1471592391921. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'https://login.com' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.
Update2:
After adding my client address in
.putHeader("Access-Control-Allow-Origin", "*")
I got following log:
XMLHttpRequest cannot LOAD https://192.168.1.20:7070/Notify/773/k3zq1z2z/xhr_send?t=1471601521206. Credentials flag IS 'true', but the 'Access-Control-Allow-Credentials' header IS ''. It must be 'true' TO allow credentials. Origin 'https://login.com' IS therefore NOT allowed access.
My code is as follows:
if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "https://login.com")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.putHeader("Access-Control-Allow-Credentials", "true")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close();
That's because order matters when defining routes.
Simply switch between your CORS and BodyHandler:
Router router = Router.router(vertx);
router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("*")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowedHeader("Access-Control-Request-Method")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Headers")
.allowedHeader("Content-Type"));
router.route().handler(BodyHandler.create());
I had the same problem and was finally able to solve it. You will need to provide a valid regex String to CorsHandler.create("Regex String Here"). So if you want to allow any protocol:host:port, aka "*", through CORS handling you can use.
router.route().handler(CorsHandler.create(".*.") //note the "." surrounding "*"
If you want a fine-grained control of allowed protocol:host:port, you have flexibility with the Regex String. Example: CORS handling for either http:// or https:// from localhost and any port will look like this:
router.route().handler(CorsHandler.create("((http://)|(https://))localhost\:\d+")
.allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST)
.allowedMethod(HttpMethod.OPTIONS)
.allowCredentials(true)
.allowedHeader("Access-Control-Allow-Method")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Content-Type")); //makes sure you add other headers expected in the request
To allow a list of specific clients only, you can concatenate with an OR operator "|" in your regex string.
router.route().handler(CorsHandler.create("http://localhost:8080" | "https://128.32.24.45:\\d+"))
The problem was solved by adding withCredential flag to true inside the router handler.
My code is as follows
router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("https://login.com")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowCredentials(true)
.allowedHeader("Access-Control-Allow-Method")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Content-Type"));
previously it was set only in the response header
if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "https://login.com")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.putHeader("Access-Control-Allow-Credentials", "true")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close();
I write an answer for this because non of this solutions helped due to another nature of my cors issue.
Issue:
In Chrome browser (no issues in Firefox) I got random cors errors. One part of the cors requests (pre-flight OPTIONS request) was always fine, but second one, no matter it is GET, POST etc randomly failed with standard browser's cors error (Access to fetch at '' from origin '' has been blocked by CORS policy: 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.)
I have separate domain names for api and frontend.
Scheme: AWS CloudFront -> S3 (static website hosting) -> ELB -> api (in ec2)
Solution:
First of all here is the final code for cors settings in vertx that is working:
private void initCors(final Router router) {
final Set<String> allowedHeaders = new HashSet<>();
allowedHeaders.add("x-requested-with");
allowedHeaders.add("Access-Control-Allow-Origin");
allowedHeaders.add("Access-Control-Allow-Methods");
allowedHeaders.add("Access-Control-Allow-Headers");
allowedHeaders.add("Access-Control-Allow-Credentials");
allowedHeaders.add("origin");
allowedHeaders.add("Content-Type");
allowedHeaders.add("accept");
allowedHeaders.add("X-PINGARUNER");
allowedHeaders.add("Authorization");
final Set<HttpMethod> allowedMethods = new HashSet<>();
allowedMethods.add(HttpMethod.GET);
allowedMethods.add(HttpMethod.POST);
allowedMethods.add(HttpMethod.OPTIONS);
allowedMethods.add(HttpMethod.DELETE);
allowedMethods.add(HttpMethod.PATCH);
allowedMethods.add(HttpMethod.PUT);
router.route().handler(CorsHandler.create(".*.")
.allowCredentials(true)
.allowedMethods(allowedMethods)
.allowedHeaders(allowedHeaders));
}
But as I've said it wasn't enough. Then I enabled a logging for all routes:
private void initApiLogging(Router router) {
router.route().handler(event -> {
String headers = "";
if (event.request().headers() != null
&& !event.request().headers().isEmpty()) {
headers = event.request().headers().entries().toString();
}
LOGGER.debug("{} request to {}, headers: {}",
event.request().method(),
event.request().absoluteURI(),
headers);
event.next();
});
}
router.route().failureHandler(this::exceptionHandler);
AND for the whole http server:
private HttpServer server;
server.exceptionHandler(ex -> {
LOGGER.error("Http server exception handler.", ex);
});
And then during the cors error I got error message: io.netty.handler.codec.TooLongFrameException: HTTP header is larger than 8192 bytes.
So increasing this value solved the issue.
HttpServerOptions options = new HttpServerOptions();
options.setMaxHeaderSize(1024 * 16);
server = vertx.createHttpServer(options);
NOTE: If you are using AWS S3 too in your environment, then don't forget to add cors configuration for it:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>https://you-domain-here.com</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Hope it was helpful!
If your client connects to server, just add your client address in
.putHeader("Access-Control-Allow-Origin", "*")
(instead of *)
Therefore you'll be able to send request withCredentials from your browser.