SOLVED-How do I fix error with access control in web.config file? - web-config

Im getting this error and I don´t know how to fix it.The site is live so therefor I don´t want to test a lot of stuff, breaking it wile testing.
I guess the problem is in my web.config file and that its related to the service worker that I use to cache files since that is using "fetch".
The error Im getting.
Fetch API cannot load https://www.google-analytics.com/j/collect?... due to access control checks.
[Error] Failed to load resource: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
And the web.config file looks like this.
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="public, max-age=365000000" />
<!--<add name="Access-Control-Allow-Origin" value="['https://mydomain.se','http://dibspayment.eu','https://checkout.dibspayment.eu','https://www.google-analytics.com']" />-->
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="'HEAD,OPTIONS, GET, POST, PUT, PATCH, DELETE'" />
<add name="Access-Control-Allow-Headers" value="'X-Requested-With, Origin, Content-Type, X-Auth-Token, Accept, Authorization, Content-Length,Access-Control-Allow-Origin, Access-Control-Allow-Methods,Cache-Control'" />
</customHeaders>
</httpProtocol>
My service worker looks like this.
self.addEventListener('install', function(event) {
self.skipWaiting()
event.waitUntil(
caches.open('v19').then(function(cache) {
return cache.addAll([
'/js/jquery.cookie.js',
'/js/jquery.sumoselect.min.js',
'/js/wookmark.min.js',
'/js/imagesloaded.pkgd.min.js',
'/js/exif/exif.min.js',
'/js/exif/load-image.min.js',
'/js/exif/load-image-scale.min.js',
'/js/exif/load-image-orientation.min.js',
'/fonts/Framework7Icons-Regular.woff2',
'/fonts/Framework7Icons-Regular.woff',
'/fonts/Framework7Icons-Regular.ttf',
'/fonts/Framework7Icons-Regular.eot',
]);
//caches.open(v2)
//.then( cache = cache.match('/js/v5/framework7.bundle.min.js'))
//.then( res =res.text())
//.then( js = console.log(js))
})
);
});
self.addEventListener('fetch', function(event) {
if (event.request.clone().method === 'GET') {
event.respondWith(
caches.open("v19").then(function (cache) {
return fetch(event.request).then(function (res) {
cache.put(event.request, res.clone());
return res;
})
})
)
} else if (event.request.clone().method === 'POST') {
// attempt to send request normally
event.respondWith(fetch(event.request.clone()).catch(function
(error) {
// only save post requests in browser, if an error occurs
//savePostRequests(event.request.clone().url, form_data)
}))
}
});
self.addEventListener('activate', function(event) {
var cacheKeeplist = ['v19'];
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheKeeplist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
);
});
How should I do with the Access-Control-Allow-Origin? I guess that´s where the problem is, or?
Any input really appreciated, thanks.
Solution:
Ok so I changed it to this so it is not caching google.analytis and the error went away.
self.addEventListener('fetch', function(event) {
if (( event.request.url.indexOf( 'analytics' ) !== -1 ) || ( event.request.url.indexOf( 'checkout' ) !== -1 )){
}else{
if (event.request.clone().method === 'GET') {
event.respondWith(
caches.open("v19").then(function (cache) {
return fetch(event.request).then(function (res) {
cache.put(event.request, res.clone());
return res;
})
})
)
} else if (event.request.clone().method === 'POST') {
// attempt to send request normally
event.respondWith(fetch(event.request.clone()).catch(function
(error) {
// only save post requests in browser, if an error occurs
//savePostRequests(event.request.clone().url, form_data)
}))
}
}
});

It's not the issue with yours web.config, but Google Analytics (GA) server's. So you have to adjust requests to meet GA requirements.
GA responses do not want to be cached (underlined in green). All transfer of statistics is done in the send request, the answer is only confirmation of delivery (the text like 1gfr).
GA do not accept requests with credentials (underlined in red) because of:
- presents of wildcard * in Access-Control-Allow-Origin response header
- absent of Access-Control-Allow-Credentials: true in responce header
Hence GA wait cross-origin requests with no credentials (no auth cookies should not be sent). The feth() uses mode: 'cors', credentials: 'same-origin' by default (send credentials only to same-origin requests), therefore all should be OK.
But if you still have got CORS error above, it means some browsers send credentials. Try to set Request.credentials to "omit" as recommended by Mozilla.
Or may be it's possible to exclude GA from caching and let process GA requests native way (GA natively use XMLHttpRequest with withCredentials = false option, not fetch()).

Related

Service workers "sync" operation is working while its offline?

I have a PWA project where I send the data to server. During this process, if the user is offline then the data is stored in indexedDb and a sync tag is registered. So, then when the user comes online that data can sent to the server.
But In my case the sync event gets executed immediately when the we register a sync event tag, which means the data is tried to be sent to server while its offline, which is not going to work.
I think the sync event supposed to fire while its online only, what could be issue here ?
The service worker's sync event works accordingly when I tried to enable and disable the offline option of chrome devtools, and also works correctly in my android phone.
This is how I register my sync tag
function onFailure() {
var form = document.querySelector("form");
//Register the sync on post form error
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready
.then(function (sw) {
var post = {
datetime1: form.datetime1.value,
datetime: form.datetime.value,
name: form.name.value,
image: form.url.value,
message: form.comment.value
};
writeData('sync-comments', post)
.then(function () {
return sw.sync.register('sync-new-comment');
})
.then(function () {
console.log("[Sync tag registered]");
})
.catch(function (err) {
console.log(err);
});
});
}
}
And this is how the sync event is called
self.addEventListener('sync', function (event) {
console.log("[Service worker] Sync new comment", event);
if (event.tag === 'sync-new-comment') {
event.waitUntil(
readAllData('sync-comments')
.then(function (data) {
setTimeout(() => {
data.forEach(async (dt) => {
const url = "/api/post_data/post_new_comment";
const parameters = {
method: 'POST',
headers: {
'Content-Type': "application/json",
'Accept': 'application/json'
},
body: JSON.stringify({
datetime: dt.datetime,
name: dt.name,
url: dt.image,
comment: dt.message,
datetime1: dt.datetime1,
})
};
fetch(url, parameters)
.then((res) => {
return res.json();
})
.then(response => {
if (response && response.datetimeid) deleteItemFromData('sync-comments', response.datetimeid);
}).catch((error) => {
console.log('[error post message]', error.message);
})
})
}, 5000);
})
);
}
});
you mention
The service worker's sync event works accordingly when I tried to enable and disable the offline option of chrome devtools, and also works correctly in my android phone.
So I'm not sure which case is the one failing.
You are right that the sync will be triggered when the browser thinks the user is online, if the browser detects that the user is online at the time of the sync registration it will trigger the sync:
In true extensible web style, this is a low level feature that gives you the freedom to do what you need. You ask for an event to be fired when the user has connectivity, which is immediate if the user already has connectivity. Then, you listen for that event and do whatever you need to do.
Also, from the workbox documentation
Browsers that support the BackgroundSync API will automatically replay failed requests on your behalf at an interval managed by the browser, likely using exponential backoff between replay attempts.

How to manage contacts using Contacts API in our website correctly?

I was trying to integrate Google Contacts API to manage the contacts in my website.
I've done the following things:
I've created an application in google developer console and added http://localhost:4200 as URIs & Authorized redirect URIs.
Enabled 'Contacts API'.
I've added the following in my index.html (I've replaced {clientID} with my original client ID (of course):
<script>
function loadAuthClient() {
gapi.load('auth2', function() {
gapi.auth2.init({
client_id: '{clientID}'
}).then(() => {
console.log("success");
}).catch(err => {
console.log(err);
});
});
}
</script>
<script src="https://apis.google.com/js/platform.js?onload=loadAuthClient" async defer></script>
<meta name="google-signin-client_id" content="{clientID}">
Signed in successfully using:
gapi.auth2.getAuthInstance().signIn().then(() => {
console.log("Logged in")
}).catch(err => {
console.log(err);
});
Tried fetching the contacts using the following:
var user = gapi.auth2.getAuthInstance().currentUser.get();
var idToken = user.getAuthResponse().id_token;
var endpoint = `https://www.google.com/m8/feeds/contacts/`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken));
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
But I'm getting the error:
Access to XMLHttpRequest at 'https://www.google.com/m8/feeds/contacts/?access_token={I removed the access token}' from origin 'http://localhost:4200' 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.
Can someone please guide me where I'm going wrong?
My original response was off the mark. The actual answer is much simpler.
In step 4, try changing your endpoint:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
In my local tests, this resulted in the expected response.
Another suggestion is to add alt=json to your query, so that you get easy to parse JSON payload. Otherwise you'll get a nasty XML payload in the response.
Here's the updated step 4 with these changes:
var endpoint = `https://www.google.com/m8/feeds/contacts/default/full`;
var xhr = new XMLHttpRequest();
xhr.open('GET', endpoint + '?access_token=' + encodeURIComponent(idToken) + '&alt=json');
xhr.setRequestHeader("Gdata-Version", "3.0");
xhr.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
window.alert(xhr.responseText);
}
};
xhr.send();
Here's my original response, just in case it helps someone else.
I suspect that you'll need to add http://localhost:4200 to your list of "Authorized JavaScript origins" for the OAuth Client that you are using.
Edit your OAuth 2.0 Client ID and add the URI to the Javascript origins as below:
The other section on that page, Authorized Redirect URIs, only permits the OAuth flow to be redirected back to your web app. Often your web app server will actually consume the APIs so Google doesn't automatically permit CORS access to these APIs to keep things secure.

X-XSRF-TOKEN header with axios

Do I have to set anything to send X-XSRF-TOKEN header if I set a XSRF-TOKEN cookie server side?
https://github.com/axios/axios/blob/master/lib/defaults.js#L74
https://github.com/axios/axios/blob/master/dist/axios.js#L1072
It reads like I don't, but I'm not seeing one go out.
I'll add that I have set withCredentials to true, so I do meet the first check in the OR:
var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
so if config.xsrfCookieName is a default.....
Update:
So, my OPTIONS preflight CORS is working, as is the POST now, but no X-XSRF-TOKEN being sent.
methods: {
onSubmit(e) {
this.axios
.post(
e.target.action,
{ data: this.form },
{
withCredentials: true,
xsrfCookieName: "XSRF-TOKEN",
xsrfHeaderName: "X-XSRF-TOKEN"
}
)
.then(res => {
console.log(res)
})
.catch(err => {
this.errors.push(err)
})
}
}
Thanks.
I had the same issue and was about the "secure" flag on the cookie as can be seen on the cookies tab of the request, but is not showing on the cookies under "application" tab:
In my case, I had to ask the backend to set it down.
This happens because, as secure, you cannot access to it via javascript.
document.cookie // is empty

Angular2-Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource

I am calling a http post in Angular 2. This is working fine in post man but when I implement this API call in Angular 2 I get No 'Access-Control-Allow' error. Here is my code
getInspections(): Observable<IInspection[]> {
if (!this.inspections) {
let body =JSON.stringify({"Statuses":["Submitted", "Opened"]});
let headers = new Headers({ 'Content-Type': 'application/json' });
headers.append('Access-Control-Allow-Origin','*');
let options = new RequestOptions({ headers: headers });
return this.http.post(this._baseUrl + '/api/Inspect/ListI',body,options)
.map((res: Response) => {
this.inspections = res.json();
return this.inspections;
})
.catch(this.handleError);
}
else {
//return cached data
return this.createObservable(this.inspections);
}
}
Or can I do this? Just pass header instead of options
getInspections(): Observable<IInspection[]> {
if (!this.inspections) {
let body =JSON.stringify({"Statuses":["Submitted", "Opened"]});
let headers = new Headers({ 'Content-Type': 'application/json' });
//headers.append('Access-Control-Allow-Origin','*');
// let options = new RequestOptions({ headers:headers });
return this.http.post(this._baseUrl + '/api/Inspect/ListI',body,headers)
.map((res: Response) => {
this.inspections = res.json();
return this.inspections;
})
.catch(this.handleError);
}
else {
//return cached data
return this.createObservable(this.inspections);
}
}
CORS headers like
headers.append('Access-Control-Allow-Origin','*');
need to be provided by the server. Adding them on the client is pointless.
When using non-standard headers (json is apparently considered non-standard) then a pre-flight check is carried out to ask if the requested action (in this case 'post') can be carried out. Only the server can respond with the permissive headers. How you respond does depend on your server language. In my webapi2 I implement cors in the WebAppConfig
var cors = new EnableCorsAttribute("http://localhost:3000", "*", "GET, HEAD, OPTIONS, POST, PUT");
cors.SupportsCredentials = true;
config.EnableCors(cors);
Note for a live server you would replace the localhost ref with a web configed list ( or specific location where the caller resides). The SupportsCredentials is only needed if you are using authentication.
To handle the pre-flight I added a method to Globals.asax which just intercepts pre-flight messages and returns enough data for the post to move ahead.
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
var origin = HttpContext.Current.Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "content-type, withcredentials, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT");
Response.Flush();
}
}
Note here that I am cheating somewhat by reflecting the origin back - this is not safe in a production environment and should list the specific servers otherwise you are being too loose with security.
Be aware that there are some dev cheats. - If you run on internet explorer on localhost (for dev purposes) then ie ignores the port which most other browsers do not so making things easier. There is also a CORS enhancement for Chrome which adds the headers for you. Finally you will see a lot of code that uses '*' returns (to permit all) - by all means use them to get the code working but before release lock these down far more aggressively.

Passport and SailsJS, how to get isAuthenticated() test to pass

My code keeps failing here when the user tries to login:
isAuthenticated: function (req, res) {
if (req.isAuthenticated()) { return res.json(req.user); }
else { return res.send(401); }
},
It FAILS and I get GET http://localhost:1337/user/authenticated 401 (Unauthorized) in the console, even though the user has entered in a correct email and password.
Where in the code makes that test pass?
I have the related StackOverflow question with more info HERE.
The problem was that my frontend application has a different origin than my backend application, so the AJAX requests will not include the session cookie and req.isAuthenticated() will never return true.
Use the withCredentials options to force it.
$http({ withCredentials: true, ... })