Draft for adding OAuth support to shiny #518
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Info: This is a draft for discussion purposes. It's not a polished PR and currently includes minimal error handling and documentation. It may be big enough to warrant a separate package, but it may also be so tighly coupled with httr2 that it makes sense for it to be integrated.
Summary
This PR addresses #47 and attempts to bring support for OAuth 2.0 apps to shiny. It builds upon the cookie and routing approach developed in r-lib/gargle#157 and extends this to:
httr2
functionality (e.g. token objects)This PR primarily addresses two key scenarios:
A more detailed guide for getting started is included in
vignettes/articles/shiny.Rmd
.Demo
You can run this locally or view an example application on shinyapps.io or a deployed version on Google Cloud Run. The demo application runs
oauth_shiny_app_example()
and stores no user information. Here is a preview of what to expect:OAuth 2.0 Authentication for Apps
To enforce login within a Shiny application, use
oauth_shiny_app()
with a configuration of OAuth clients:Standard clients (e.g.
oauth_client_github()
) resolve client IDs and secrets using environment variables (OAUTH_GITHUB_ID
andOAUTH_GITHUB_SECRET
). This setup will present a sign-in UI with login buttons (based on Google Material) for clients marked asauth_provider = TRUE
. Buttons link to client login endpoints (e.g.login/github
) which triggers the OAuth flow.Upon loading, the application checks for a signed cookie containing standard claims (at minimum
identifier
andprovider
) , which is set after successful authentication for a client withauth_provider = TRUE
. These claims can be retrieved from the server side in Shiny to customize the user interface:To disable authentication, pass
require_auth = FALSE
:Fetching access tokens
By default, access tokens retrieved after completing the OAuth flow are not stored (
access_token_validity = 0
). Client access tokens can be stored as encrypted cookies with amax-age
equal toaccess_token_validity
. These tokens can be retrieved asoauth_token
objects from the server side using:Here is an example which requests user information from Github in a public app :
Logging out
Redirecting users to
logout
clears both app cookies and all client access token cookies. Redirecting tologout/{client}
will only clear a single client's access token cookies.Example application
An example application (same as demo) is included to facilitate debugging client configurations and token retrieval:
OAuth Shiny Client
This PR introduces
oauth_shiny_client()
, similar tooauth_client
, but with additional information necessary to complete the authorization code flow. Standardized clients for GitHub, Google, Microsoft, and Spotify are included, but custom clients can be added easily, e.g. for Strava:For OAuth 2.0 applications compliant with the OpenID specification, it is enough to pass the
open_issuer_url
andscope
and optionally the claims to retrieve:This will automatically resolve the
auth_url
andtoken_url
endpoints and verify the signature of retrieved access tokens using public JSON Web Keys (JWK).Limitations
State Loss During Redirection
Currently, Shiny OAuth apps lose state during the OAuth redirection process. This could potentially be addressed by setting a server-side bookmarked state as a secure cookie, but this is not something I have given much thought.
Local Development
http://127.0.0.1
instead ofhttp://localhost
as the redirect URL. Cookies set at localhost won’t persist when using 127.0.0.1, causing the OAuth flow to fail.options(shiny.launch.browser = TRUE)
to avoid issues with the built-in browser, which does not support external redirects and OAuth cookies.Shinyapps.io
Shinyapps.io works well with non-OpenID providers (e.g., GitHub, Spotify). However, OpenID providers like Google and Microsoft may cause issues due to how Shinyapps.io handles redirection, potentially triggering a loading screen that replays the OAuth flow with the same authorization code causing it to fail.
Cloud Deployment
Shiny OAuth apps can be deployed as Docker containers, even on serverless platforms like Azure Container Apps and Google Cloud Run. Ensure you set the
HTTR2_OAUTH_APP_URL
environment variable to guarantee the correct server URL is inferred.Shiny Server
Shiny Server is not compatible with Shiny OAuth apps because it strips cookies.
Further Work
HTTR2_OAUTH_APP_URL
.req_oauth_auth_code_shiny
: Finalize API before adding this.oauth_shiny_config()
instead of passing separate arguments tooauth_shiny_app
.sodium
?Closing Remarks
I hope this serves as a sufficient draft for discussing how OAuth support could be integrated in shiny. I think this functionality could make it much easier for others to integrate OAuth apps. It feels like httr2 could be the right place for it, but happy to discuss this. This is my first PR for a public R package, apologies in advance if I have made errors or overlooked standards.👍