If you have ever received an embarrassing message with a warning saying that you may have published your credentials or secrets when publishing your code, you know what I’m talking about. A very common mistake among noob coders is (temporarily) hardcoding passwords, tokens, secrets, that should never be shared with others, and… shared them.
- But, how can we handle a public or shared repository or reproducible code without doing so?
- Are there one-time-only safe solutions that can set our credentials once and for all without having to worry if they will be shared but will always work?
Today I’ll share with you a simple but effective approach.
I have several functions that live in my public lares library that use get_creds()
to fetch my secrets. Some of them are used as credentials to query databases, send emails with API services such as Mailgun, ping notifications using Slack‘s webhook, interacting with Google Sheets programatically, fetching Facebook and Twitter’s API stuff, Typeform, Github, Hubspot… I even have a portfolio performance report for my personal investments. If you check the code underneath, you won’t find credentials written anywhere but the code will actually work (for me and for anyone that uses the library). So, how can we accomplish this?
You may want to install the library to follow the examples:
install.packages("lares")
Credentials in YAML files
A YAML (acronym for “YAML Ain’t Markup Language”) file is a readable text file, commonly used to save configurations in a .yml
file. So, the trick here will be to post our credentials and secrets into a local YAML file, set RStudio to “know and remember” where it is saved, and call the file every time we use a credential-needed-function. That’s where get_creds comes in!
When using functions in lares that need credentials to actually work, you’ll notice there is always a creds
argument. In it, you’ll specify which service you need to fetch the secrets from and will be used in the function. Every time you call this function it will check for your .Renviron
file which will reveal where you have your .yml
file is and get a list with the credentials needed.
The first time you run the get_creds()
or use any function that has the creds
parameter, it will reactively ask you to set the path for tour YAML local file. This will be asked once and will be set for further R sessions. Remember, once you change this path you must reset your session for this setup to start working properly.
One-time only setup
Let’s run an example. If you already have a YAML file, you’re halfway there. If you already installed the lares
library, you already have a dummy file locally that will work just fine for this exercise; you can find it here: system.file("docs", "config.yml", package = "lares")
. If not, you can download the file and save it in your machine, wherever you wish to keep it.
1. Know the path: you must place the YAML file in a secure place and know its absolute path.
2. Set the path: load the library and call the get_creds()
function to set the directory. It will ask for the directory (not the file).
library(lares) # I'm using this function to get the library's dummy file directory # dirname(system.file("docs", "config.yml", package = "lares")) get_creds() Please, set your creds directory (one-time only step to set LARES_CREDS): Set directory where your config.yml file is saved:
/Library/Frameworks/R.framework/Versions/4.0/Resources/library/lares/docs ALL's SET! But, you must reset your session for it to work!
3. Reset your session: close your R/RStudio session and open it again. That should be all!
get_creds() NULL Warning message: In get_creds() : No credentials for NA found in your YML file. Try any of the following: 'service1', 'service2', 'service3'
We did it! As the warning message suggested, we can run the same function with one of the options available in our file. We’ll get a “list” object containing a (dummy) username, a repo, and a (fake) token, which can be now passed to any function without revealing its values. Awesome, right!?
get_creds("service3") $username [1] "myusername" $repo [1] "laresbernardo/lares" $token [1] "clntbjnrdbgvutdlkcecricuurtjtnbe"
Once you set your path, it will work from now on as long as you keep your file in the correct path. Of course, you don’t need the library to follow this logic, but feel free to use it and pass any feedback. I’ve been using this method for more than 3 years now, locally and in servers, with no issues so far.
BONUS 1: I frequently use 2-3 different computers all the time. To avoid having three different files (which will probably be recommended for security reasons), I only have one that syncs across all machines using Dropbox. So the path I’ve set is ~/Dropbox (Personal)/...
for all of them, regardless of their origin path names.
BONUS 2: You can manually change your .Renviron
file with usethis::edit_r_environ()
. Even though you could add all your credentials directly into this file, I’d rather use this post’s function because you can sync/share/admin a file to be used in several computers, you can group credentials by services and use them as lists, it has a warning that shows available services if you ask for the wrong (or none) service name, and I find it way friendlier.
Hope you find this approach useful next time you are in need of hiding your coding secrets! Reveal only what’s necessary and avoid shouting your credentials out to the web.
“Keep it secret, keep it safe” _Gandalf, the Grey
Happy coding!