posit-dev / r-shinylive

https://posit-dev.github.io/r-shinylive/
Other
151 stars 16 forks source link

Tiles from openstreetmap fail to load #51

Open guasi opened 7 months ago

guasi commented 7 months ago

Here is a simple shiny app with a map built with leaflet package. The shiny app works fine. I can runApp() from any R session and view it in multiple browsers. When exported using liveshiny and launched either by httpuv::runStaticServer() or by Live Preview in VSCode, the external resources for the leaflet map do not load. The png tiles get error Failed to load resource: net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep. I have tested in 5 different browsers.

library(shiny)
library(leaflet)

ui <- fluidPage(
  leafletOutput("map", width = "100%", height = "700px")
)

server <- function(input, output, session) {
  output$map <- renderLeaflet({
    m <- leaflet() |>
      addTiles() |>
      addPopups(-93.65, 42.0285, "ISU")
    m
  })
}

shinyApp(ui = ui, server = server)

I'm posting this as bug because it seems to be an error on how the static shiny app built with shinylive communicates or "authenticates" with sites asking for resources. But I may be wrong...I know very little about how resources get denied.

georgestagg commented 7 months ago

This is the same problem as in #49.

The R shinylive app iframe is served from the JS Service Worker with headers:

"cross-origin-embedder-policy": "require-corp"
"cross-origin-resource-policy": "cross-origin"

This was originally put in place so that the iframe could be embedded into the page when webR is running in Cross-Origin Isolated (COI) mode for the benefits that provides. However, the headers mean that the iframe has higher security requirements for further embedding its own content. Namely, embedded resources must themselves also be served with permissive CORP/CORS headers.

Now, though, webR can run in Shinylive without COI (with some limitations) and, in fact, currently does so by default on https://shinylive.io. So, we can now loosen this restriction a little. In fact, I recently discovered that there may be a way we can still support both modes and additionally support embedding further content (like the map tiles in this example) with a minor tweak to the headers:

"cross-origin-embedder-policy": "credentialless"
"cross-origin-resource-policy": "cross-origin"

IIUC switching to credentialless should allow resources to be loaded by the app in the iframe without those resources being served with CORP/CORS. Additionally, I think using credentialless still allows for COI for webR itself.

Credentialless means that requests with cookie-based authentication using webR/Shinylive may be lost, but I don't believe anyone is currently doing this. I will need to research this some more before making any changes, but my initial thoughts are that we can indeed improve this, both for Shinylive and the webR REPL.

I'll report back here again after some more testing.

SGEng6e commented 7 months ago

Believe this is the same issue posted on Stack Overflow with complete MWE. I am eager to learn how this solution could be implemented. Adding the following header to the shinylive-r chunk in the app.qmd file doesn't resolve the issue: #| Cross-Origin-Embedder-Policy: credentialless