Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: vmxr
Title: VeloMetrix R Client
Version: 0.1.1
Version: 0.1.1.9000
Authors@R: c(
person(given = "Eric", family = "Novik", email = "eric@generable.com", role = c("aut", "cre")),
person(given = "Juho", family = "Timonen", role = "ctb"),
Expand All @@ -23,6 +23,7 @@ Imports:
cli,
curl,
httr2,
jsonlite,
rlang,
tibble,
vctrs
Expand All @@ -31,7 +32,6 @@ Suggests:
dplyr,
ggplot2,
httptest2,
jsonlite,
knitr,
rmarkdown,
testthat (>= 3.0.0),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export(vmx_fit_obs_vs_pred)
export(vmx_fit_subject_estimates)
export(vmx_fit_vpc)
export(vmx_health)
export(vmx_login)
export(vmx_model_build)
export(vmx_model_build_artifacts)
export(vmx_model_build_cancel)
Expand Down
13 changes: 13 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# vmxr 0.1.1.9000 (development)

* Native OIDC device-code authentication (GEN-2332). `vmx_login()` runs the
RFC 8628 device-code flow via `httr2::oauth_flow_device()` against the
Authentik provider (public client, `offline_access` scope for a refresh
token) and caches the token. `vmx_client()` now **auto-authenticates** from
that cache when no `VMX_API_TOKEN` / `token=` is set, refreshing silently and
prompting `vmx_login()` only when there's no usable cached token. The token
is cached as plain JSON at `~/.config/vmx/oidc-token.json` — the same path and
shape the `vmx` CLI uses (`0600`) — so one login serves both R and the CLI and
survives a fresh R session / pod restart. Configured via `VMX_OIDC_ISSUER`,
`VMX_OIDC_CLIENT_ID`, `VMX_OIDC_SCOPES`.

# vmxr 0.1.1

* Send upload `config_yaml` as inline YAML text, matching the current API form
Expand Down
16 changes: 11 additions & 5 deletions R/client.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
#' -> a classed error. The token is stored on the object but redacted in
#' [print()] and never logged.
#'
#' When no token is supplied (neither `token=` nor `VMX_API_TOKEN`), the client
#' **auto-authenticates** via OIDC: it reads the token cached by [vmx_login()]
#' (`~/.config/vmx/oidc-token.json`), refreshing it silently when expired, and
#' -- in an interactive session -- running [vmx_login()] if there is no usable
#' cached token. This requires `VMX_OIDC_ISSUER` + `VMX_OIDC_CLIENT_ID` to be
#' set; otherwise a `vmx_auth_error` names both auth methods.
#'
#' @param base_url API base URL. Defaults to `Sys.getenv("VMX_API_BASE_URL")`.
#' @param token Authentik personal access token (PAT). Defaults to
#' `Sys.getenv("VMX_API_TOKEN")`. Never hard-code a PAT in source.
#' `Sys.getenv("VMX_API_TOKEN")`, then to an OIDC access token (see
#' [vmx_login()]). Never hard-code a PAT in source.
#' @param ... Reserved for future options (timeouts, retries, user agent).
#'
#' @return An object of class `vmx_client`.
Expand All @@ -25,10 +33,8 @@ vmx_client <- function(base_url = NULL, token = NULL, ...) {
)
}
if (!nzchar(token)) {
vmx_abort(
"No API token. Set `token=` or the VMX_API_TOKEN env var.",
class = "vmx_auth_error"
)
# No PAT: fall back to OIDC device-code auth (cache -> refresh -> login).
token <- .vmx_client_bearer()
}

structure(
Expand Down
Loading