opentelemetry-js icon indicating copy to clipboard operation
opentelemetry-js copied to clipboard

CORS requirements on web exporters with navigator.sendBeacon

Open legendecas opened this issue 3 years ago • 13 comments
trafficstars

  • [x] This only affects the JavaScript OpenTelemetry library

navigator.sendBeacon with non-simple content-type header like "application/json" requires CORS checks like Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, and Access-Control-Allow-Credentials to be set with appropriate values on preflight requests.

Notably, sendBeacon initialize the request with credential mode set to true with a non-simple content-type, which means the server has to respond to preflight requests with Access-Control-Allow-Credentials true too. This is not configurable nor an option exposed with the sendBeacon API. That is to say, credentials like cookies, authorization headers or TLS client certificates are sent with sendBeacon in this case. However, the credential mode is configurable with fetch and XHR APIs.

We have the following use cases on sendBeacon:

  • OTLP exporter: Sent with application/json content type, not tested if OTLP server responds with proper CORS headers.
  • Zipkin exporter: actually, the Zipkin server accepts JSON data sent over content-type as text/plain, so the problem does not apply to Zipkin backends.
  • Zipkin exporter sending to Jaeger backend: Not supported without a reverse proxy, Jaeger server rejects text/plain content-type, and doesn't respond with Access-Control-Allow-Credentials CORS header. (issue: https://github.com/open-telemetry/opentelemetry-js/issues/1727)

Currently, we don't provide options on how those data are sent, like sent with fetch (environments that don't support XHR or beacon), XHR or beacon, and the CORS credential modes like omit or include. I think it may be worthwhile to add a package that shares all the necessary setups on the web platform.

legendecas avatar Jun 27 '22 10:06 legendecas

For the Zipkin compatible mode of the Jaeger server, I'm if it is behaving in an expected way. Maybe @yurishkuro can chime in here?

legendecas avatar Jun 27 '22 10:06 legendecas

Zipkin receiver in Jaeger has a config option for cors.

yurishkuro avatar Jun 27 '22 14:06 yurishkuro

In https://www.jaegertracing.io/docs/1.35/cli/ I see the allowed headers and allowed origins, but I don't see the allow credentials response header. Is there somewhere else I should look for cors configs?

dyladan avatar Jun 27 '22 16:06 dyladan

Zipkin receiver in Jaeger has a config option for cors.

Yes, but as stated in the OP it is missing Access-Control-Allow-Credentials CORS header. This behavior is as same as Zipkin's. Yet, Zipkin supports interpreting simple content types like text/plain so navigator.sendBeacon (the API Zipkin exporter uses by default) can successfully send data to Zipkin servers. However, Jaeger (tested with 1.35) does not support simple content types and rejects requests that are not typed as MIME application/json.

With MIME types like application/json, navigator.sendBeacon requires the server to respond with CORS headers, and specifically Access-Control-Allow-Credentials need to be true.

legendecas avatar Jun 27 '22 16:06 legendecas

but I don't see the allow credentials response header

Seems like easily added if needed. The full Options struct has a lot more fields than what Jaeger CLI flags currently support - https://github.com/rs/cors/blob/da52b0701de54d82f71776d634d4183b69c3a915/cors.go#L32

yurishkuro avatar Jun 27 '22 18:06 yurishkuro

Having said that, maybe it's better to implement this in OTLP receiver, then Jaeger could use that as well. I prefer to spend less effort on supporting legacy formats like Zipkin.

yurishkuro avatar Jun 27 '22 18:06 yurishkuro

This works fine for me for now ~

import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
const exporter = new OTLPTraceExporter({
  headers: {}, // magic here !!!
  url: '<your ot receiver host>/v1/traces',
});

After digging into the source code with debugger i found this, with the header: {} option, ot sender will use xhr instead of sendBeacon !

image

so without credentials: 'include', this config on ot receiver will work

receivers: 
  otlp:
    protocols:
      grpc:
      http:
        cors:
          allowed_origins: ["*"]
          allowed_headers: ["*"]

iShawnWang avatar Jul 19 '22 15:07 iShawnWang

Maybe we should include a way to force xhr or sendbeacon by config

dyladan avatar Jul 20 '22 14:07 dyladan

Maybe we should include a way to force xhr or sendbeacon by config

Yeah, this is what I have in my mind too. I'm thinking about introducing a new @opentelemetry/core-web package for web utilities that need to be shared across various web packages. Currently, those utils are located in the @opentelemetry/sdk-trace-web (e.g. https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-sdk-trace-web/src/utils.ts#L201).

legendecas avatar Jul 20 '22 16:07 legendecas

Maybe consider having xhr (or fetch considering how it's available in node v18+, deno v1 and usually polyfilled in IE) as the default method in general and navigator.sendBeacon as backup when in browser document is being unloaded?

  • sendBeacon has a 64KB limit. In firefox this is per sendBeacon call (eg can do 64KB + 64KB + 64KB), but in chrome this limit also applies for requests queue so 64 KB + won't send + won't send

image

  • It's common to send RUM data directly to a vendor endpoint (eg. https://bam.vendor-data.net/). There is also a rule in EasyPrivacy list used by content blockers like adblock, ublock origin and such:
! Block ping
$ping,third-party

What this rule means is all sendBeacon calls to third-party domains should be blocked, meaning all data from users with this adblock list enabled would be missing

  • More of a future looking but should any format introduce support for retrying sending data, it would be necessary to read response from server

t2t2 avatar Jul 21 '22 14:07 t2t2

In my opinion, xhr or fetch is more predictable ~

iShawnWang avatar Jul 21 '22 14:07 iShawnWang

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

github-actions[bot] avatar Sep 26 '22 06:09 github-actions[bot]

This issue was closed because it has been stale for 14 days with no activity.

github-actions[bot] avatar Oct 31 '22 06:10 github-actions[bot]

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

github-actions[bot] avatar Jan 30 '23 06:01 github-actions[bot]

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

github-actions[bot] avatar Apr 03 '23 06:04 github-actions[bot]

This issue was closed because it has been stale for 14 days with no activity.

github-actions[bot] avatar Jul 10 '23 06:07 github-actions[bot]