AngularJS $http, CORS and Basic authentication with Jersey - angularjs

I am learning on AngularJS. I have created the sample project with CORS with Basic Authentication. My server side code is Jersey Rest. But Still I am getting 403 Forbidden Error. I don't understand. Am I missing something. My code is given below. Please help me to solve it.
ServerCode--
/*
* This is the controller level Code to get all UserlogInHistoryAngular
* information
*/
#GET
#Path("/getall")
#Produces({
MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON
})
public Response getAllUserLogInHistoryAngular() {
log.info("Controller layer for Get All UserlogInHistory");
UserLoginHistoryService userLogInHistoryService = new UserLoginHistoryService();
GenericEntity < List < UserLogInHistory >> userLoginHistory = new GenericEntity < List < UserLogInHistory >> (
userLogInHistoryService.getAllUserlogInHisotry()) {};
return Response
.status(200)
.entity(userLoginHistory)
.header("Access-Control-Allow-Credentials", true)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods",
"GET, POST, DELETE, PUT, OPTIONS")
.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name")
.allow("OPTIONS").build();
}
}
AngularJS Code--
var myapp = angular
.module('myapp', ['angularUtils.directives.dirPagination']);
myapp.config(function($httpProvider) {
//Enable cross domain calls
$httpProvider.defaults.withCredentials = true;
$httpProvider.defaults.useXDomain = true;
});
myapp.controller('customerController', function($scope, $http) {
var encodingstring = window.btoa("numery" + ":" + "password");
console.log(encodingstring);
$http({
withCredentials: true,
headers: {
'Authorization': 'Basic ' + encodingstring,
'Content-Type': 'application/json; charset=utf-8'
},
method: "GET",
url: "http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall"
}).then(function(response) {
$scope.lstUser = response.data;
//$log.info(response);
console.log(response.data);
console.log($scope.lstUser);
})
$scope.sortColumn = "name";
$scope.reverseSort = false;
$scope.sortData = function(column) {
$scope.reverseSort = ($scope.sortColumn == column) ? !$scope.reverseSort : false;
$scope.sortColumn = column;
};
$scope.getSortColumn = function(column) {
if ($scope.sortColumn == column) {
return $scope.reverseSort ? 'arrow-down' : 'arrow-up';
}
return '';
};
function getSelectedIndex(id) {
for (var i = 0; i < $scope.listCustomers.length; i++)
if ($scope.listCustomers[i].id == id)
return i
return -1;
};
Error--
angular.js:13018 OPTIONS http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall 403 (Forbidden)
(anonymous) # angular.js:13018
sendReq # angular.js:12744
serverRequest # angular.js:12485
processQueue # angular.js:17396
(anonymous) # angular.js:17444
$digest # angular.js:18557
$apply # angular.js:18945
bootstrapApply # angular.js:1939
invoke # angular.js:5108
doBootstrap # angular.js:1937
bootstrap # angular.js:1957
angularInit # angular.js:1842
(anonymous) # angular.js:35431
trigger # angular.js:3491
(index):1 Failed to load http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall: 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:8081' is therefore not allowed access. The response had HTTP status code 403.
angular.js:15018 Possibly unhandled rejection: {"data":null,"status":-1,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","withCredentials":true,"headers":{"Authorization":"Basic bnVtZXJ5OnBhc3N3b3Jk","Accept":"application/json, text/plain, */*"},"url":"http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall"},"statusText":"","xhrStatus":"error"}
(anonymous) # angular.js:15018
(anonymous) # angular.js:11302
processChecks # angular.js:17428
$digest # angular.js:18557
$apply # angular.js:18945
done # angular.js:12799
completeRequest # angular.js:13056
requestError # angular.js:12972
error (async)
(anonymous) # angular.js:12985
sendReq # angular.js:12744
serverRequest # angular.js:12485
processQueue # angular.js:17396
(anonymous) # angular.js:17444
$digest # angular.js:18557
$apply # angular.js:18945
bootstrapApply # angular.js:1939
invoke # angular.js:5108
doBootstrap # angular.js:1937
bootstrap # angular.js:1957
angularInit # angular.js:1842
(anonymous) # angular.js:35431
trigger # angular.js:3491
VM2032:185 [CodeLive] HTTP detected: Connecting using WS
VM2032:109 [CodeLive] Connected to CodeLive at ws://127.0.0.1:42529
bundle.js:10 license url https://www.genuitec.com/go/webclipse-buy-now

So here's what's going on. The CORS Preflight request is an OPTIONS request. This request happens prior to the real request. It checks with the server to see if the request is allowed. In response to the preflight request, the server should send back the Access-Control-X-X headers like you are in your getAllUserLogInHistoryAngular method.
So what is happening in your case during the preflight, is that it hits the Basic Auth filter and automatically gets rejected without adding the CORS response headers. Your putting the CORS response headers in the resource method does nothing and is useless. Generally the CORS should be handled in a filter so it handles all requests prior to hitting the resource methods.
Here's what you should do. Just like you did for Basic Auth, use a ContainerRequestFilter to handle the CORS. You want this filter to be called before the Auth filter because the CORS preflight doesn't care about authentication and it will not send the authentication credentials with the request. In this filter, you should check to see if it is a CORS preflight request. Generally this can be done by checking for an OPTIONS method and he presence of the Origin header. If it is a preflight, then abort the request and add the CORS headers in a ContainerResponseFilter. It might look something like
#Provider
#PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request) throws IOException {
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
if (request.getHeaderString("Origin") == null) {
return;
}
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, " +
"Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
}
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Notice it's a #PreMatching filter, which will cause it to be called before your Auth filter (assuming that filter is not also a pre-matching filter, in which case you should also use the #Priority annotation).
What this filter does is check to see if the request is a preflight request, and if it is, it aborts the entire request with a 200, which causes the request to skip the resource method, skip all other request filters after it, and jump to the response filter. In the response filter, we check to see if it is preflight by checking a property we set earlier. If it is, then we set the CORS response headers. If you want learn more about CORS and more about this specific filter implementation, check out this post.
So no here's the flow of what will happen
Client Browser Server
------ ------- ------
Request endpoint -------> Remembers request -------> Sends back CORS
But first sends response headers
CORS preflight from CorsFilter
|
Grabs original <----------------+
request, sends it
|
+----------------> Skips CorsFilter,
Goes to Auth filter,
Goes to resource
Sends resource response
|
Handle response <------- Receives response <----------------+
Gives it to client

Related

DELETE request's preflight fails on Firefox for Quarkus

I'm getting a very strange error which I'm having a very hard time understanding why it's occurring.
Setup
I have a Quarkus backend which serves the following endpoints:
#Path("/rest/manager")
#ApplicationScoped
public class ManagerController {
...
#GET
#Path("/")
public Response getManagerList() {
...
}
#POST
#Path("/")
public Response createManager(#Valid ManagerView managerView) {
...
}
#GET
#Path("/{userId}")
public Response getManager(#Parameter(required = true) #PathParam("userId") String userId) {
...
}
#DELETE
#Path("/{userId}")
public Response deleteManager(#Parameter(required = true) #PathParam("userId") String userId) {
...
}
#PUT
#Path("/{userId}")
public Response updateManager(#PathParam("userId") String userId, #Valid ManagerView managerView) {
...
}
}
Quarkus application.properties looks like this:
quarkus.http.cors=true
quarkus.http.cors.origins=http://localhost:3000
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
My frontend runs on localhost:3000 and backend on localhost:8080.
Frontend is in React, and i'm using axios to make the call like this:
.post<ManagerView>(`/manager`, managerToManagerView(data.manager), {
headers: {
Authorization: `Bearer ${data.accessToken}`,
'Content-Type': 'application/json',
},
})
...
.delete<boolean>(`/manager/${userId}`, {
headers: {
Authorization: `Bearer ${data.accessToken}`,
},
})
The Error
In Firefox, I get the following error when i make a Delete request.
XHR OPTIONS http://localhost:8080/rest/manager/auth0|63de17288141500bc6ac65e0
CORS Missing Allow Origin
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rest/manager/auth0|63de17288141500bc6ac65e0. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 400.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rest/manager/auth0|63de17288141500bc6ac65e0. (Reason: CORS request did not succeed). Status code: (null).
Here's a screenshot from the Browser's devtools:
As you can see, just before the DELETE request, there is a POST request and the preflight for that is successful.
This is the details of the preflight which fails (the one before DELETE):
Also for comparison, here is the preflight request which doesn't fail (The one before POST)
This error only happens on DELETE or PUT. Other methods (POST and GET) are working fine.
This error only happens on Firefox, not on Chrome or Edge
Another interesting detail might be that on Chrome it doesn't work if I don't supply the Authorization header
I have tried various CORS settings for Quarkus however nothing works.

Jeresy CORS filter working but React rest GET failing still with header ‘access-control-allow-origin’ is not allowed

I create a rest react front end to talk to a Jersey servlet on tomcat on the back end for RH 8.6. When react tried to do on REST GET or POST commands I got the "‘access-control-allow-origin’ is not allowed according to header" error. So I then added the CORS filter which was suppose to fix the origin problem, but the react client is still failing. I have tried different filters but there is no change. I assume the problem is in the react GET fetch but it looks ok with me and gets a header back when mode: 'no-cors' is set. In the debugger the CORSFilter class gets the GET, but it does not reach the resource class endpoint so its getting rejected.
Using postman I have verified the CORSFilter is inserting the values in the response as you can see here.
POST http://localhost:8080/rtc-servlet/mcd/location
Headers from postman tool:
Status Code: 200
access-control-allow-credentials: true
access-control-allow-headers: X-Requested-With, CSRF-Token, X-Requested-By, Authorization, Content-Type
access-control-allow-methods: API, GET, POST, PUT, DELETE, OPTIONS, HEAD
access-control-allow-origin: *
access-control-max-age: 151200
connection: keep-alive
content-length: 701
content-type: application/json
date: Sat, 10 Dec 2022 02:52:19 GMT
keep-alive: timeout=20
servlet code:
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
// *(allow from all servers) OR https://crunchify.com/
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
// As part of the response to a request, which HTTP headers can be used during the actual request.
responseContext.getHeaders().add("Access-Control-Allow-Headers",
"X-Requested-With, CSRF-Token, X-Requested-By, Authorization, Content-Type");
Also tried these options:
"Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add("Access-Control-Allow-Methods",
"API, GET, POST, PUT, DELETE, OPTIONS, HEAD");
// How long the results of a request can be cached in a result cache.
responseContext.getHeaders().add("Access-Control-Max-Age", "151200");
}
}
#GET // read in updated/original files
#Produces(MediaType.APPLICATION_JSON) // what format we send back
public JsonObject getLocationValues() {
System.out.println("Called location getLocationValues ");
return locationRepository.readConfigFile(false);
}
React Rest GET fetch:
const urll1 = "http://localhost:8080/rtc-servlet/mcd/location";
useEffect(() => {
const fetchPost = async () => {
await fetch(urll1, {
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
"Accept": "application/json",
"Access-Control-Allow-Origin": "*",
},
})
.then((response) => {
if (response.ok) {
response.json().then(data => {
console.log("response fetchPost :" + JSON.stringify(data));
setPosts1(data);
});
} else {
console.log("response was not ok");
}
})
.catch((err) => {
console.log(err.message);
});
};
fetchPost();
}, []);
The console error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rtc-servlet/mcd/location. (Reason: header ‘access-control-allow-origin’ is not allowed according to header ‘Access-Control-Allow-Headers’ from CORS preflight response).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/rtc-servlet/mcd/location. (Reason: CORS request did not succeed). Status code: (null).
NetworkError when attempting to fetch resource.
So does anyone see that I am doing wrong?
After read the CORS not working posts in stackoverflow again I came across a commit about getting the origin from the header and then setting Access-Control-Allow-Origin to it vs. "*" and react on port localhost:3000 started to get responses back from the localhost:8080 servlet (origin is being set to "localhost:3000"). This was the forum string if you want to read up on it:
How to enable Cross domain requests on JAX-RS web services?.
So the change in the filter class is as follows:
String origin = requestContext.getHeaderString("origin");
if ((origin != null) && (!origin.isEmpty())) {
headers.add("Access-Control-Allow-Origin", origin);
} else {
// *(allow from all servers) OR https://crunchify.com/
headers.add("Access-Control-Allow-Origin", "*");
}
headers.add("Access-Control-Allow-Credentials", "true");
and in the js script "Access-Control-Allow-Origin": "*" was deleted:
await fetch(urll1, {
headers: {
'Content-Type': 'application/json',
"Accept": "application/json"
},
})
I am not sure if I now need the else since "*" didn't work for me, but I left it in. If its not needed or I am just doing something that sort of works because I am using firefox please let me know.

ERR_EMPTY_RESPONSE error in authorization Laravel

I have a web service with Laravel that solved the CORS Origin problem, but the next problem is that requests that have authorization headers return the following error server.
OPTIONS https://sandbox.example.com / api / v1 / user / net profile :: ERR_EMPTY_RESPONSE
I'm currently using cloudflare and wanted to know if this is a CDN or something else on the server.
This is preflight requests.
You need allow OPTIONS request. In first you need create CORS middleware
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
public function handle($request, Closure $next)
{
$headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers'=> 'X-Requested-With, Content-Type, Accept, Origin, Authorization',
'Access-Control-Allow-Origin' => '*'
];
if($request->getMethod() === 'OPTIONS') {
// The client-side application can set only headers allowed in Access-Control-Allow-Headers
return \response('', 200, $headers);
}
$response = $next($request);
foreach($headers as $key => $value)
$response->header($key, $value);
return $response;
}
}
then add in Http/Kernel.php in array $middleware:
protected $middleware = [
// other middlewares
Cors::class
];
After it all requests with type OPTIONS will return response 200 with headers.

CORS Authorization polymer and goapp golang

I have polymer frontend which interact with goapp server. Everything works fine as long as I do not pass authorization token in header. Here is the code at Polymer side
<iron-ajax
auto
url="http://localhost:8080/ephomenotes"
handle-as="json"
last-response="{{response}}"
headers="[[_computeHeader()]]"
debounce-duration="300"></iron-ajax>
_computeHeader() {
var token = localStorage.getItem("savedToken");
var obj = {};
obj.Authorization = "Bearer " + token;
return obj;
//return {"Authorization": "Bearer " + token};
}
At golang server side
w.Header().Set("Access-Control-Allow-Credentials", "true")
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if r.Method == "OPTIONS" {
return
}
Please note is I remove headers="[[_computeHeader()]]" from polymer code then it works..However with Authorization token it throws following error.
XMLHttpRequest cannot load http://localhost:8080/ephomenotes. 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:8081' is therefore not allowed
access.
Please help
Resolved the issue ..
created new route for options
r.OPTIONS("/ephomenotes", optionsheader)
r.GET("/ephomenotes", env.EPHomePage)
This is the new function.
func optionsheader(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Header().Set("Access-Control-Allow-Credentials", "true")
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
// w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
}
However I am not sure, why this one worked?

ASP.NET WebAPI2 CORS: null request in GetOwinContext on preflight

I'm creating an AngularJS (Typescript) SPA with a WebAPI2 backend, requiring authentication and authorization from the API. The API is hosted on a different server, so I'm using CORS, mainly following the guidance found at http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En as I'm a newcomer in this field.
All works fine, I can register and login, and then make requests to restricted-access controller actions (here the dummy "values" controller from the default VS WebAPI 2 template) by passing the received access token, in a client-side service with this relevant code:
private buildHeaders() {
if (this.settings.token) {
return { "Authorization": "Bearer " + this.settings.token };
}
return undefined;
}
public getValues(): ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/values",
method: "GET",
headers: this.buildHeaders(),
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + " " +
data.Message + ": " +
data.ExceptionMessage);
});
return deferred.promise;
}
Now, I'd like to retrieve the user's roles once logged in so that the AngularJS app can behave accordingly. Thus I added this method in my account API (which at the class level has attributes [Authorize], [RoutePrefix("api/Account")], [EnableCors(origins: "*", headers: "*", methods: "*")] (* are for testing purposes):
[Route("UserRoles")]
public string[] GetUserRoles()
{
return UserManager.GetRoles(User.Identity.GetUserId()).ToArray();
}
I then added this code to my login controller:
private loadUserRoles() {
this.accountService.getUserRoles()
.then((data: string[]) => {
// store roles in an app-settings service
this.settings.roles = data;
}, (reason) => {
this.settings.roles = [];
});
}
public login() {
if ((!this.$scope.name) || (!this.$scope.password)) return;
this.accountService.loginUser(this.$scope.name,
this.$scope.password)
.then((data: ILoginResponseModel) => {
this.settings.token = data.access_token;
// LOAD ROLES HERE
this.loadUserRoles();
}, (reason) => {
this.settings.token = null;
this.settings.roles = [];
});
}
where the account controller's method is:
public getUserRoles() : ng.IPromise<string[]> {
var deferred = this.$q.defer();
this.$http({
url: this.config.rootUrl + "api/account/userroles",
method: "GET",
headers: this.buildHeaders()
}).success((data: string[]) => {
deferred.resolve(data);
}).error((data: any, status: any) => {
deferred.reject(status.toString() + ": " +
data.error + ": " +
data.error_description);
});
return deferred.promise;
}
Anyway this triggers an OPTIONS preflight request, which in turn causes a 500 error. If I inspect the response, I can see that the GetOwinContext method gets a null request. Here is the beginning of the error stack trace:
{"message":"An error has occurred.","exceptionMessage":"Value cannot be null.\r\nParameter name: request","exceptionType":"System.ArgumentNullException","stackTrace":" at System.Net.Http.OwinHttpRequestMessageExtensions.GetOwinContext(HttpRequestMessage request)\r\n at Accounts.Web.Controllers.AccountController.get_UserManager() ...}
Yet, the code I'm using for GETting the roles is no different from that I use for GETting the dummy "values" from the WebAPI test controller. I can't exactly see the reason why a preflight should be required here, but in any case I'm getting this nasty exception in OWIN code.
My request header is (the API being at port 49592):
OPTIONS /api/account/userroles HTTP/1.1
Host: localhost:49592
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:64036
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:64036/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,it;q=-5.4
Could anyone explain?
I think I found some sort of working solution even if it looks somewhat dirty, but at least it works. I'm posting it here so other can eventually take advantage of it, but I'm open to suggestions. (Sorry for the bad formatting, but I tried several times and the editor does not allow me to correctly mark the code).
Essentially, the solution was suggested by the answer to this post: Handling CORS Preflight requests to ASP.NET MVC actions, but I changed the code which did not work for me (WebAPI 2 and .NET 4.5.1). Here is it:
in Global.asax, method Application_Start, add BeginRequest += Application_BeginRequest;.
add the override, which simply responds to OPTIONS requests by allowing everything (this is OK in my testing environment):
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Request.Headers.AllKeys.Contains("Origin")) &&
(Request.HttpMethod == "OPTIONS"))
{
Response.StatusCode = 200;
Response.Headers.Add("Access-Control-Allow-Origin", "*");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
string sRequestedHeaders = String.Join(", ",
Request.Headers.GetValues("Access-Control-Request-Headers") ?? new string[0]);
if (!String.IsNullOrEmpty(sRequestedHeaders))
Response.Headers.Add("Access-Control-Allow-Headers", sRequestedHeaders);
Response.End();
}
}
the attribute decorating the accounts controller method is just the RouteAttribute:
[Route("UserRoles")]
public string[] GetUserRoles()
{
string id = User.Identity.GetUserId();
Debug.Assert(id != null);
string[] aRoles = UserManager.GetRoles(id).ToArray();
return aRoles;
}
This way the OPTIONS request gets a proper response and the successive GET succeeds.
ADDITION
I must also add that the EnableCors attribute is not enough as we must not only handle the OPTIONS verb, but also ensure that any CORS request gets the Access-Control-Allow-Origin header. Otherwise, you might observe an apparently correct response (code 200 etc) but see the $http call failing. In my case I add to global.asax this line:
GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsAllowOriginHandler());
My CorsAllowOriginHandler is a DelegatingHandler which simply ensures that this header with value * is present in each response where the request included a Origin header:
public sealed class CorsAllowOriginHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// all CORS-related headers must contain the Access-Control-Allow-Origin header,
// or the request will fail. The value may echo the Origin request header,
// or just be `*`.
if ((request.Headers.Any(h => h.Key == "Origin")) &&
(response.Headers.All(h => h.Key != "Access-Control-Allow-Origin")))
{
response.Headers.Add("Access-Control-Allow-Origin", "*");
}
return response;
}
}

Resources