I develop my code on posit workbench. I knit a Rmd file and deploy (“publish with source code”) to rstudio-connect. The Rmd needs to authenticate my google account, and I use googledrive::drive_auth()
. Since I don’t want my auth token shared when I publish the source code, I encrypt the token following the steps outlined here to create an encrypted .rds file. When I want to authenticate, I decrypt the token and pass it to drive_auth
, but drive_auth
fails unless I include the original cached binary file in my deployed resources. Is there a way to prevent that behavior and use only the token that I have encrypted?
googledrive
version 2.1.1
gargle
version 1.5.2
First, to setup my auth, in an interactive session, I run the following code:
library(gargle)
library(googledrive)
Specify the gargle cache directory and other options.
Cache directory must be in the same directory as the Rmd file (analysis
) to publish to rstudio-connect.
dir.create(here::here("analysis/secrets"))
options(
gargle_oauth_cache = here::here("analysis/secrets"),
gargle_oauth_email = "[email protected]",
gargle_oauth_client_type = "web"
)
Do initial gdrive auth (interactive)
drive_auth() # this creates a binary file in the secrets directory
Use key to encrypt the drive oauth cached token.
Key created with gargle::secret_make_key()
and added to my .Renviron file.
I also add it to rstudio-connect’s {X} Vars, so it will be used as an environment variable.
Place the encrypted token in the same cache directory.
gargle::secret_write_rds(
drive_token(),
here::here("analysis/secrets/gdrive-token.rds"),
key = "GARGLE_ENCRYPT_KEY"
)
Then in a Rmd file (test.Rmd), I have:
library(gargle)
library(googledrive)
options(
gargle_oauth_cache = "secrets",
gargle_oauth_email = "[email protected]",
gargle_oauth_client_type = "web",
gargle_verbosity = "debug"
)
Code to check the directory when knitting and deployed.
getwd()
## [1] "opt/rstudio-connect/mnt/app"
list.files()
## [1] "manifest.json" "packrat" "secrets" "test.Rmd"
dir.exists(gargle::gargle_oauth_cache())
## [1] TRUE
Make sure the decrypted token is valid.
httr2::secret_has_key("GARGLE_ENCRYPT_KEY")
## [1] TRUE
gargle::secret_read_rds(
"secrets/gdrive-token.rds",
key = "GARGLE_ENCRYPT_KEY"
)
## <request>
## Auth token: Gargle2.0
drive_auth(
token = gargle::secret_read_rds(
"secrets/gdrive-token.rds",
key = "GARGLE_ENCRYPT_KEY"
)
)
- When I knit the file, it works.
- When I deploy (or “publish with source code”), it fails when I include only
secrets/gdrive-token.rds
as the additional resource file.
I get this generic error message:
Error in `drive_auth()`:
! Can't get Google credentials.
ℹ Are you running googledrive in a non-interactive session? Consider:
• Call `drive_deauth()` to prevent the attempt to get credentials.
• Call `drive_auth()` directly with all necessary specifics.
ℹ See gargle's "Non-interactive auth" vignette for more details:
ℹ <https://gargle.r-lib.org/articles/non-interactive-auth.html>
Backtrace:
1. googledrive::drive_auth(...)
Execution halted
Unable to render the deployed content: Rendering exited abnormally: exit status 1
- But if I deploy and include the original cached token binary file
secrets/<token binary>
, the deployment succeeds. Looking at the messages, though, it seems like there might be an issue with the cache? Why does it try to use the cache when I give it the token? And when it does try the cache, why does it try the cache that was given at first in the interactive session? Here are the debugging messages I get when the deployment succeeds:
drive_auth(
token = gargle::secret_read_rds(
"secrets/gdrive-token.rds",
key = "GARGLE_ENCRYPT_KEY"
)
)
## trying `token_fetch()`
## Trying `credentials_byo_oauth()` ...
## ! The `scopes` cannot be specified when user brings their own OAuth token.
## ℹ The `scopes` are already implicit in the token.
## ℹ Requested `scopes` are effectively ignored.
## ! Token's declared scopes are not the same as the requested scopes.
## ℹ Scopes declared in token: ...drive, ...userinfo.email
## ℹ Requested scopes: ...drive
## putting token into the cache:
## '<directory of gargle cache on posit-workbench, not rstudio-connect>'
## Warning caught by `token_fetch()`:
## cannot open compressed file
## '<directory of gargle cache on posit-workbench, not rstudio-connect>/<token binary>',
## probable reason 'No such file or directory'
## Error caught by `token_fetch()`:
## cannot open the connection
## trying `credentials_service_account()`
## Error caught by `token_fetch()`:
## Argument 'txt' must be a JSON string, URL or file.
## trying `credentials_external_account()`
## aws.ec2metadata not installed; can't detect whether running on EC2 instance
## trying `credentials_app_default()`
## Trying `credentials_gce()` ...
## ✖ We don't seem to be on GCE.
## trying `credentials_user_oauth2()`
## attempt to access internal gargle data from: googledrive
## Gargle2.0 initialize
## adding "userinfo.email" scope
## loading token from the cache
## email: '[email protected]'
## oauth client name: tidyverse-erato
## oauth client name: web
## oauth client id:
## <suppressed>
## scopes: ...drive, ...userinfo.email
## token(s) found in cache:
## <token binary>
## token we are looking for:
## <token binary>
## matching token found in the cache