Transition from OAuth app to OAuth client
Source:vignettes/oauth-client-not-app.Rmd
oauth-client-not-app.RmdIn 2023, over the course of versions 1.3.0, 1.4.0, and 1.5.0, gargle
switched from an OAuth “app” to an OAuth client in the
user flow facilitated by credentials_user_oauth2(). gargle
introduced client-based functions, arguments, and fields and deprecated
the app-based ones, while keeping them working.
That transition is now wrapping up, in two stages:
- Wrapper packages remove their own deprecated
appinterfaces, such as theappargument ofPKG_auth_configure()and thePKG_oauth_app()function, and stop using gargle’s deprecatedappinterfaces. The tidyverse and r-lib packages that wrap Google APIs are doing this now. - A future version of gargle removes its own deprecated
apparguments, methods, and functions.
This vignette is a checklist for maintainers of wrapper packages. A package that satisfies every item is ready for both stages.
Background
In 2022, Google partially deprecated the conventional out-of-band
(OOB) OAuth flow, which is how many people authenticate when they use R
in a browser, e.g., on Posit Workbench or Google Colaboratory. In
response, gargle 1.3.0 introduced a new flow, pseudo-OOB, that needs to
know the type of the OAuth client (“installed” or “web”) and its
redirect URIs. httr::oauth_app(), which gargle had been
using to store a client’s ID and secret, has no place for this
information, so gargle introduced a new S3 class,
"gargle_oauth_client". We also took the chance to adopt the
term “OAuth client”, which is what Google’s documentation has always
used (and what httr2 uses, in httr2::oauth_client()). For
now, the "gargle_oauth_client" class also inherits from
httr’s "oauth_app" class, but don’t rely on that.
See vignette("auth-from-web") for more about
conventional OOB, pseudo-OOB, and client types.
Checklist for wrapper packages
The examples below come from googledrive, whose prefix is
drive. They assume the design laid out in
vignette("gargle-auth-in-client-package"), in which an
internal AuthState object named .auth manages
the package’s auth state.
Create OAuth clients with gargle
Create an OAuth client with
gargle_oauth_client_from_json() (strongly recommended) or
gargle_oauth_client(). Don’t use
httr::oauth_app() or the deprecated
oauth_app_from_json(). Likewise, use
gargle_client(), not the deprecated
gargle_app(), and, in a tidyverse package, use
tidyverse_client(), not the deprecated
tidyverse_app(). This applies to your package’s code and to
what you show your users in examples and documentation.
The JSON downloaded from Google Cloud Console records the client’s type and redirect URIs, which is why it’s the preferred way to create a client. gargle ships with JSON for a non-functional client of each type. Here’s the “web” one:
path <- system.file(
"extdata", "client_secret_web.googleusercontent.com.json",
package = "gargle"
)
(client <- gargle_oauth_client_from_json(path))
#> <gargle_oauth_client>
#> name: a_project_59aa1797ab7ac4667a77617c3400999c
#> id: abc.apps.googleusercontent.com
#> secret: <REDACTED>
#> type: web
#> redirect_uris: https://www.tidyverse.org/google-callback/
class(client)
#> [1] "gargle_oauth_client" "oauth_app"The “installed” one is
client_secret_installed.googleusercontent.com.json, in the
same folder.
Use the client interfaces of
AuthState
- Create the auth state with
init_AuthState(client = ), notinit_AuthState(app = ). - Set the client with
.auth$set_client(), not.auth$set_app(). - Get the client from
.auth$client, not.auth$app. Theappfield exists only for backwards compatibility and$set_client()doesn’t update it.
Pass client to gargle’s token functions
Inside PKG_auth(), you presumably call
token_fetch(). Pass the OAuth client as
client, not app. Neither is a formal argument
of token_fetch(); it’s passed along to
credentials_user_oauth2(). Here’s a sketch of
googledrive::drive_auth():
drive_auth <- function(...) {
# code not shown
cred <- gargle::token_fetch(
scopes = scopes,
client = drive_oauth_client() %||% gargle::tidyverse_client(),
email = email,
path = path,
package = "googledrive",
cache = cache,
use_oob = use_oob,
token = token
)
# code not shown
}If you call credentials_user_oauth2() or
gargle2.0_token() directly, use their client
argument, not app.
Finish the user-facing functions
PKG_auth_configure() should take client as
its first argument and should have no app argument.
PKG_oauth_client() reveals the user’s configured client and
PKG_oauth_app() should not exist.
If your package deprecated app and
PKG_oauth_app() during the transition, it’s time to remove
them, along with the lifecycle::is_present(app) branch in
PKG_auth_configure(). Mention the removal in
NEWS.md. Here’s how this looks in googledrive:
drive_auth_configure <- function(client, path, api_key) {
# code not shown
.auth$set_client(client)
# code not shown
}
drive_oauth_client <- function() .auth$clientIf your package still has app as the main argument of
PKG_auth_configure(), you can’t remove it right away,
because your users may be relying on it. Introduce client
and PKG_oauth_client() and deprecate app and
PKG_oauth_app() for at least one release, following the
conventions in
vignette("communicate", package = "lifecycle").
Re-document with current gargle
gargle has unexported roxygen helpers that generate standard
documentation for auth functions, e.g.,
#' @eval gargle:::PREFIX_auth_configure_params(). In
current gargle:
-
PREFIX_auth_configure_description()crosslinks toPREFIX_oauth_client(), notPREFIX_oauth_app(). -
PREFIX_auth_configure_params()documents theclientargument and crosslinks togargle_oauth_client_from_json(), which requires gargle (>= 1.3.0). -
PREFIX_auth_configure_params()does not documentapp.
These helpers run when you re-document, so the gargle version
installed at that moment is what matters. Re-document with current
gargle after you remove app from
PKG_auth_configure(). An older gargle documents
app, which leads to an R CMD check warning
about a documented argument that’s not in the function’s usage.
What’s next in gargle
Once wrapper packages have made these changes, a future version of
gargle will remove its own deprecated app interfaces:
- The
appargument ofinit_AuthState(),AuthState$new(),credentials_user_oauth2(),gargle2.0_token(), andGargle2.0$new(). - The
appfield and the$set_app()method ofAuthState. -
oauth_app_from_json(),gargle_app(), andtidyverse_app().
A package that satisfies the checklist above won’t be affected.