Proxied Authentication

RStudio Connect supports proxied authentication. This allows an external system to intercept requests and handle the authentication of users visiting the RStudio Connect dashboard or applications Connect is hosting.

How this Works

A service (like Apache, for example) runs as your customized authentication server. It is responsible for intercepting all requests to RStudio Connect and performing the required authentication and authorization. Requests from authenticated users will have custom HTTP headers added before the request is proxied through to RStudio Connect. The HTTP headers will contain the username and other identifying information of that visitor. RStudio Connect will take the values from the HTTP headers and treat the traffic as the user specified in the headers.

We have no means of validating that this HTTP header was added by your authentication server and not by the user directly. It is very important from a security perspective that network access to the RStudio Connect server is properly limited by a firewall or other network restriction. All access to RStudio Connect must be proxied through your authentication server.

Important Note

The HTTP header identifying the user should never be set by the requester. In all cases, your authentication server should delete that header if it exists before authenticating the user and adding the header itself. RStudio Connect will return a generic authentication failure if duplicate authentication headers are provided.

RStudio Connect does not currently support directing users to a login page when using proxied authentication. Therefore, we recommend that your proxy prevent anonymous access to RStudio Connect; only allow authenticated users.

Deployment from the RStudio IDE

Deploying from the RStudio IDE is a unique situation. The IDE uses an R package rsconnect to obtain deployment credentials from RStudio Connect. Those credentials are used to sign deployment requests. The minimum required version of rsconnect for proxied authentication is 0.8.7.

Deployment requests are signed with credentials obtained during an earlier, authenticated session, and should pass through your proxy without alteration.

The X-Auth-Token header identifies deployment requests. When the X-Auth-Token header is present, the request should be allowed to pass through your proxy without attempting to authenticate the user. Further, the following three headers are used in the deployment process and should not be edited or removed by your proxy.

  • X-Auth-Token
  • X-Auth-Signature
  • X-Content-Checksum

Using the RStudio Connect Server API

The Authorization header should allow traffic to pass through the proxy without attempting to authenticate the user when:

  • The RStudio Connect Server API is being used.
  • RStudio Connect is serving hosted APIs (i.e. Plumber, TensorFlow, etc.)

Note: Look for a value containing Key followed by the API key for a more specific match of the Authorization header used by RStudio Connect.

Configuring Proxied Authentication

To configure RStudio Connect to use proxied authentication, set Authentication.Provider to proxy.

; /etc/rstudio-connect/rstudio-connect.gcfg
[Authentication]
Provider = proxy

Proxied authentication requires that you set Server.Address to point at your proxy server. If you do not configure Server.Address, the browser may not have all its requests routed through your authenticating proxy. See Editing the Configuration File for more information about Server.Address.

; /etc/rstudio-connect/rstudio-connect.gcfg
[Server]
Address = https://myproxy.company.com/

You can customize the name of the header that your authentication server will send upon a successful authentication. By default, this key name is X-Auth-Username.

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
UsernameHeader = X-Auth-Username

RStudio Connect can also be configured to receive more complete user profile information from the proxy via additional headers.

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
UsernameHeader = X-Auth-Username
FirstNameHeader = X-Auth-First-Name
LastNameHeader = X-Auth-Last-Name
EmailHeader = X-Auth-Email

A user's username, first name, last name, and email address may be mapped from the headers as in the example above. If a mapping is not configured, the corresponding field will be editable in RStudio Connect, subject to the Authorization.UserInfoEditableBy configuration option.

Note: The fields configured to be sent by the proxy are not editable via the RStudio Connect dashboard or Connect Server API. Any updated values sent by the proxy will be reflected in the user's profile the next time that user logs in. A non-editable field (one configured to be sent in the proxy headers) can be forcefully updated before a login via the usermanager CLI tool. See the User Management CLI appendix.

Assigning User Roles Automatically

Additionally, a user role in RStudio Connect can be informed via a proxy header. Use the setting ProxyAuth.RoleHeader to inform which header will contain the role.

Note: The role header accepts the values viewer, publisher or administrator. Invalid values are defaulted to value of Authorization.DefaultUserRole and the issue is reported in the logs if debug logging is enabled.

If you prefer to map the values of a field to RStudio Connect user roles, you can use the Automatic User Role Mapping to map custom values returned during authentication or group names to valid user roles in RStudio Connect.

Note: Roles are no longer editable via the Dashboard if assigned automatically.

Supporting Changes to the Username

By default, the username is the only means of user identity required by RStudio Connect, and every new username gets a new account in RStudio Connect. If you wish to be able to update usernames over time while maintaining a single account for each user, a different form of unique user identity must be provided via the proxy headers.

The setting ProxyAuth.UniqueIdHeader determines which header holds the user identity. It's important that the user identity sent over this header by the proxy be:

  • Unique: It is extremely important that different users do not have the same identity.

  • Persistent: Ideally this identity must not be attached to the username, email or names, since those may change over time. Values based on UUID/GUID are a great options if your authentication system provides them.

Note: When configured this way, usernames provided by the proxy are not required to be unique.

IMPORTANT: Existing customers willing to enable the ProxyAuth.UniqueIdHeader MUST first update all users to the new identity before configuring the setting. The usermanager CLI tool can help in this migration. See the User Management CLI appendix.

Note: Configuring ProxyAuth.UniqueIdHeader also enables the RStudio Connect Server API to accept the field unique_id for creating new users. This value must match the value sent by the proxy via headers. Internally in Connect, the UniqueID is stored encoded as a Base64 value. Once the user is created, the RStudio Connect identity (guid) should be used for subsequent API operations with that user.

When the ProxyAuth.UniqueIdHeader option is configured, you must include either the ProxyAuth.UsernameHeader or the ProxyAuth.EmailHeader option (or both) in your configuration. If you do not configure the username attribute, RStudio Connect will create a new unique username for a user the first time that user logs in. The username will be derived from the user's email address, without the domain, adding a numeric suffix as needed for uniqueness if necessary. These usernames are editable.

Supporting Groups

The setting ProxyAuth.GroupsHeader defines the name of the header containing the names of the groups the authenticating user is member of.

Note: Your authentication provider may send the groups' identifiers instead of their names during login. This is true for Azure. In this situation, you should use the ProxyAuth.GroupsByUniqueId configuration option and manage your groups via the Connect Server API. This option also alters the values expected for role assignment via groups.

The list of group names sent by the proxy is compared with the memberships the user currently has in RStudio Connect and then any missing memberships are added to existing groups and any memberships that no longer apply are removed.

By default it is expected that multiple groups names will come as separate occurrences of the same header, for example:

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
GroupsHeader = X-Auth-Groups

HTTP Request:

X-Auth-Groups: Engineering
X-Auth-Groups: Managers
...

However, it is also possible to send group names on a single header. In this case, the group names in the header value must be sent as a separated list. Use the setting ProxyAuth.GroupsHeaderSeparator to enable this behavior and set which character should be used to split the group names.

For example:

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
GroupsHeader = X-Auth-Groups
GroupsHeaderSeparator = "|"

HTTP Request:

X-Auth-Groups: Engineering|Managers|...

Note: Certain characters cannot be used as separators since they are prohibited in HTTP headers. The RFC 7230 define those.

Automatic Group Provisioning

In addition to delegating group membership management to the authentication engine behind the proxy, RStudio Connect can also delegate the management of groups themselves to the proxy. By using ProxyAuth.GroupsAutoProvision RStudio Connect will automatically create and optionally remove groups based on the list of group names received from the proxy.

With this option enabled groups are provisioned in RStudio Connect when the first member is added and remain there indefinitely, even after the last member has been removed, so that any access to content is preserved for a future member of those groups.

Note: RStudio Connect can be configured to automatically decommission (remove) groups without members with the additional setting ProxyAuth.GroupsAutoRemoval. It also removes the group from all content it has been previously associated with.

In this mode, it is assumed that all group management will be done externally to RStudio Connect and therefore the user interface for group management will be hidden in the RStudio Connect dashboard.

Switching Between Manual and Automatic Group Provisioning

Groups created by automatic provisioning do not have owners while any groups created manually or via the Connect Server API are always associated with a user.

RStudio Connect will issue a warning on startup if the ownership of the existing groups does not match the provider configuration.

In these situations, either the group needs to be removed or group ownership needs to be adjusted. This can be done with the usermanager CLI tool with the alter command using the --new-owner or --drop-owner switches.

The goal is that all groups automatically provisioned should be assigned an owner if you intend to manage them in RStudio Connect. Conversely if you intend to have all groups managed by the authentication provider their owners should be removed. Also, you should remove any groups that do not make sense in the new configuration and this can be done via the RStudio Connect dashboard, the Connect Server API or the usermanager. See the User management CLI appendix.

Note: Additional to group ownership, groups created manually or via the Connect Server API may not match the names of the groups sent by the proxy and therefore those cannot be removed if both auto provisioning and auto removal are enabled. The Connect Server API can also be used to remove these extra groups.

Custom Login & Logout URLs

Logout

Since authentication is controlled by the proxy, RStudio Connect has no means for finishing the user session. By default, there is no "Log Out" link visible in the Dashboard, and steps must be taken outside of RStudio Connect in order to close the session.

If your proxy has a known endpoint that users should visit to close a session, you can configure the endpoint URL in RStudio Connect with the configuration option ProxyAuth.LogoutURL.

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
LogoutURL = https://proxy/logout

It is important to note that RStudio Connect will take this URL as-is and place it in the "Log Out" link in its Dashboard. Clicking the link will take the browser directly to the configured logout URL. It will not be a redirect.

Note: No information about the current logged in user will be provided, the proxy must be able to determine this based on the context (i.e. cookies).

Login

In most cases, the authentication and the proxy will not let users reach RStudio Connect unless they have a valid session. However, in some cases you may wish to let users visit some content without authentication; allowing RStudio Connect to control when login is required.

If your proxy has a known endpoint that users should visit to open a session, you can configure the endpoint URL in RStudio Connect with the configuration option ProxyAuth.LoginURL.

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
LoginURL = https://proxy/login

It is important to note that RStudio Connect will take this URL as-is and it will redirect to it when users click the "Log In" link in its Dashboard.

Proxies should consider the referer header during this redirect. It may contain the URL originally visited by the user in RStudio Connect and the proxy may want to refer users back to this location after authentication.

Note: The "Log Out" in the Dashboard is made visible when ProxyAuth.LoginURL is defined. This behavior is considered a convenience, and the act of logging out will only take users back to the configured login URL. It is up to the proxy to determine if this action is capable of invalidating the session.

Troubleshooting Proxied Authentication

When attempting to troubleshoot a problem relating to proxied authentication, you can enable more verbose logging with ProxyAuth.Logging:

; /etc/rstudio-connect/rstudio-connect.gcfg
[ProxyAuth]
Logging = true

All the contents of headers and the names of the cookies received with the authentication request will be logged. Cookies and headers used by RStudio Connect will be logged as <internal>. External cookie values will be logged as <redacted>. If an external cookie comes without a value it will be logged as <empty>.

  • "Rejected insecure proxied authentication attempt" appears in the server logs, users cannot log in

    1. Ensure that the proxy is configured to delete the configured username, names, email and unique identity headers from incoming requests (X-Auth-Username by default).

    2. Ensure that users are connecting to RStudio Connect by its proxy, and not directly to the server. As noted above, your network should be configured to make non-proxied connections to RStudio Connect impossible.

  • Attempts to deploy to RStudio Connect from the IDE fail because users are redirected to a Single Sign-On page.

    1. Ensure that the proxy is configured to pass through all requests that set the X-Auth-Token header.

    2. Ensure that the user has the rsconnect package with at least version 0.8.7 installed. If not, and if the package isn't available from CRAN, it may be installed from the R console using devtools::install_github('rstudio/rsconnect')

    See Deployment from the RStudio IDE.

  • Attempts to use the Connect Server API fail because users are redirected to a Single Sign-On page.

    1. Ensure that the proxy is configured to pass through all requests that set the Authentication header with a Key value.

    2. Ensure that the proxy is not blocking other headers that may be required by hosted APIs (i.e. Plumber, TensorFlow, etc.)

    See Using the RStudio Connect Server API.

Example Proxy Configuration

The following examples demonstrate how to intercept and re-route unauthenticated requests to RStudio Connect, which is served via reverse-proxy at /rsconnect. The examples assume an authentication server is running at /auth which, upon successful authentication, will set a HTTP-only cookie named verified-user containing the username of the authenticated user and redirect the user back to RStudio Connect via the URL specified in the url query parameter. A similar logic is applied to remaining examples that send additional user information.


IMPORTANT: These examples are intended to aid with an initial configuration of RStudio Connect behind an authenticating proxy. They are not recommended for production environments and have severe functional limitations (i.e. ' and ; are not accepted in values because the examples use plain text cookies) and offer no security at all. Customers are urged to adhere to the best security practices and recommended settings for their proxy.


All examples below are based on how to run a proxy in front of RStudio Connect. See Running with a Proxy.

Note: These examples assume your proxy has no specific URLs for login and logout but there is indication after each example of what should be changed in the proxy configuration if these URLs are available and configured in RStudio Connect with the options ProxyAuth.LoginURL and ProxyAuth.LogoutURL.

Basic configuration example

The bare minimum to allow a user coming from an authentication proxy to enter RStudio Connect. It assumes default values for the existing settings for proxied authentication. See Configuring Proxied Authentication.

These examples send traffic through the proxy if the verified-user cookie is present. The examples also allow traffic for deployments from the RStudio IDE or API use if the respective headers are present. See Using the RStudio Connect Server API and Deployment from the RStudio IDE.

Nginx:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ""      close;
}

map $http_cookie $auth_username {
  default "";
  "~*verified-user=(?<username>[^;]+)" "$username";
}

map "$http_x_auth_token" $has_token {
    default "";
    "~.+" "token";
}

map "$http_authorization" $has_key {
    default "";
    "~Key .+" "key";
}

map "$auth_username:$has_token:$has_key" $requires_auth {
  default 0;
  "::" 1;
}

server {
  listen 80 default_server;

  location /rsconnect/ {

    # redirect directly to authentication if the username is not present
    # needed if there is no login endpoint in the proxy
    if ($requires_auth = 1) {
      return 307 $scheme://$host:$server_port/auth/login?url=$request_uri;
    }

    rewrite                    ^/rsconnect/(.*)$ /$1 break;
    proxy_pass                 http://rstudio-connect-host:3939;
    proxy_redirect             / /rsconnect/;
    proxy_pass_request_headers on;
    proxy_connect_timeout      5;
    proxy_http_version         1.1;
    proxy_buffering            off; # Required for XHR-streaming

    proxy_set_header           Upgrade $http_upgrade;
    proxy_set_header           Connection $connection_upgrade;
    proxy_set_header           X-RSC-Request $scheme://$host:$server_port$request_uri;
    proxy_set_header           X-Auth-Username $auth_username;
  }
}

Note: If you have configured ProxyAuth.LoginURL the authentication condition if ($requires_auth = 1) of the example above is no longer needed.

Apache:

RewriteEngine on

<VirtualHost *:80>
  SetEnvIf Cookie "verified-user=([^;]+)" AuthUsername=$1

  # redirect directly to authentication if the username is not present
  # needed if there is no login endpoint in the proxy
  <If "%{REQUEST_URI} !~ m#^/auth# && -z env('AuthUsername') && -z %{HTTP:X-Auth-Token} && %{HTTP:Authorization} !~ /Key .+/">
    Redirect 307 "%{REQUEST_SCHEME}://%{HTTP_HOST}/auth/login?url=%{REQUEST_URI}"
  </If>

  # store variable values with dummy rewrite rules
  RewriteRule . - [E=req_scheme:%{REQUEST_SCHEME}]
  RewriteRule . - [E=http_host:%{HTTP_HOST}]
  RewriteRule . - [E=req_uri:%{REQUEST_URI}]
  # set header with variables
  RequestHeader set X-RSC-Request "%{req_scheme}e://%{http_host}e%{req_uri}e"

  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteCond %{HTTP:Connection} upgrade [NC]
  RewriteRule ^/(.*) "ws://rstudio-connect-host:3939/$1" [P,L]

  <If "-n env('AuthUsername')">
    RequestHeader set X-Auth-Username "%{AuthUsername}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Username
  </Else>

  <Location />
    ProxyPass http://rstudio-connect-host:3939/ connectiontimeout=5
  </Location>
</VirtualHost>

Note: If you have configured ProxyAuth.LoginURL the authentication condition <If "%{REQUEST_URI} of the example above is no longer needed.

Configuration example with user profile support

These configurations extend the basic examples and add support for headers carrying user profile information, such as names and email. The names for the different headers must be also present in your RStudio Connect configuration. See Configuring Proxied Authentication.

Nginx:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ""      close;
}

map $http_cookie $auth_username {
  default "";
  "~*verified-user=(?<username>[^;]+)" "$username";
}

map $http_cookie $auth_firstname {
  default "";
  "~*proxyauth-as-first=(?<first>[^;]+)" "$first";
}

map $http_cookie $auth_lastname {
  default "";
  "~*proxyauth-as-last=(?<last>[^;]+)" "$last";
}

map $http_cookie $auth_email {
  default "";
  "~*proxyauth-as-email=(?<email>[^;]+)" "$email";
}

map "$http_x_auth_token" $has_token {
    default "";
    "~.+" "token";
}

map "$http_authorization" $has_key {
    default "";
    "~Key .+" "key";
}

map "$auth_username:$has_token:$has_key" $requires_auth {
  default 0;
  "::" 1;
}

server {
  listen 80 default_server;

  location /rsconnect/ {

    # redirect directly to authentication if the username is not present
    # needed if there is no login endpoint in the proxy
    if ($requires_auth = 1) {
      return 307 $scheme://$host:$server_port/auth/login?url=$request_uri;
    }

    rewrite                    ^/rsconnect/(.*)$ /$1 break;
    proxy_pass                 http://rstudio-connect-host:3939;
    proxy_redirect             / /rsconnect/;
    proxy_pass_request_headers on;
    proxy_connect_timeout      5;
    proxy_http_version         1.1;
    proxy_buffering            off; # Required for XHR-streaming

    proxy_set_header           Upgrade $http_upgrade;
    proxy_set_header           Connection $connection_upgrade;
    proxy_set_header           X-RSC-Request $scheme://$host:$server_port$request_uri;
    proxy_set_header           X-Auth-Username $auth_username;
    proxy_set_header           X-Auth-Firstname $auth_firstname;
    proxy_set_header           X-Auth-Lastname $auth_lastname;
    proxy_set_header           X-Auth-Email $auth_email;    
  }
}

Note: If you have configured ProxyAuth.LoginURL the authentication condition if ($requires_auth = 1) of the example above is no longer needed.

Apache:

RewriteEngine on

<VirtualHost *:80>
  SetEnvIf Cookie "verified-user=([^;]+)" AuthUsername=$1
  SetEnvIf Cookie "proxyauth-as-first=([^;]+)" AuthFirstName=$1
  SetEnvIf Cookie "proxyauth-as-last=([^;]+)" AuthLastName=$1
  SetEnvIf Cookie "proxyauth-as-email=([^;]+)" AuthEmail=$1

  # redirect directly to authentication if the username is not present
  # needed if there is no login endpoint in the proxy
  <If "%{REQUEST_URI} !~ m#^/auth# && -z env('AuthUsername') && -z %{HTTP:X-Auth-Token} && %{HTTP:Authorization} !~ /Key .+/">
    Redirect 307 "%{REQUEST_SCHEME}://%{HTTP_HOST}/auth/login?url=%{REQUEST_URI}"
  </If>

  <If "-n env('AuthUsername')">
    RequestHeader set X-Auth-Username "%{AuthUsername}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Username
  </Else>
  <If "-n env('AuthFirstName')">
    RequestHeader set X-Auth-Firstname "%{AuthFirstName}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Firstname
  </Else>
  <If "-n env('AuthLastName')">
    RequestHeader set X-Auth-Lastname "%{AuthLastName}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Lastname
  </Else>
  <If "-n env('AuthEmail')">
    RequestHeader set X-Auth-Email "%{AuthEmail}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Email
  </Else>

  # store variable values with dummy rewrite rules
  RewriteRule . - [E=req_scheme:%{REQUEST_SCHEME}]
  RewriteRule . - [E=http_host:%{HTTP_HOST}]
  RewriteRule . - [E=req_uri:%{REQUEST_URI}]
  # set header with variables
  RequestHeader set X-RSC-Request "%{req_scheme}e://%{http_host}e%{req_uri}e"

  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteCond %{HTTP:Connection} upgrade [NC]
  RewriteRule /rsconnect/(.*) "ws://rstudio-connect-host:3939/$1" [P,L]

  RewriteRule ^/rsconnect$ /rsconnect/ [R]

  <Location /rsconnect/>
    ProxyPass http://rstudio-connect-host:3939/ connectiontimeout=5
    ProxyPassReverse /rsconnect/
    ProxyPassReverse /
  </Location>
</VirtualHost>

Note: If you have configured ProxyAuth.LoginURL the authentication condition <If "%{REQUEST_URI} of the example above is no longer needed.

Configuration example with user profile and unique identity support

In these examples, username is considered part of the user profile. An additional header is used to communicate user identity. See Configuring Proxied Authentication.

Nginx:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ""      close;
}

map $http_cookie $auth_uniqueid {
  default "";
  "~*proxyauth-as-id=(?<uniqueid>[^;]+)" "$uniqueid";
}

map $http_cookie $auth_username {
  default "";
  "~*verified-user=(?<username>[^;]+)" "$username";
}

map $http_cookie $auth_firstname {
  default "";
  "~*proxyauth-as-first=(?<first>[^;]+)" "$first";
}

map $http_cookie $auth_lastname {
  default "";
  "~*proxyauth-as-last=(?<last>[^;]+)" "$last";
}

map $http_cookie $auth_email {
  default "";
  "~*proxyauth-as-email=(?<email>[^;]+)" "$email";
}

map "$http_x_auth_token" $has_token {
    default "";
    "~.+" "token";
}

map "$http_authorization" $has_key {
    default "";
    "~Key .+" "key";
}

map "$auth_uniqueid:$has_token:$has_key" $requires_auth {
  default 0;
  "::" 1;
}

server {
  listen 80 default_server;

  location /rsconnect/ {

    # redirect directly to authentication if the username is not present
    # needed if there is no login endpoint in the proxy
    if ($requires_auth = 1) {
      return 307 $scheme://$host:$server_port/auth/login?url=$request_uri;
    }

    rewrite                    ^/rsconnect/(.*)$ /$1 break;
    proxy_pass                 http://rstudio-connect-host:3939;
    proxy_redirect             / /rsconnect/;
    proxy_pass_request_headers on;
    proxy_connect_timeout      5;
    proxy_http_version         1.1;
    proxy_buffering            off; # Required for XHR-streaming

    proxy_set_header           Upgrade $http_upgrade;
    proxy_set_header           Connection $connection_upgrade;
    proxy_set_header           X-RSC-Request $scheme://$host:$server_port$request_uri;
    proxy_set_header           X-Auth-Uniqueid $auth_uniqueid;
    proxy_set_header           X-Auth-Username $auth_username;
    proxy_set_header           X-Auth-Firstname $auth_firstname;
    proxy_set_header           X-Auth-Lastname $auth_lastname;
    proxy_set_header           X-Auth-Email $auth_email;    
  }
}

Note: If you have configured ProxyAuth.LoginURL the authentication condition if ($requires_auth = 1) of the example above is no longer needed.

Apache:

RewriteEngine on

<VirtualHost *:80>
  SetEnvIf Cookie "verified-user=([^;]+)" AuthUniqueId=$1
  SetEnvIf Cookie "proxyauth-as-user=([^;]+)" AuthUsername=$1
  SetEnvIf Cookie "proxyauth-as-first=([^;]+)" AuthFirstName=$1
  SetEnvIf Cookie "proxyauth-as-last=([^;]+)" AuthLastName=$1
  SetEnvIf Cookie "proxyauth-as-email=([^;]+)" AuthEmail=$1

  # redirect directly to authentication if the username is not present
  # needed if there is no login endpoint in the proxy
  <If "%{REQUEST_URI} !~ m#^/auth# && -z env('AuthUniqueId') && -z %{HTTP:X-Auth-Token} && %{HTTP:Authorization} !~ /Key .+/">
    Redirect 307 "%{REQUEST_SCHEME}://%{HTTP_HOST}/auth/login?url=%{REQUEST_URI}"
  </If>

  <If "-n env('AuthUniqueId')">
    RequestHeader set X-Auth-Uniqueid "%{AuthUniqueId}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Uniqueid
  </Else>
  <If "-n env('AuthUsername')">
    RequestHeader set X-Auth-Username "%{AuthUsername}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Username
  </Else>
  <If "-n env('AuthFirstName')">
    RequestHeader set X-Auth-Firstname "%{AuthFirstName}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Firstname
  </Else>
  <If "-n env('AuthLastName')">
    RequestHeader set X-Auth-Lastname "%{AuthLastName}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Lastname
  </Else>
  <If "-n env('AuthEmail')">
    RequestHeader set X-Auth-Email "%{AuthEmail}e"
  </If>
  <Else>
    RequestHeader unset X-Auth-Email
  </Else>

  # store variable values with dummy rewrite rules
  RewriteRule . - [E=req_scheme:%{REQUEST_SCHEME}]
  RewriteRule . - [E=http_host:%{HTTP_HOST}]
  RewriteRule . - [E=req_uri:%{REQUEST_URI}]
  # set header with variables
  RequestHeader set X-RSC-Request "%{req_scheme}e://%{http_host}e%{req_uri}e"

  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteCond %{HTTP:Connection} upgrade [NC]
  RewriteRule /rsconnect/(.*) "ws://rstudio-connect-host:3939/$1" [P,L]

  RewriteRule ^/rsconnect$ /rsconnect/ [R]

  <Location /rsconnect/>
    ProxyPass http://rstudio-connect-host:3939/ connectiontimeout=5
    ProxyPassReverse /rsconnect/
    ProxyPassReverse /
  </Location>
</VirtualHost>

Note: If you have configured ProxyAuth.LoginURL the authentication condition <If "%{REQUEST_URI} of the example above is no longer needed.