R/AuthState-class.R

Defines functions make_package_hint init_AuthState

Documented in init_AuthState

#' Create an AuthState
#'
#' Constructor function for objects of class [AuthState].
#'
#' @param package Package name, an optional string. It is recommended to record
#'   the name of the package whose auth state is being managed. Ultimately, this
#'   may be used in some downstream messaging.
#' @param api_key Optional. API key (a string). Some APIs accept unauthorized,
#'   "token-free" requests for public resources, but only if the request
#'   includes an API key.
#' @param auth_active Logical. `TRUE` means requests should include a token (and
#'   probably not an API key). `FALSE` means requests should include an API key
#'   (and probably not a token).
#' @param cred Credentials. Typically populated indirectly via [token_fetch()].
#' @inheritParams gargle2.0_token
#'
#' @return An object of class [AuthState].
#' @export
#' @examples
#' my_client <- gargle_oauth_client(
#'   id = "some_long_client_id",
#'   secret = "ssshhhhh_its_a_secret",
#'   name = "my-nifty-oauth-client"
#' )
#'
#' init_AuthState(
#'   package = "my_package",
#'   client = my_client,
#'   api_key = "api_key_api_key_api_key",
#' )
init_AuthState <- function(package = NA_character_,
                           client = NULL,
                           api_key = NULL,
                           auth_active = TRUE,
                           cred = NULL,
                           app = deprecated()) {
  if (lifecycle::is_present(app)) {
    lifecycle::deprecate_soft(
      "1.5.0",
      "init_AuthState(app)",
      "init_AuthState(client)"
    )
    client <- app
  }

  AuthState$new(
    package     = package,
    client      = client,
    api_key     = api_key,
    auth_active = auth_active,
    cred        = cred
  )
}

#' Authorization state
#'
#' @description
#' An `AuthState` object manages an authorization state, typically on behalf of
#' a wrapper package that makes requests to a Google API.
#'

#' The `vignette("gargle-auth-in-client-package)` describes a design for wrapper
#' packages that relies on an `AuthState` object. This state can then be
#' incorporated into the package's requests for tokens and can control the
#' inclusion of tokens in requests to the target API.
#'
#'   * `api_key` is the simplest way to associate a request with a specific
#'     Google Cloud Platform [project](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy#projects).
#'     A few calls to certain APIs, e.g. reading a public Sheet, can succeed
#'     with an API key, but this is the exception.
#'   * `client` is an OAuth client ID (and secret) associated with a specific
#'     Google Cloud Platform [project](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy#projects).
#'     This is used in the OAuth flow, in which an authenticated user authorizes
#'     the client to access or manipulate data on their behalf.
#'   * `auth_active` reflects whether outgoing requests will be authorized by an
#'     authenticated user or are unauthorized requests for public resources.
#'     These two states correspond to sending a request with a token versus an
#'     API key, respectively.
#'   * `cred` is where the current token is cached within a session, once one
#'     has been fetched. It is generally assumed to be an instance of
#'     [`httr::TokenServiceAccount`][httr::Token-class] or
#'     [`httr::Token2.0`][httr::Token-class] (or a subclass thereof), probably
#'     obtained via [token_fetch()] (or one of its constituent credential
#'     fetching functions).
#'
#' An `AuthState` should be created through the constructor function
#' [init_AuthState()], which has more details on the arguments.
#'
#' @param package Package name.
#' @param client An OAuth client.
#' @param api_key An API key.
#' @param auth_active Logical, indicating whether auth is active.
#' @param cred Credentials.
#' @param app `r lifecycle::badge('deprecated')` Use `client` instead.
#'
#' @export
#' @name AuthState-class
AuthState <- R6::R6Class("AuthState", list(
  #' @field package Package name.
  package = NULL,
  #' @field client An OAuth client.
  client = NULL,
  #' @field app `r lifecycle::badge('deprecated')` Use `client` instead.
  app = NULL,
  #' @field api_key An API key.
  api_key = NULL,
  #' @field auth_active Logical, indicating whether auth is active.
  auth_active = NULL,
  #' @field cred Credentials.
  cred = NULL,
  #' @description Create a new AuthState
  #' @details For more details on the parameters, see [init_AuthState()]
  initialize = function(package = NA_character_,
                        client = NULL,
                        api_key = NULL,
                        auth_active = TRUE,
                        cred = NULL,
                        app = deprecated()) {
    gargle_debug("initializing AuthState")
    if (lifecycle::is_present(app)) {
      # I'm using deprecate_warn() intentionally here. If I use
      # deprecate_soft(), you don't see the warning for a call to
      # AuthState$new(app). Most folks should be instantiating through
      # init_AuthState() anyway, so anyone who sees this warning probably needs
      # to see it.
      lifecycle::deprecate_warn(
        "1.5.0",
        "AuthState$initialize(app)",
        "AuthState$initialize(client)"
      )
      client <- app
    }
    stopifnot(
      is_scalar_character(package),
      is.null(client) || is.oauth_app(client),
      is.null(api_key) || is_string(api_key),
      is_bool(auth_active),
      is.null(cred) || inherits(cred, "Token2.0")
    )
    self$package     <- package
    self$client      <- client
    # for backwards compatibility; could eventually be removed
    self$app         <- client
    self$api_key     <- api_key
    self$auth_active <- auth_active
    self$cred        <- cred
    self
  },
  #' @description Format an AuthState
  #' @param ... Not used.
  format = function(...) {
    x <- list(
      package     = cli::format_inline("{.pkg {self$package}}"),
      client      = self$client$name,
      api_key     = obfuscate(self$api_key),
      auth_active = self$auth_active,
      credentials = cli::format_inline("{.cls {class(self$cred)[[1]]}}")
    )
    c(
      cli::cli_format_method(
        cli::cli_h1("<AuthState (via {.pkg gargle})>")
      ),
      glue("{fr(names(x))}: {fl(x)}")
    )
  },
  #' @description Set the OAuth client
  set_client = function(client) {
    stopifnot(is.null(client) || is.oauth_app(client))
    self$client <- client
    invisible(self)
  },
  #' @description `r lifecycle::badge('deprecated')` Deprecated method to set
  #'   the OAuth client
  set_app = function(app) {
    lifecycle::deprecate_soft(
      "1.5.0",
      "AuthState$set_app()",
      "AuthState$set_client()",
      details = make_package_hint(self$package)
    )
    # needed for backwards compatibility, as long as there are packages out
    # there consulting .auth$app
    self$app <- app
    self$set_client(client = app)
  },
  #' @description Set the API key
  #' @param value An API key.
  set_api_key = function(value) {
    stopifnot(is.null(value) || is_string(value))
    self$api_key <- value
    invisible(self)
  },
  #' @description Set whether auth is (in)active
  #' @param value Logical, indicating whether to send requests authorized with
  #'   user credentials.
  set_auth_active = function(value) {
    stopifnot(isTRUE(value) || isFALSE(value))
    self$auth_active <- value
    invisible(self)
  },
  #' @description Set credentials
  #' @param cred User credentials.
  set_cred = function(cred) {
    self$cred <- cred
    invisible(self)
  },
  #' @description Clear credentials
  clear_cred = function() {
    self$set_cred(NULL)
  },
  #' @description Get credentials
  get_cred = function() {
    self$cred
  },
  #' @description Report if we have credentials
  has_cred = function() {
    ## FIXME(jennybc): how should this interact with auth_active? should it?
    !is.null(self$cred)
  }
))

make_package_hint <- function(pkg) {
  hint <- NULL
  if (is_string(pkg)) {
    hint <- glue("
      This probably needs to be addressed in the {pkg} package.")
    url <- pkg_url_bug(pkg)
    if (!is.null(url)) {
      hint <- c(hint, glue("Please report the issue at <{url}>."))
    }
  }
  hint
}
ropenscilabs/gauth documentation built on Sept. 11, 2023, 6:20 a.m.