This document serves as a
walkthrough of how otndata’s authentication framework is
set up. I welcome contributions, but I worry that this flow is a bit
opaque and might trip someone up right out of the gate. As such, it is
mainly geared toward those who wish to contribute to, or develop with,
otndata. This package is intended to interface with the
Plone API, so what’s written here will be a mix of things you can find
in that documentation and otndata’s wrapper on top of
that.
In order to log in, we must POST our username and password to the
Plone CMS REST API. To get and access user credentials, a lot of
inspiration (and code!) has been taken from the tidycensus
package, which writes credentials to the user’s
.Renviron file for use across sessions. The upside to this
method is that it is easy to code in base R; the downside is that
secrets are stored in plain text on the user’s machine. This is
universally considered unacceptable, but I’ve decided that interacting
with telemetry data low-stakes enough to take the risk.
To allow users to store credentials for multiple networks,
credentials are written to OTN_PASS.{network} and
OTN_USER.{network} environmental variables. Again, these
are stored in plain text in the users .Renviron file if the
user wishes to use them across sessions. When an R session is started,
the .Renviron file is loaded along with other system
variables, meaning the credentials can be accessed via
Sys.getenv("OTN_USER.{network}") or
Sys.getenv("OTN_PASS.{network}").
“Setting” credentials takes a lot of inspiration from ggplot2::set_theme,
which uses a “ggplot_global” environment to hold user-set
options for ggplot2:
Environment that holds various global variables and settings for ggplot, such as the current theme. It is not exported and should not be directly manipulated by other packages.
ggplot_global <- new.env(parent = emptyenv())
Mirroring this, we have an otn_global
envrionment. You can see what’s inside of it like so:
If you use devtools::load_all(),
you can interact with it like you would an R list.
otn_global[["fake_info"]] <- 1234
ls(otn_global)
#> [1] "fake_info"
otn_global$fake_info
#> [1] 1234It is in this way that we manage our credentials within the R session.
When the user runs otn_login("{network}"), we first use
network to figure out what the URL of the specific Plone
instance is. This URL is placed
into the otn_global environment under
otn_global${network}$server_url. We can now think of
otn_global${network} as a list that will hold information
about the server, including our to-be-found session token.
Next, otndata looks for the
OTN_USER.{network} and OTN_PASS.{network}
environment variables and runs a temporary version of
otn_set_credentials if they aren’t found. If they aren’t
found, this places your credentials in your current R session’s
environment in plain text such that these will be wiped when you close
or restart R.
Finally, we
POST our username (as “login”) and password (as “password”) to the Plone
CMS using the credentials we’ve grabbed and server URL we’ve sussed
out. If the login is successful an authorization token is returned that
we must attach to all requests that need the user to be logged in.
otndata places this token in the otn_global
environment under its respective network. We wind up with something that
looks like this, assuming you’ve just logged into the ACT Network’s data portal:
#> ✔ Login successful!
ls(otndata:::otn_global)
#> [1] "act" "otn_devel"
otndata:::otn_global$act
#> $server_url
#> [1] "https://data.theactnetwork.com"
#>
#> $token
#> [1] "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJvYnJpZW4iLCJleHAiOjE3OTIyMDQ3NTUsImZ1bGxuYW1lIjoiTWlrZSBPJ0JyaWVuIn0.ZndqyvzxtJO-pH20HOHxKMOdu-mX8v-i5zfXs2aXxGQ"Setting thing up in this way allows us to:
As server information and the authentication token are stored under
the network, we can log into multiple networks in the same session and
extract the token via otn_global[["{network}"]]$token. Note
that while I’m using otndata::: here, you do not need to do
this in the code or while developing.
otn_login("otn_devel")
#> ✔ Login successful!
ls(otndata:::otn_global)
#> [1] "act" "otn_devel"
otndata:::otn_global$otn_devel
#> $server_url
#> [1] "https://members.devel.oceantrack.org"
#>
#> $token
#> [1] "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtb2JyaWVuIiwiZXhwIjoxNzkyMjA0NzU1LCJmdWxsbmFtZSI6Ik1pa2UgTydCcmllbiJ9.Kll_dgk_zsWfDa28IquaZvGca2ojlkDIwnNnan2zr2E"
otndata:::otn_global[["otn_devel"]]$token
#> [1] "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtb2JyaWVuIiwiZXhwIjoxNzkyMjA0NzU1LCJmdWxsbmFtZSI6Ik1pa2UgTydCcmllbiJ9.Kll_dgk_zsWfDa28IquaZvGca2ojlkDIwnNnan2zr2E"