When developing a package that uses secrets (API keys, OAuth tokens) and produces them (OAuth tokens, sensitive data),
- You want the secrets to be usable by you, collaborators and CI services, without being readable by anyone else;
- You want tests and checks (e.g. vignette building) that use the secrets to be turned off in environments where secrets won’t be available (CRAN, forks of your development repository).
Your general attitude should be to think about:
- what are my secrets (an API key, an OAuth2.0 access token and the refresh token, etc.) and where/how exactly are there used (in the query part of an URL? as a header? which header, Authentication or something else?) – packages like httr might abstract some of the complexity for you but you need to really know where secrets are used and could be leaked,
- what could go wrong (e.g. your token ending up being published),
- how to prevent that (save your unedited token outside of your package, make sure it is not printed in logs or present in package check artefacts),
- how to fix mistakes (how do you deactivate a token and how do you check no one used it in the meantime).
This book is about testing but security starts with how you develop your package. To better protect your users’ secret,
It might be best not to let users pass API keys as parameters. It’s best to have them save them in
.Renvironor e.g. using the keyring package. This way, API keys are not in scripts.
If the API you are working with lets you pass keys either in the request headers or query string, prefer to use request headers.
- With vcr make sure to configure vcr correctly.
- With httptest only the response body (and headers, but not by default) are recorded. If those contains secrets, refer to the documentation about redacting sensitive information.
- With webfakes you will be creating recorded responses yourself, make sure this process does not leak secrets. If you test something related to authentication, use fake secrets.
If the API you are interacting with uses OAuth for instance, make sure you are not leaking access tokens nor refresh tokens.
In that case you might want to gitignore the cassettes / mock files / recorded responses,
and skip the tests using them on continuous integration (e.g.
testthat::skip_on_ci() or something more involved).
You’d also Rbuildignore the cassettes / mock files / recorded responses, as you do not want to release them to CRAN.