internal-projects/10_token-encryption.md

OAuth Token Encryption for Travis

Heavily based on Noam Ross's instructions, here is how we obtain and encrypt the OAuth2.0 token used for testing, including on Travis-CI. Go here for the generic instructions on how to encrypt files on Travis.

In theory, you do this once. In reality, you do it more often because something goes wrong with your token. For example, with Google, you can only have 25 active tokens per account. In the course of googlesheets development, if one of us does something that results in several new token requests, we inevitably use up some of those 25. Over time, the token on Travis eventually falls off the end and we need to request and encrypt a new one. Here's how we do it.

0. Package design re: OAuth

The support provided by httr for OAuth token management is a beautiful thing. Specifically, the automagic usage of .httr-oauth for token caching. It is a great kindness to preserve the user's blissful ignorance about the OAuth2 flow for as long as possible.

However, I've decided it's pretty important to anticipate token workflows where the user asserts more control. You will want this for development purposes, e.g., in automated testing against the API, and for non-interactive use of the package, e.g. in scripts run in batch mode or in a Shiny app. Our initial OAuth flow relied solely on .httr-oauth but we eventually extended gs_auth() to make it easy to retrieve the current token and to put a pre-existing token into force, whether stored in an R object or in an .rds file.

1. Get a valid token and save it to file

Either do this from a working directory where the existing .httr-oauth contains the token you'd like to use or where there is NO existing .httr-oauth (or use gs_auth(new_user = TRUE)). In the latter case, when you are kicked to the browser, make sure you log in (or already logged in) to Google as the desired user. Pro tip: start with a fresh token or one near the beginning of the current 25-token sequence.

library(googlesheets)
token <- gs_auth()
saveRDS(token, file = "tests/testthat/googlesheets_token.rds")

This token is now in an obvious location for use in (local) testing via testthat.

2. Use the stored token in your tests

Prior to tests that require authorization, put this token into force.

gs_auth(token = "googlesheets_token.rds")

or, more realistically,

suppressMessages(gs_auth(token = "googlesheets_token.rds", verbose = FALSE))

A line like this will be at the top of your testing files, but presumably outside of actual tests or expectations. Here's an example. I also like to explicitly suspend authenticated access at the end of every testing file.

3. Encrypt the token file and send to Travis

This is the work necessary to use our existing token on Travis CI, securely.

First you need to install the Travis command line client:

gem install travis

Note: I had to do this as sudo.

Then log into your Travis account using your GitHub username and password.

travis login

Encrypt the token and send to Travis:

travis encrypt-file tests/testthat/googlesheets_token.rds --add

The --add option should add a decrypt command to your pre-existing .travis.yml file, along these lines:

before_install:
- openssl aes-256-cbc -K $encrypted_84f43c85142c_key -iv $encrypted_84f43c85142c_iv -in tests/testthat/googlesheets_token.rds.enc -out tests/testthat/googlesheets_token.rds -d

Double check that the token and encrypted token live in tests/testthat/ and that .travis.yml reflects the correct path. You will probably need to move the encrypted token into the correct directory and edit the path(s) in .travis.yml!

4. Carefully ignore, commit, and push

List the token tests/testthat/googlesheets_token.rds in .gitignore.

List the encrypted token tests/testthat/googlesheets_token.rds.enc in .Rbuildignore.

Do not mix these up.

Commit tests/testthat/googlesheets_token.rds.enc and your updated .travis.yml and push to Github. God willing, your tests that require authorization will now pass on Travis.

5. Special considerations when submitting to win-builder and CRAN

I use the token in tests/testthat/googlesheets_token.rds to authorize googlesheets in the vignette and was sort of horrified when I noticed that the vignette compiled just fine on http://win-builder.r-project.org. Since we do NOT (and cannot) Rbuildignore the unencrypted token, it was indeed being bundled up and sent along to win-builder. This got me concerned that, unless I took deliberate steps to omit the token in my eventual CRAN submission, it would be distributed with my package source.

I decided to make a "CRAN submission" branch and add tests/testthat/googlesheets_token.rds to .Rbuildignore. Of course, this breaks the build on Travis, so this little exercise will have to be done just prior to each CRAN submission and I'll need to take care to submit from the branch, not from master.

When I first submitted the "CRAN submission" branch to win-builder, I got an education on all the ways in which CRAN checks vignettes. In the absence of the token, googlesheets cannot do anything that requires authorization, which broke many chunks of the original vignette. How I worked around that is a separate story (TO DO: write that down).

Temporary summary of vignette analogue of testthat::skip_on_CRAN(): there are two issues. First, CRAN will attempt to build the vignette. Second, CRAN will attempt to run the R code extracted from the vignette. To solve the first problem, conditionally suppress chunk execution on CRAN. To solve the second problem, prevent the extraction of source code from some or all chunks with purl = FALSE. The vignette CRAN actually uses is the one you build and submit, so these measures aren't as harmful as you might fear.

For CRAN updates, I still plan to vet them with win-builder and it's pretty clear that, if I allow the token to go, it's not kept private in any shape or form. So either I'll just tolerate the small chance this could come back to haunt me or I'll handle like a CRAN submission and submit from a branch in which I've Rbuildignored the unencrypted token.

Notes

2015-06-29 The token we had been using finally fell off the end of the 25-token stack. But the basic instructions did not work for me when attempting to encrypt the new token file. I got this:

Jennifers-2015-MacBook-Pro:googlesheets jenny$ travis encrypt-file tests/testthat/googlesheets_token.rds 
repository not known to https://api.travis-ci.com/: jennybc/gspreadr

The old name of this repo was somehow blocking me. The repo hasn't been called gspreadr for months! From this issue thread I learned to inspect and correct the Travis slug in .git/config. It now reads like so:

[travis]
    slug = jennybc/googlesheets

That allowed me to encrypt a new token file.



jennybc/googlesheets documentation built on Feb. 8, 2022, 11:48 p.m.