knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(gargle)
Over the course of several releases (v1.3.0, v1.4.0, and v1.5.0), gargle has shifted to using an OAuth client in the user flow facilitated by gargle::credentials_user_oauth2()
, instead the previous OAuth "app".
This is a more than just a vocabulary change (but it is also a vocabulary change).
This vignette explains what actually changed and how wrapper packages should adjust.
In 2022, Google partially deprecated the out-of-band (OOB) OAuth flow. The OOB flow is used by R users who are working with Google APIs and who use R in the browser, such as via RStudio Server, Posit Workbench, Posit Cloud, or Google Colaboratory.
Conventional OOB auth still works under certain conditions, for example, if the OAuth client is associated with a GCP project that is in testing mode or that is internal to a Google Workspace. But conventional OOB is no longer supported for projects that serve external users that are in production mode. In particular, this means that conventional OOB is no longer supported for the GCP project that has historically made auth "just work" for casual users of packages such as googledrive, googlesheets4, and bigrquery. The default OAuth client used by these package no longer works with conventional OOB.
In response, as of v1.3.0, gargle implements a new variant of OOB, called pseudo-OOB, to continue to provide a user-friendly auth flow for googledrive/googlesheets4/bigrquery on RStudio Server/Posit Workbench/Posit Cloud/Google Colaboratory.
The pseudo-OOB flow is also available for other developers to use.
This flow is triggered when use_oob = TRUE
(an existing convention in gargle and gargle-using packages) and the configured OAuth client is of the web type (when creating an OAuth client, this is called the "Web application" type).
```{=html}
FALSE | TRUE | ||
---|---|---|---|
client type | installed | use httpuv to spin up a temporary web server |
conventional OOB |
web | --not possible-- | pseudo-OOB |
In the past, gargle basically assumed that every OAuth client was of the *installed* type (when creating an OAuth client, this is called the "Desktop app" type). Therefore, the introduction of pseudo-OOB meant that gargle had to learn about different OAuth client types (web vs. installed). And that didn't play well with `httr::oauth_app()`, which gargle had been using to store the client ID and secret. That's why there is a new S3 class, `"gargle_oauth_client"`, with a constructor of the same name. Since more information is now necessary to instantiate a client (e.g. its type and, potentially, redirect URIs), the recommended way to create a client is to provide JSON downloaded from the GCP console to `gargle_oauth_client_from_json()`. Since we had to introduce a new S3 class and supporting functions, we also took this chance to make the vocabulary pivot from "OAuth app" to "OAuth client". Google's documentation has always talked about the "OAuth client", so this is more natural. This vocabulary is also more future-facing, anticipating the day when gargle might shift from httr to httr2, which uses `httr2:oauth_client()`. As a bridging measure, the `"gargle_oauth_client"` class currently inherits from httr's `"oauth_app"`, but this probably won't be true in the long-term. ### How to instantiate an OAuth client in R If you do auth via gargle, here are some recommended changes: 1. Stop using `httr::oauth_app()` or `gargle::oauth_app_from_json()` to instantiate an OAuth client. 2. Start using `gargle_oauth_client_from_json()` (strongly recommended) or `gargle_oauth_client()` instead. This advice applies to anything you do inside your package and also to what you encourage and document for your users. gargle ships with JSON files for two non-functional OAuth clients, just to make this all more concrete: ```r (path_to_installed_client <- system.file( "extdata", "client_secret_installed.googleusercontent.com.json", package = "gargle" )) jsonlite::prettify(scan(path_to_installed_client, what = character())) (client <- gargle_oauth_client_from_json(path_to_installed_client)) class(client) (path_to_web_client <- system.file( "extdata", "client_secret_web.googleusercontent.com.json", package = "gargle" )) jsonlite::prettify(scan(path_to_web_client, what = character())) (client <- gargle_oauth_client_from_json(path_to_web_client)) class(client)
Notice the difference in the JSON for the installed vs. web client.
Note the class of the client
object, the new type
field, and the redirect_uris
.
AuthState
classThere are two gargle classes that are impacted by the OAuth-app-to-client switch: AuthState
and Gargle2.0
.
We cover AuthState
here and Gargle2.0
in the next section.
If a wrapper package follows the design laid out in vignette("gargle-auth-in-client-package")
, it will use an instance of AuthState
to manage the package's auth state.
Let's assume that internal object is named .auth
, which it usually is.
Here are the changes you need to know about in AuthState
:
app
field is deprecated, in favor of a new field client
.
If you request .auth$app
, there will be a deprecation message and the
client
field is returned.$set_app()
method is deprecated, in favor of a new $set_client()
method. If you call .auth$set_app()
, there will be a deprecation message and
the input is used, instead, to set the client
field.app
argument of the init_AuthState()
constructor is deprecated in
favor of the new client
argument. If you call init_AuthState(app = x)
,
there will be a deprecation message and the input x
is used as the
client
argument instead.Here are the changes you probably need to make in your package:
PKG_auth_configure()
, should
become client
(which is new). Move the existing app
argument to the last
position and deprecate it.PKG_oauth_app()
(the function to reveal the user's configured
OAuth client).PKG_oauth_client()
to replace PKG_oauth_app()
.Here's how googledrive::drive_auth_configure()
and googledrive::drive_oauth_client()
looked before and after the transition:
# BEFORE drive_auth_configure <- function(app, path, api_key) { # not showing this code .auth$set_app(app) # more code we're not showing } drive_oauth_app <- function() .auth$app # AFTER drive_auth_configure <- function(client, path, api_key, app = deprecated()) { if (lifecycle::is_present(app)) { lifecycle::deprecate_warn( "2.1.0", "drive_auth_configure(app)", "drive_auth_configure(client)" ) drive_auth_configure(client = app, path = path, api_key = api_key) } # not showing this code .auth$set_client(client) # more code we're not showing } drive_oauth_client <- function() .auth$client drive_oauth_app <- function() { lifecycle::deprecate_warn( "2.1.0", "drive_oauth_app()", "drive_oauth_client()" ) drive_oauth_client() }
The approach above follows various conventions explained in vignette("communicate", package = "lifecycle")
.
If you also choose to use the lifecycle package to assist in this process, usethis::use_lifecycle()
function does some helpful one-time setup in your package:
usethis::use_lifecycle()
The roxygen documentation helpers in gargle assume PKG_auth_configure()
is adapted as shown above:
PREFIX_auth_configure_description()
crosslinks to PREFIX_oauth_client()
now, not PREFIX_oauth_app()
.PREFIX_auth_configure_params()
documents the client
argumentPREFIX_auth_configure_params()
uses a lifecycle badge and text to
communicate that app
is deprecated.PREFIX_auth_configure_params()
crosslinks to
gargle::gargle_oauth_client_from_json()
which requires gargle (>= 1.3.0)Gargle2.0
classGargle2.0
is the second gargle class that is impacted by the OAuth-app-to-client switch.
Here are the changes you probably need to make in your package:
Inside PKG_auth()
, you presumably call gargle::token_fetch()
. If you are
passing app = <SOMETHING>
, change that to client = <SOMETHING>
. Neither
app
nor client
are formal arguments of gargle::token_fetch()
, instead,
these are intended for eventual use by gargle::credentials_user_oauth2()
.
Here's a sketch of how this looks in googledrive::drive_auth()
:
r
drive_auth <- function(...) {
# code not shown
cred <- gargle::token_fetch(
scopes = scopes,
# app = drive_oauth_client() %||% <BUILT_IN_DEFAULT_CLIENT>, # BEFORE
client = drive_oauth_client() %||% <BUILT_IN_DEFAULT_CLIENT>, # AFTER
email = email,
path = path,
package = "googledrive",
cache = cache,
use_oob = use_oob,
token = token
)
# code not shown
}
If you ever call gargle::credentials_user_oauth2()
directly, use the new
client
argument instead of the deprecated app
argument.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.