500 Internal Server Error when submitting FORM/POST using CORS

Follow

Symptoms:

When launching an application in Posit Connect, the user receives the below error.

An error has occurred!
An error has occurred. Check your logs or contact the app author for clarification.

CORS is enabled in rstudio-connect.gcfg.

[CORS]
Enabled = true

 

Explanation:

Even if Connect is configured to have CORS enabled, it might be that the application still needs to account for the preflight OPTIONS request. When content hosted in Connect is responsible for handling requests and issue responses, sometimes such content needs to be adjusted so it allows CORS as well.

In other words, to fully enable CORS there are two scenarios to consider:
  1. When CORS communicates with Connect
  2. When Connect hands over requests to published content
When communicating with Connect, for example, via API; to manage users, groups, or even content, using Connect's configuration fields is enough. With that configuration in place, Posit Connect will know how to handle external domain requests. This is because, for this kind of request, Connect is the entity that builds the response.
However, when the external domain request is handed over by Connect to a published app, the published app then is responsible for building the response and if the published app is not prepared to handle external domain requests it replies with an error. Connect then passes that error to the user. For these cases, specific headers need to be set and handled within published apps that expect requests coming from external domains.

 

Workaround:

First, broaden the permissions for the CORS parameter in /etc/rstudio/rstudio-connect.gcfg.

[CORS]
Enabled = true
AllowOrigin = *
EnforceWebsocketOrigin = false

You will need to restart Posit Connect after making this change. 

systemctl restart rstudio-connect

Next, somewhere inside the R code for your application, you will need to add "OPTIONS" as a supported HTTP method. The response from the "OPTIONS" request needs to be a 200  and include the following headers:

"Access-Control-Allow-Origin: *"
"Access-Control-Allow-Methods: DELETE, POST, GET, OPTIONS, PUT, HEAD"
"Access-Control-Allow-Headers: *"

Finally, you will need to call these headers into the body of your code. The below example uses the Plumber API, but you will need to customize this for your specific code and use case.

#' @filter cors
cors <- function(req, res) {

res$setHeader("Access-Control-Allow-Origin", "*")

if (req$REQUEST_METHOD == "OPTIONS") {
res$setHeader("Access-Control-Allow-Methods","*")
res$setHeader("Access-Control-Allow-Headers", "*")
res$setHeader("Access-Control-Allow-Credentials", "true")
res$status <- 200 
return(list())
} else {
plumber::forward()
}
}

Comments