Version 3.0.2 of the documentation is no longer actively maintained. The site that you are currently viewing is an archived snapshot. For up-to-date documentation, see the latest version.
FAQ
Container did not respond in time
If you see the following error message in your browser
Error
Status code: 500
Message: Container did not respond in time
it is very likely that your container was started, but that the Shiny app itself could not be started properly.
This almost always means an R error is triggered when docker is trying to launch the Shiny app.
Typical examples are:
- some dependencies for your Shiny application were not installed into the Docker image and therefore could not be loaded when you run your application
- you are using non-exported functions from a package and this triggers an error
- any other R error that is triggered prior to the proper loading of your Shiny application
The best way to pinpoint the origin of the problem is to launch the docker container ‘manually’ i.e. independently of ShinyProxy and see whether or not this succeeds.
By means of example, let’s assume the hello
application of the openanalytics/shinyproxy-demo
image does not work properly. In the default application.yml
we can see that it is being run using
specs:
- id: 01_hello
container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"]
container-image: openanalytics/shinyproxy-demo
access-groups: [scientists, mathematicians]
In other words, the docker command (container-cmd
) used is ["R", "-e", "shinyproxy::run_01_hello()"]
.
In order to run it ‘manually’ (using the docker command line interface) we can do the following:
sudo docker run -p 3838:3838 openanalytics/shinyproxy-demo R -e 'shinyproxy::run_01_hello()'
and this will generate the following output:
R version 3.2.4 Revised (2016-03-16 r70336) -- "Very Secure Dishes"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
Natural language support but running in an English locale
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.
> shinyproxy::run_01_hello()
Loading required package: shiny
Listening on http://0.0.0.0:3838
No errors in this output, but we can see exactly what happens inside the R session. This trick can come handy if you have real errors that prevent your Shiny app to come online!
Failed to start container
If you see the following error message in your browser
Error
Status code: 500
Message: Failed to start container: Request error: POST http://localhost:2376/containers/d3e0f4c02d72f71b9619e5b70a60aa1000ae5498a3743255e9afeb9ae5cbb14b/start: 500
it is very likely that shinyproxy was not able to start the container.
The first thing to check is whether the docker daemon is running. On Ubuntu 14.04 this can be done using
$ sudo service docker status
docker start/running, process 21167
When the docker service is up and running, the most common cause is that the firewall rules on the machine prevent ShinyProxy to connect to the docker API. In that case, it is worthwhile to disable the firewall temporarily and to check whether ShinyProxy is then able to start a container.
Address already in use
If you see the following information when launching ShinyProxy
(e.g. in the shinyproxy.log
file)
Exception in thread "main" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:62)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[...]
Caused by: java.net.BindException: Address already in use
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:433)
at sun.nio.ch.Net.bind(Net.java:425)
[...]
this will in all likelihood mean that another application (e.g. a Tomcat application server) is already running on port 8080, the default port ShinyProxy uses.
You can either stop the other application prior to launching ShinyProxy
or use a custom port (e.g. 9999) for ShinyProxy by editing the port
field in the application.yml
file:
proxy:
[...]
port: 9999
For more details, see the General section of the Configuration page.
Invalid redirect_uri when using OpenID Connect or Keycloak
When authenticating using OpenID, ShinyProxy first redirects the user to the IDP.
In this redirection request, ShinyProxy specifies a redirect_uri
.
When the IDP successfully authenticates the user, the IDP redirects the user to this redirect_uri
.
Therefore, this redirect_uri
should be the location at which ShinyProxy is hosted.
For security reasons, the IDP contains a list of which redirect_uri
are valid or allowed.
Therefore, if ShinyProxy specifies a wrong redirect_uri
to the IDP, the IDP will abort the authentication request.
This error message is typically caused when ShinyProxy is accessed over https but the generated redirect_uri
contains the http scheme instead of https.
By default, ShinyProxy generates a redirect_uri
using the plain http scheme, unless it detects that ShinyProxy is accessed over https.
As discussed in Security the recommended way to setup https for ShinyProxy is by using a reverse proxy.
The proxy server still accesses ShinyProxy using the plain HTTP protocol, therefore ShinyProxy cannot detect that it is accessed (by the users) using https.
To solve this issue, reverse proxies can add extra headers to the request, specifying the protocol used to access ShinyProxy.
These headers are X-Forwarded-For
and X-Forwarded-Proto
.
When you are using a reverse proxy and ShinyProxy is generating a wrong redirect_uri
,
- check that you correctly setup ShinyProxy to use the forward headers.
- check that the
server
part of this configuration is at the top level of the YAML file. - check that you specified the correct valid
redirect_uri
in the IDP configuration. - check whether your reverse proxy configuration contains the required options so that it adds the necessary headers.
- check that all proxies and/or loadbalancers in your setup support the forward-headers mechanism. For example, loadbalancers that operate at the network layer (rather than the application level) often do not support this feature. This is, for instance, the case when using AWS NLB.
- if it still does not work, enable request dumping and check whether the incoming requests to ShinyProxy contain the forward headers.
My browser reports a redirect loop when using OpenID Connect
As discussed in the previous entry, ShinyProxy and the IDP perform some redirects between each other. However, in the following scenario a redirect loop could happen:
- users goes to ShinyProxy
- ShinyProxy redirects the user to the IDP
- users successfully logins into the IDP
- IDP redirects you back to ShinyProxy
- ShinyProxy needs to validate the token provided by the IDP. However, for some reason ShinyProxy is unable to do this, and therefore thinks that the user is unauthenticated.
- therefore ShinyProxy redirects you back to the IDP
- IDP thinks you are authenticated and redirects you back to ShinyProxy
- see 5.
In ShinyProxy version 2.4.2 we prevented this from happening, instead the user is redirect to the /auth-error
page, which explains what has gone wrong.
Therefore, it is advised to upgrade to 2.4.2 when you are facing a redirect loop, because this version makes it easier to debug the underlying issue.
If you are still encountering a redirect loop when using version 2.4.2, please open an issue at GitHub.
Authentication using OpenID does not work because of Missing attribute 'email' in attributes
exception
When using OpenID (or Keycloak), ShinyProxy tries to use the e-mail of the user to identify the user. However, when the email is not specified in the IDP, ShinyProxy is unable to do so and therefore the authentication fails. To solve this change the username-attribute or name-attribute for respectively OpenID and Keycloak.
Tip: when using OpenID the sub
attribute should always be available to use. You can use this property when no other property works.
Authentication using OpenID does not work because of invalid_token_response
When using OpenID, ShinyProxy has to contact the IDP in order to validate the token. This exception indicates that this validation was unsuccessful, which can be caused by multiple things:
- ShinyProxy cannot reach the IDP because the access to the network access is blocked (e.g. because of some firewall), wrong port number, the connection timed out, … The error will be similar to:
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for "https://idp.com/openid-connect/token": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
Solution: fix the network issue.
- ShinyProxy cannot reach the IDP because it cannot resolve the hostname. The error will be similar to:
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for "https://non-existingidp.com/protocol/openid-connect/token": non-existingidp.com; nested exception is java.net.UnknownHostException: non-existingidp.com
Solution: ensure that the hostname is correct and that DNS is working properly.
- The firewall of the IDP is blocking the request. The error will be similar to:
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 403 Forbidden: [403 Forbidden]
Tip: when your IDP is protected by CloudFlare it could be that requests from ShinyProxy are blocked based on the user agent. Try changing the user-agent by starting ShinyProxy using the following command: java -Dhttp.agent="<user-agent>" shinyproxy.jar
- The
client-secret
is wrong and therefore the IDP is rejecting the request. The error will be similar to:
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]
Solution: specify the correct client-secret
.
- The
jwks-url
URL is wrong or giving bad responses. The error will be similar to:
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_id_token] An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.HttpClientErrorException$NotFound: 404 Not Found: [{"error":"RESTEASY003210: Could not find resource for full path: "}]
Solution: fix the URL or find out why the IDP is giving bad responses.
Debugging group-access
when using OpenID
The OpenID authentication backend supports reading a claim from the ID token and use this as the groups for the users. Try the following steps, when this does not work:
-
make sure the claim is added to the ID token (ShinyProxy does not read the groups from the access token). Since ShinyProxy 3.0.0 it is also possible to add the claim to the
userinfo
endpoint (but only if you specify theuserinfo-url
option). -
enable debug logging for the OpenID component by adding the following configuration:
logging: level: eu: openanalytics: containerproxy: auth: DEBUG
Note: the above code should be placed outside the
proxy:
block.ShinyProxy logs the contents of every claim it finds and also logs how it tries to parse the values of the roles claim. Note that you must include a
roles-claim
property before the logs will be visible. -
make sure the name of the claim is correctly configured in ShinyProxy. See the previous point to find out the name of the claim.
Uploading big files does not work
Before ShinyProxy 2.4.0, this documentation contained some information about how to configure file upload using multipart
properties.
Starting from ShinyProxy 2.4.0 this configuration is no longer needed and should be removed, since this version does not have any restrictions on the size of file uploads.
Hence, the following code should be removed from your configuration:
# Do not add this to your configuration!
spring:
servlet:
multipart:
max-file-size: <value>
max-request-size: <value>
Login does not work after upgrading from 2.3.0
If you are using custom templates (using the proxy.template-path
property) and
upgrade from ShinyProxy 2.3.0 to 2.3.1 or later you can face this issue. Since
ShinyProxy 2.3.1, CSRF protection is enabled and therefore an update of the login
template is needed.
In the login.html
template replace this line:
<form class="form-signin" action="login" method="POST">
by
<form class="form-signin" th:action="@{/login}" method="POST">
The issue should now be solved.
Is it possible to run Desktop Apps?
Yes! We have an extensive demo setup containing of two desktop apps: Phaedra and Visual Studio Code. All information can be found in the repository.
The credentials of the user expire when using SAML
By default SAML credentials are valid for a certain amount of time. This also means that those credentials have an “age’. When a user tries to login into ShinyProxy it could be possible that the IDP still has some valid (i.e. not expired) credentials for this user. In that case the IDP may provide these existing credentials to ShinyProxy. When validating these credentials, ShinyProxy checks the age of the credentials to make sure these are not too old. By default ShinyProxy rejects credentials that are older than two hours. However, the IDP is unaware of this restriction and thus may provide ShinyProxy credentials that are older than these two hours.
If you are experiencing this problem, the user is faced with a page that contains a stacktrace containing the following error:
org.springframework.security.saml.SAMLStatusException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
In the ShinyProxy log the following errors are logged:
org.springframework.security.authentication.CredentialsExpiredException: Authentication statement is too old to be used with value ...
There are two ways to solve this:
-
enable the
proxy.saml.force-authn
property. This option ensures that ShinyProxy asks the IDP to provide fresh credentials, even if the IDP believes the credentials are still valid. We have good experience with this option using Microsoft ADFS and Microsoft Azure AD. However, the downside of this option is that the user may have to refill their username and password, even if they are still logged in.Note: it seems that some IDPs ignore this option, e.g. Google ignores it.
-
set the
proxy.saml.max-authentication-age
option to a value greater or equal to the maximal age of the credentials provided by the IDP. This value is specified in seconds. This has the advantage that the user is not forced to refill their username and password.
How do I create a keystore for signing SAML messages?
By default SAML messages sent by ShinyProxy are not signed. However, the SAML
standard makes it mandatory that an application signs the messages used to
sign-out a user. Therefore, when you use the SAML Logout feature, you must
provide ShinyProxy with a keystore. When your IDP encrypts the SAML messages,
you already have a keystore and should not create a separate one. In the other
case you do have to create a new keystore. This section describes the process of
generating such a keypair, using the keytool
utility included in Java.
The tool can be used as follows:
keytool -deststoretype pkcs12 \
-genkeypair \
-keyalg RSA \
-keysize 4096 \
-sigalg SHA256withRSA \
-validity 1460 \
-alias shinyproxy-saml \
-keypass changeme \
-keystore samlKeystore.jks
validity
: how long the certificate is valid, specified in days. In the example this is about 4 years.keyalg
: use RSA instead of DSA (note: DSA does not work for ShinyProxy, EC may not work with Active Directory products)keysize
: use sensible keysizesigalg
: use a secure signature algorithmalias
: a name for your keypairkeypass
: the password used to encrypt the private keykeystore
: the filename of the keystore
The tool now aks to provide a password for the complete keystore after which it
asks for some attribute to specify in the certificate. In theory, all these
values are optional. You could specify your domain name as the value for the
first attribute (common name
) in order to easily identify the certificate.
If you wish you can now export the certificate and inspect it (e.g. to verify the remaining lifetime):
keytool -exportcert -alias shinyproxy-saml -keystore samlKeystore.jks -rfc -file cert.pem
openssl x509 -noout -text -in cert.pem
In order to configure ShinyProxy to use this keystore, use:
proxy:
saml:
keystore: /path/to/samlKeystore.jks
keystore-password: changeme # the password you provided when the keytool asked for it
encryption-cert-password: changeme # the password provided after the keypass parameter of the keytool
encryption-cert-name: shinyproxy-saml # the alias of the certificate
The assets of my Shiny app sometimes fail to load (HTTP error 503)
This issue is most likely caused by bug in a library used by the HTTP server used by the Shiny package. This library validates that the header size of an incoming HTTP request is not too big. However, it seems that this validation is not 100% correct, especially when the TCP connection is re-used for multiple HTTP requests. Fortunately, it is possible to disable this validation, by providing a build parameter when installing Shiny.
In order to disable the check, install Shiny using the following snippet in your Dockerfile:
RUN R -e "install.packages(c('withr'), repos='https://cloud.r-project.org/')"
RUN R -e "withr::with_makevars(c(PKG_CPPFLAGS='-DHTTP_MAX_HEADER_SIZE=0x7fffffff'), {install.packages(c('shiny'), repos='https://cloud.r-project.org/')}, assignment = '+=')"
Note: disabling this validation does not introduce any security issues, when running the Shiny app using ShinyProxy, since ShinyProxy contains the same validation. (however, since the check is disabled there will be no check when running the app standalone.)
References:
- https://github.com/nodejs/http-parser/issues/426#issuecomment-386215023
- https://github.com/nodejs/http-parser/issues/451
- https://github.com/rstudio/httpuv/blob/13bcd461e4228583b29e63d4eae8ad6bb00a4ab0/src/http-parser/http_parser.c#L144-L162
How to embed ShinyProxy using an iframe on a different domain?
It is possible to embed ShinyProxy using an iframe in the same way as any website, however, when this iframe is hosted on a different domain (or origin), this requires some extra configuration of the security features of ShinyProxy. In addition, it is required that ShinyProxy is accessed over a secure connection (HTTPS). Note that these settings require ShinyProxy 2.6.0 or later.
server.frame-options
should be configured correctly. Thedisable
value (which is the default) will always work. However, it is advised to change this setting to a correctallow-from
option.proxy.same-site-cookie
must be set toNone
.server.secure-cookies
must be set totrue
.
How to use Keycloak roles in ShinyProxy?
Warning
The keycloak authentication backend is deprecated since ShinyProxy 3.0.2, use the OpenID Connect backend instead (as shown in this example).Using OpenID connect you can integrate ShinyProxy with Keycloak, including using Keycloak roles as groups in ShinyProxy.
-
configure Keycloak to pass the groups to ShinyProxy:
- open the Keycloak console and go to the openid client you created
- go to the
Client scopes
tab - click on the first scope, e.g.
my-client-dedicated
- click on
Add mapper
, click onBy configuration
- click on
User Realm Role
- use
realm_roles
asName
- keep
Realm Role prefix
empty - keep the
Multivalued
option enabled - use
realm_roles
asToken Claim Name
- use
String as
Claim JSON type` - ensure the
Add to ID token
option is selected - the
Add to access token
orAdd to userinfo
options may optionally be enabled, but are not required for ShinyProxy - click
Save
The completed form should look like:
(click on the screenshot for a bigger version)
-
configure ShinyProxy to use Keycloak using the
openid
backend. See the documentation and this example:openid: auth-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/auth token-url: https:/keycloak.example.org/auth/realms/master/protocol/openid-connect/token jwks-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/certs client-id: shinyproxy client-secret: changeme logout-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/logout?id_token_hint=#{oidcUser.idToken.tokenValue}&returnTo=http%3A%2F%2Fmy-shinyproxy.io/logout-success username-attribute: email roles-claim: realm_roles
How to use Keycloak groups in ShinyProxy?
Warning
The keycloak authentication backend is deprecated since ShinyProxy 3.0.2, use the OpenID Connect backend instead (as shown in this example).Keycloak supports the concept of groups, in addition to roles. It’s possible to use these groups in ShinyProxy .
-
configure Keycloak to pass the groups to ShinyProxy:
- open the Keycloak console and go to the openid client you created
- go to the
Client scopes
tab - click on the first scope, e.g.
my-client-dedicated
- click on
Add mapper
, click onBy configuration
- click on
Group Membership
- use
groups
asName
- use
groups
asToken Claim Name
- switch off the
Full group path
option - ensure the
Add to ID token
option is selected - the
Add to access token
orAdd to userinfo
options may optionally be enabled, but are not required for ShinyProxy - click
Save
The completed form should look like:
(click on the screenshot for a bigger version)
-
configure ShinyProxy to use Keycloak using the
openid
backend. See the documentation and this example:openid: auth-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/auth token-url: https:/keycloak.example.org/auth/realms/master/protocol/openid-connect/token jwks-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/certs client-id: shinyproxy client-secret: changeme logout-url: https://keycloak.example.org/auth/realms/master/protocol/openid-connect/logout?id_token_hint=#{oidcUser.idToken.tokenValue}&returnTo=http%3A%2F%2Fmy-shinyproxy.io/logout-success username-attribute: email roles-claim: groups
Note: do not use the built-in groups
mapper of Keycloak as this mapper
provides the realm-roles of the user and not the groups.