ApplicationInsights-JS icon indicating copy to clipboard operation
ApplicationInsights-JS copied to clipboard

AppInsights and third-party script CORS problem

Open nbelyh opened this issue 3 years ago • 2 comments

Not quite sure if it is a bug, more like a question, but maybe a bug..

I have application insights enabled on my app website, and then have a third-party script loaded that communicates with its own website.

The problem is that app insights instruments the global XMLHttpRequest object, and when that script tries to fetch data from its own site it gets CORS error because it's sent actually from app insights (different origin). This is my understanding of the situation at least, the details are below.

How can I workaround the issue? Can I tell app insights not to instrument the XMLHttpRequest? (I don't really need it)

To clarify the problem:

<body>
...
<script>
// this instrumetns XMLHttpRequest
const appInsights = new ApplicationInsights({
  config: {
    instrumentationKey: SOM_APPINSIGHTS_INSTRUMENTATIONKEY
  }
});
</script>
</body>

// this fails because XMLHttpRequest is instrumented (CORS error)
<script><script type="text/javascript" src="https://js.hs-scripts.com/XXX.js"></script>
</body>

The Error (in the latest Chrome browser):

Access to XMLHttpRequest at 
'https://api.hubspot.com/livechat-public/v1/message/public?portalId=XXXXXXXX' 
from origin 'http://localhost:3333' has been blocked by CORS policy: 
Request header field traceparent is not allowed by Access-Control-Allow-Headers in preflight response.

(anonymous) @ InstrumentHooks.js:97       <<<<<< this is app insights code
f @ fetchWidgetData.js:78                 <<< this is third-party javascript
loadWidget @ WidgetShell.js:498
(anonymous) @ throttle.js:21
start @ WidgetShell.js:581
v @ startOnceReady.js:55
I @ startOnceReady.js:96
(anonymous) @ startOnceReady.js:111
captureErrors @ ErrorLogger.js:119
b @ startOnceReady.js:110
(anonymous) @ start.js:18
s @ bootstrap:19
(anonymous) @ bootstrap:97
(anonymous) @ conversations-embed.js:1

nbelyh avatar Sep 20 '22 18:09 nbelyh

We have several mechanisms to handle this (If I'm following correctly), If I assume that the CORS error is coming from the additional headers (eg. traceparent) that is added as part of the then you can "block" specific domains by using either of the following 2 config options correlationHeaderExcludedDomains and correlationHeaderExcludePatterns (Added in v2.5.6 )

There is not a lot of concise documentation for these, so I'm going to have to provide some code references https://github.com/microsoft/ApplicationInsights-JS/blob/master/shared/AppInsightsCommon/src/Util.ts#L307-L314 correlationHeaderExcludePatterns: https://github.com/microsoft/ApplicationInsights-JS/blob/master/shared/AppInsightsCommon/src/Util.ts#L342-L348

Test coverage usage:

  • https://github.com/microsoft/ApplicationInsights-JS/blob/master/shared/AppInsightsCommon/Tests/Unit/src/Util.tests.ts#L190-L194
  • https://github.com/microsoft/ApplicationInsights-JS/blob/master/extensions/applicationinsights-dependencies-js/Tests/Unit/src/ajax.tests.ts#L2277-L2290

And if those 2 config options don't work the latest version has some additional callback functions that you can register so that you can intercept

  • https://github.com/microsoft/ApplicationInsights-JS#dependency-listeners
  • https://github.com/microsoft/ApplicationInsights-JS/blob/master/API-reference.md#adddependencylistener
  • https://github.com/microsoft/ApplicationInsights-JS/blob/master/API-reference.md#adddependencyinitializer

MSNev avatar Sep 20 '22 19:09 MSNev

@MSNev Thanks, I'll try that. The problem is not that something is passed/not passed, the problem is the instrumentation itself because the script that does it (app insights, that is) "lives" in another domain; so the network call to API happens from script in another domain, and that's what CORS error is about (as far as I understand). But maybe I'm missing something, I'll try it with exclude patters.

nbelyh avatar Sep 20 '22 19:09 nbelyh

FYI: @MSNev The above did not work; the problem was XMLHttpRequesta ctually intercepted by the appInsights. If XMLHttpRequest is intercepted (by anything), that causes the above CORS error in case there is a strict CORS applied by a third-party that hosts the script. I worked around that for now by setting disableAjaxTracking = true.

The "correlationHeaderExclude" thing deals with excluding some headers from telemetry; this is not the problem in the above case at all. The problem is interception of the XMLHttpRequest itself by a script originating from a different origin. I'm not sure if this can really be worked around in any way beside turning the interception off...

nbelyh avatar Oct 04 '22 19:10 nbelyh

Hmm, I've not heard of that before...

This

from origin 'http://localhost:3333' has been blocked by CORS policy: 
Request header field traceparent is not allowed by Access-Control-Allow-Headers in preflight response.

is a response from the server and the "presence" of the traceparent header on the request.... So that is not caused by the code being intercepted...

There is another config correlationIdCanIncludeCorrelationHeader that you can set to stop the traceparent from being added to "ALL" requests (but the ajax request will still cause a RemoteDepenency event to be sent after the request)...

And also correlationHeaderExcludedDomains which is used to block specific domains (rather than regex pattern) https://github.com/microsoft/ApplicationInsights-JS/blob/master/shared/AppInsightsCommon/src/Util.ts#L374-L384

MSNev avatar Oct 04 '22 20:10 MSNev

@MSNev Thank you. I've already almost lost hope, thus just disabled. Will give it another try, maybe still was doing something wrong.

nbelyh avatar Oct 04 '22 20:10 nbelyh

Thank you once again @MSNev. I was indeed excluding wrong domain (that script made cross-domain requests as well). You were completely right, the problem was exactly with the tracepart header. It looks like I just made up these absurd "diagnosis", pls forget that nonsense regarding instrumentation 🤦

For anyone facing similar thing with hubspot API, the correlationHeaderExcludedDomains fixes it:

config: {
   .....
   correlationHeaderExcludedDomains: ["js.hs-scripts.com", "api.hubspot.com"],
}

nbelyh avatar Oct 04 '22 20:10 nbelyh

As @MSNev suggested, the existence of the traceparent HTTP request header (and/or others added by the A.I code possibly?) added to certain fetch requests (as I had manually enabled fetch tracking via disableFetchTracking: false) was causing them to fail CORS checks on the recipient server. For me, this was googleapis.com as part of my Google Firebase based authentication process.

I resolved this by excluding requests to that domain from having tracking headers added via:

correlationHeaderExcludedDomains: ["www.googleapis.com"]

in my A.I config

HARVS1789UK avatar Dec 01 '22 15:12 HARVS1789UK

As @MSNev suggested, the existence of the traceparent HTTP request header (and/or others added by the A.I code possibly?) added to certain fetch requests (as I had manually enabled fetch tracking via disableFetchTracking: false) was causing them to fail CORS checks on the recipient server. For me, this was googleapis.com as part of my Google Firebase based authentication process.

I resolved this by excluding requests to that domain from having tracking headers added via:

correlationHeaderExcludedDomains: ["www.googleapis.com"]

in my A.I config

You have just saved me a great deal of time. Thank you so much

I am now using: correlationHeaderExcludedDomains: ['www.googleapis.com', 'identitytoolkit.googleapis.com']

Which solves the issue. Not sure if there is a better way to wildcard these domains.

jeremylcarter avatar Feb 01 '23 02:02 jeremylcarter

These are (simply) "converted" into regular expressions where any "*" is changed to the .* regex (so you can include wildcards). This is done here

This is a simple conversion as it's only expecting URL's so the presence of other unhandled RegEx expressions would do cause some potential unexpected behavior (this may be addressed at some point if it becomes an issue) -- ie. I mean if we start getting requests to escape other special regEx values (tokens) then if you rely on a complex expression expect it could get broken.

So for now "*" is the only supported regex field, so specifying a single "*.googleapis.com" should work.

MSNev avatar Feb 01 '23 16:02 MSNev

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

github-actions[bot] avatar Feb 02 '24 00:02 github-actions[bot]