Authentication
authentication.RmdIntroduction
The spdgt.auth package provides authentication utilities
for SpeedGoat APIs, designed primarily for package developers rather
than individual users. This package supports the following
scenarios:
- Interactive sessions: When users write and run code in an R console.
- Non-interactive sessions: When scripts are executed automatically, e.g., via a cron job.
- Graphical User Interfaces (GUI): For web-based GUIs like Shiny apps.
This vignette explains how to authenticate in each scenario and
demonstrates how to integrate spdgt.auth into your R
package.
Authentication scenarios
Interactive sessions
In interactive sessions, calling a function that requires authentication will redirect the user to the SpeedGoat login page. After logging in, users are instructed to return to R, where an authentication token is stored for the session.
- Token expiration: Tokens are refreshed automatically, and users are notified if this happens.
- Session persistence: Authentication is session-based and does not persist across R restarts.
Non-interactive sessions
For non-interactive sessions (e.g., cron jobs, CI pipelines),
authentication uses an API key instead of the browser-based OAuth flow.
Set the SPDGT_API_KEY environment variable in your
.Renviron file:
SPDGT_API_KEY=your-counts-api-key
auth_login() is called automatically on your first API
request — you do not need to call it yourself.
Note: API key authentication is currently only available for the counts realm. The telemetry realm uses OAuth for interactive sessions.
Graphical User Interfaces (GUI)
When a Shiny app runs behind ShinyProxy, authentication is handled via OpenID Connect (OIDC). ShinyProxy authenticates the user and passes a token to the app through environment variables. The package detects this automatically — no user action is required.
The counts realm checks the SHINYPROXY_OIDC_ACCESS_TOKEN
environment variable. The package also reads
SHINYPROXY_USERNAME to verify that the token matches the
logged-in user. OIDC for the telemetry realm is not yet supported in
ShinyProxy.
For more details on OIDC, see the OpenID Connect documentation.
Realms
Authentication is organized by realms. A realm is a
named group of APIs that share a single token authority. For example,
the "counts" realm includes the counts API and any
satellite APIs (e.g., sightability) that trust its tokens. Each realm
stores its own token, refresh token, expiration, and auth type
independently.
This means that authenticating against the counts API does not give you access to the telemetry API, and vice versa. Token refresh, expiration, and login are all handled per-realm.
All functions default to realm = "counts", so existing
code works without changes. To explicitly log in to a specific
realm:
# Log in to the telemetry realm
auth_login(realm = "telemetry")In most cases you do not need to call auth_login()
yourself — it is called automatically when you make your first API
request to a given realm.
Telemetry realm
The telemetry realm (marks.spdgt.com) has its own token
database, separate from counts. A token from one realm does not work on
the other. The package handles this automatically — when you call a
telemetry API function (e.g., from spdgt.telem),
authentication targets telemetry-api directly.
Available auth methods for the telemetry realm:
| Method | How | Status |
|---|---|---|
| OAuth | Interactive browser flow | Working |
| OIDC | ShinyProxy apps deployed against telemetry-api | Working |
| API key |
SPDGT_API_KEY_TELEMETRY env var |
Pending server-side support |
For interactive use, authentication is seamless — the browser opens
telemetry-api’s login page. For ShinyProxy apps, the OIDC token is
detected automatically from
SHINYPROXY_OIDC_ACCESS_TOKEN.
Important: The telemetry realm is registered by
spdgt.telem (or spdgt.telem.admin). You must
load one of these packages before calling telemetry API functions:
library(spdgt.telem)
# Now telemetry API calls will authenticate automaticallyIntegrating spdgt.auth into your package
Registering APIs
In your package’s R/zzz.R, use add_api() to
register your API endpoints. The spdgt.auth package
initializes a shared auth state at load time; downstream packages
register their APIs into it.
.onLoad <- function(libname, pkgname) {
# Register API — creates a new "my-api" realm
spdgt.auth::add_api("my-api", "https://my-api.spdgt.com/api")
}If your API shares a token with an existing realm (e.g., a satellite
API that trusts the counts API’s tokens), use the realm
parameter:
.onLoad <- function(libname, pkgname) {
# Join the "counts" realm — shares its token
spdgt.auth::add_api(
"sightability",
"https://sightability.spdgt.com/api",
realm = "counts"
)
}Creating an API function
Define a function in your package that performs an authenticated API call:
#' Get projects from the API
#'
#' @param ... Additional arguments passed to `api_get()`
#' @return A data frame of projects
#' @export
get_projects <- function(...) {
spdgt.auth::api_get(
api_name = "my-api",
endpoint = "projects",
...
)
}Authentication happens automatically — api_perform()
(called internally by api_get()) detects the realm from the
request and triggers login if needed. In interactive sessions this opens
an OAuth browser flow; in non-interactive sessions credentials must
already be configured (API key or OIDC token).
Using multiple realms
When your package needs to call APIs in different realms, register each API with the appropriate realm:
.onLoad <- function(libname, pkgname) {
# These two APIs share the "counts" token
spdgt.auth::add_api("counts", "https://counts.spdgt.com/api")
spdgt.auth::add_api(
"sightability",
"https://sightability.spdgt.com/api",
realm = "counts"
)
# This API has its own realm and token
spdgt.auth::add_api("telemetry", "https://telemetry.spdgt.com/api")
}When making requests, api_perform() automatically
resolves the correct realm and token from the request. No extra work is
needed in your API functions.
Token forwarding (Plumber APIs)
For Plumber APIs that receive authenticated requests and need to make
downstream API calls, use auth_use_token():
#* @filter auth
function(req, res) {
if (spdgt.auth::auth_use_token(
req$HEADERS["authorization"],
validate = TRUE
)) {
plumber::forward()
} else {
res$status <- 401
list(error = "Unauthorized")
}
}To forward a token to a non-default realm:
spdgt.auth::auth_use_token(token, realm = "telemetry")Error handling
api_perform() aborts with a structured error on any
non-2xx response. The error message includes the HTTP status code, the
server’s error message, and any field-level validation errors.
For example, a 422 validation error produces:
Error:
! API request failed with status 422 for realm "counts"
x The given data was invalid.
i email: The email field is required.
i name: The name field is required.
On a 401 response, api_perform() automatically attempts
to refresh the token and retry the request once. If the retry also
fails, it aborts with the error.
Troubleshooting
A few ideas to help with authentication errors:
- Try restarting R
- Try clearing your browser history or use incognito mode
- Try using a different browser
- Call
auth_logout()to clear all stored credentials, then try again - To clear a single realm:
auth_logout(realm = "counts")
Summary
The spdgt.auth package simplifies authentication for
SpeedGoat APIs in R. By following the examples in this vignette, you
can:
- Authenticate users in interactive, non-interactive, and GUI contexts.
- Manage authentication seamlessly across multiple APIs and realms.
- Handle errors from API responses with structured, actionable messages.
For more information, refer to the package documentation.