diff --git a/NEWS.md b/NEWS.md index ddb9d23..95feed6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -26,6 +26,10 @@ types) or a list of length 1. * New `workgroup_mandate_2fa()` provides ability to enable or disable mandatory two-factor authentication (2FA) in workgroups (#65). +* Expiry time is now stored alongside the authentication token (`token`). If a +token is expired, it will be removed from the Global Environment and +authentication will be attempted with username and password instead (#35). + # objr 0.1.1 * Set minimum versions for `dplyr` and `tidyr` dependencies (#32). diff --git a/R/assets.R b/R/assets.R index 5214b28..865fa33 100644 --- a/R/assets.R +++ b/R/assets.R @@ -20,6 +20,8 @@ #' #' @return A tibble #' +#' @family Asset functions +#' #' @export assets <- function(workspace_uuid, @@ -37,7 +39,8 @@ assets <- function(workspace_uuid, "There is currently a bug in the underlying API preventing", "users from selecting more than one asset type. See", "{.href [objr#53](https://github.com/ScotGovAnalysis/objr/issues/53)}", - "for more information."), + "for more information." + ), "i" = paste("To return all assets, use `type = list()` (default)."), "i" = paste("To return one asset type only, e.g. documents, use", "`type = list(\"document\")`. See `?assets` for all", @@ -75,6 +78,8 @@ assets <- function(workspace_uuid, #' #' @return A tibble #' +#' @family Asset functions +#' #' @export asset_info <- function(asset_uuid, @@ -108,6 +113,8 @@ asset_info <- function(asset_uuid, #' #' @return API response (invisibly) #' +#' @family Asset functions +#' #' @export delete_asset <- function(asset_uuid, @@ -144,6 +151,8 @@ delete_asset <- function(asset_uuid, #' #' @return API response (invisibly) #' +#' @family Asset functions +#' #' @export rename_asset <- function(asset_uuid, diff --git a/R/bypass_2fa.R b/R/bypass_2fa.R index 0716f8e..8d8d3bc 100644 --- a/R/bypass_2fa.R +++ b/R/bypass_2fa.R @@ -16,6 +16,8 @@ #' #' @return API response (invisibly) #' +#' @family Two-factor authentication functions +#' #' @export workgroup_bypass_2fa <- function(workgroup_uuid, @@ -63,6 +65,8 @@ workgroup_bypass_2fa <- function(workgroup_uuid, #' #' @return API response (invisibly) #' +#' @family Two-factor authentication functions +#' #' @export participant_bypass_2fa <- function(participant_uuid, @@ -106,6 +110,8 @@ participant_bypass_2fa <- function(participant_uuid, #' #' @return API response (invisibly) #' +#' @family Two-factor authentication functions +#' #' @export workgroup_mandate_2fa <- function(workgroup_uuid, diff --git a/R/comments.R b/R/comments.R index 549ae33..5392f39 100644 --- a/R/comments.R +++ b/R/comments.R @@ -21,6 +21,8 @@ #' comments() #' } #' +#' @family Comment functions +#' #' @export comments <- function(created_after = NULL, @@ -78,6 +80,8 @@ comments <- function(created_after = NULL, #' #' @return API response (invisibly) #' +#' @family Comment functions +#' #' @export new_thread <- function(workspace_uuid, @@ -121,6 +125,7 @@ new_thread <- function(workspace_uuid, #' #' @return API response (invisibly) #' +#' @family Comment functions #' @export new_reply <- function(thread_uuid, diff --git a/R/create_folder.R b/R/create_folder.R index 5716c8a..e942d3e 100644 --- a/R/create_folder.R +++ b/R/create_folder.R @@ -16,6 +16,8 @@ #' #' @return API response (invisibly) #' +#' @family Asset functions +#' #' @export create_folder <- function(folder_name, diff --git a/R/download.R b/R/download.R index 9e71760..4f6b590 100644 --- a/R/download.R +++ b/R/download.R @@ -51,7 +51,7 @@ download_helper <- function(document_uuid, # Read data from file path x <- read_temp(new_path, ...) - return(x) + x } @@ -84,6 +84,8 @@ download_helper <- function(document_uuid, #' #' @return Path to downloaded file (invisibly). #' +#' @family Read/write functions +#' #' @export download_file <- function(document_uuid, @@ -166,6 +168,8 @@ download_file_version <- function(document_uuid, #' #' @return A [tibble][tibble::tibble-package]. #' +#' @family Read/write functions +#' #' @export read_data <- function(document_uuid, diff --git a/R/mobile_auth.R b/R/mobile_auth.R index 5baa29c..c762b0a 100644 --- a/R/mobile_auth.R +++ b/R/mobile_auth.R @@ -15,6 +15,8 @@ #' * `mobileAuthLogin`: Has the user enabled login via Mobile Authenticator? #' * `mobileAuthRegistered`: Has the user registered a Mobile Authenticator? #' +#' @family Mobile authentication functions +#' #' @export mobile_auth_status <- function(use_proxy = FALSE) { @@ -49,6 +51,8 @@ mobile_auth_status <- function(use_proxy = FALSE) { #' #' @return API response (invisibly) #' +#' @family Mobile authentication functions +#' #' @export mobile_auth_login <- function(code = NULL, use_proxy = FALSE) { diff --git a/R/objr.R b/R/objr.R index e87e6e1..4dc9a45 100644 --- a/R/objr.R +++ b/R/objr.R @@ -1,6 +1,12 @@ #' Core request function #' #' @details +#' API authentication is handled automatically. See the +# nolint start: line_length_linter +#' [Authentication article](https://scotgovanalysis.github.io/objr/articles/authentication.html) +# nolint end +#' for more information. +#' #' More details on endpoints are available in the # nolint start: line_length_linter #' \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/}{API documentation}. @@ -8,13 +14,13 @@ #' #' @param endpoint The endpoint to append to the API server address. #' @param url_path A list of values to be added to the request URL path. -#' Values will be separated with `/`. +#' Values will be separated with `/`. #' @param url_query A list of named values to define query parameters. #' @param method HTTP method to use; e.g. `"GET"`, `"POST"`, `"PUT"`. -#' Defaults to `"GET"`. +#' Defaults to `"GET"`. #' @param body A list of named values to be passed to the request body. #' @param path Optional file path to save body of request (mainly used when -#' downloading files). +#' downloading files). #' @param accept Accept header. Defaults to `"application/json"`. #' @param content_type Content-Type header. Defaults to `"application/json"`. #' @param use_proxy Logical to indicate whether to use proxy. @@ -121,11 +127,24 @@ objr_auth <- function(req, call = error_call) } - if (exists("token", where = parent.frame())) { + token_exists <- exists("token", where = globalenv()) + + if (token_exists) { + if (get("token", pos = globalenv())$expiry < Sys.time()) { + remove("token", pos = globalenv()) + token_exists <- FALSE + cli::cli_inform(c( + "i" = "Existing authentication `token` has expired.", + "i" = "Re-trying authentication using username/password..." + )) + } + } + + if (token_exists) { httr2::req_headers( req, - Authorization = get("token", pos = parent.frame()) + Authorization = get("token", pos = globalenv())$value ) } else { @@ -147,7 +166,7 @@ objr_auth <- function(req, #' header. #' @param store_env The environment to bind the token to. #' -#' @return Returns the token invisibly. This function is primarily used +#' @return API response (invisibly). This function is primarily used #' for its side effect - an environment variable is created called "token". #' #' @noRd @@ -169,11 +188,12 @@ store_token <- function(response, call = error_call) } - token <- httr2::resp_header(response, "Authorization") + token <- list( + value = httr2::resp_header(response, "Authorization"), + expiry = Sys.time() + (20 * 60) + ) - if (!exists("token", where = store_env)) { - rlang::env_poke(env = store_env, nm = "token", value = token) - } + rlang::env_poke(env = store_env, nm = "token", value = token) invisible(response) diff --git a/R/participants.R b/R/participants.R index 0c63df3..1e4c097 100644 --- a/R/participants.R +++ b/R/participants.R @@ -11,6 +11,8 @@ #' #' @return A tibble #' +#' @seealso [add_participants()] +#' #' @export participants <- function(workspace_uuid, use_proxy = FALSE) { @@ -86,6 +88,8 @@ participants <- function(workspace_uuid, use_proxy = FALSE) { #' #' @return API response (invisibly) #' +#' @seealso [participants()] +#' #' @export add_participants <- function(workspace_uuid, diff --git a/R/upload.R b/R/upload.R index e81573d..2f8eaf6 100644 --- a/R/upload.R +++ b/R/upload.R @@ -26,6 +26,8 @@ #' #' @return API response (invisibly) #' +#' @family Read/write functions +#' #' @export upload_file <- function(file, @@ -150,6 +152,8 @@ upload_file_version <- function(file, #' #' @return API response (invisibly) #' +#' @family Read/write functions +#' #' @export write_data <- function(x, diff --git a/R/documents.R b/R/versions.R similarity index 97% rename from R/documents.R rename to R/versions.R index 4b019b1..50a3558 100644 --- a/R/documents.R +++ b/R/versions.R @@ -12,6 +12,8 @@ #' #' @return A tibble #' +#' @seealso [rollback_to_version()] +#' #' @export versions <- function(document_uuid, @@ -63,6 +65,8 @@ versions <- function(document_uuid, #' #' @return API response (invisibly) #' +#' @seealso [versions()] +#' #' @export rollback_to_version <- function(document_uuid, diff --git a/man/add_participants.Rd b/man/add_participants.Rd index f80fbe4..f870837 100644 --- a/man/add_participants.Rd +++ b/man/add_participants.Rd @@ -48,3 +48,6 @@ Add participant(s) to a workspace More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Participants/addParticipants}{API documentation}. } +\seealso{ +\code{\link[=participants]{participants()}} +} diff --git a/man/asset_info.Rd b/man/asset_info.Rd index 327336d..5c35c4c 100644 --- a/man/asset_info.Rd +++ b/man/asset_info.Rd @@ -21,3 +21,11 @@ Get asset information More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/getAssetByUuid}{API documentation}. } +\seealso{ +Other Asset functions: +\code{\link{assets}()}, +\code{\link{create_folder}()}, +\code{\link{delete_asset}()}, +\code{\link{rename_asset}()} +} +\concept{Asset functions} diff --git a/man/assets.Rd b/man/assets.Rd index fe18329..7b04c02 100644 --- a/man/assets.Rd +++ b/man/assets.Rd @@ -39,3 +39,11 @@ Get data frame of assets in workspace More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/getAssets}{API documentation}. } +\seealso{ +Other Asset functions: +\code{\link{asset_info}()}, +\code{\link{create_folder}()}, +\code{\link{delete_asset}()}, +\code{\link{rename_asset}()} +} +\concept{Asset functions} diff --git a/man/comments.Rd b/man/comments.Rd index 2e51e1b..3b6cd7a 100644 --- a/man/comments.Rd +++ b/man/comments.Rd @@ -47,3 +47,9 @@ comments() } } +\seealso{ +Other Comment functions: +\code{\link{new_reply}()}, +\code{\link{new_thread}()} +} +\concept{Comment functions} diff --git a/man/create_folder.Rd b/man/create_folder.Rd index 68c2fbd..12c24e9 100644 --- a/man/create_folder.Rd +++ b/man/create_folder.Rd @@ -35,3 +35,11 @@ Create a new folder More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/createFolder}{API documentation}. } +\seealso{ +Other Asset functions: +\code{\link{asset_info}()}, +\code{\link{assets}()}, +\code{\link{delete_asset}()}, +\code{\link{rename_asset}()} +} +\concept{Asset functions} diff --git a/man/delete_asset.Rd b/man/delete_asset.Rd index 9bbe6d0..45c5650 100644 --- a/man/delete_asset.Rd +++ b/man/delete_asset.Rd @@ -23,3 +23,11 @@ More details on this endpoint are available in the Note: This functionality is disabled in Scottish Government workspaces. } +\seealso{ +Other Asset functions: +\code{\link{asset_info}()}, +\code{\link{assets}()}, +\code{\link{create_folder}()}, +\code{\link{rename_asset}()} +} +\concept{Asset functions} diff --git a/man/download_file.Rd b/man/download_file.Rd index 6e423d1..ea724c4 100644 --- a/man/download_file.Rd +++ b/man/download_file.Rd @@ -53,3 +53,10 @@ API documentation: \item \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/downloadVersion}{\code{download_file_version}} } } +\seealso{ +Other Read/write functions: +\code{\link{read_data}()}, +\code{\link{upload_file}()}, +\code{\link{write_data}()} +} +\concept{Read/write functions} diff --git a/man/mobile_auth_login.Rd b/man/mobile_auth_login.Rd index c7e873c..5e4fb50 100644 --- a/man/mobile_auth_login.Rd +++ b/man/mobile_auth_login.Rd @@ -31,3 +31,8 @@ More information on mobile authentication can be found in More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/MobileAuth/login}{API documentation}. } +\seealso{ +Other Mobile authentication functions: +\code{\link{mobile_auth_status}()} +} +\concept{Mobile authentication functions} diff --git a/man/mobile_auth_status.Rd b/man/mobile_auth_status.Rd index 0f74854..e594efa 100644 --- a/man/mobile_auth_status.Rd +++ b/man/mobile_auth_status.Rd @@ -26,3 +26,8 @@ More information on mobile authentication can be found in More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/MobileAuth/getMobileAuthDetails}{API documentation}. } +\seealso{ +Other Mobile authentication functions: +\code{\link{mobile_auth_login}()} +} +\concept{Mobile authentication functions} diff --git a/man/new_reply.Rd b/man/new_reply.Rd index 133a2db..ef17a84 100644 --- a/man/new_reply.Rd +++ b/man/new_reply.Rd @@ -33,3 +33,9 @@ Create a new reply to a thread More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Comments/createReply}{API documentation}. } +\seealso{ +Other Comment functions: +\code{\link{comments}()}, +\code{\link{new_thread}()} +} +\concept{Comment functions} diff --git a/man/new_thread.Rd b/man/new_thread.Rd index a50ea5e..0cca90a 100644 --- a/man/new_thread.Rd +++ b/man/new_thread.Rd @@ -33,3 +33,9 @@ Create a new thread More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Comments/createThread}{API documentation}. } +\seealso{ +Other Comment functions: +\code{\link{comments}()}, +\code{\link{new_reply}()} +} +\concept{Comment functions} diff --git a/man/objr.Rd b/man/objr.Rd index c93a32c..48cb9c5 100644 --- a/man/objr.Rd +++ b/man/objr.Rd @@ -45,6 +45,10 @@ API response. Core request function } \details{ +API authentication is handled automatically. See the +\href{https://scotgovanalysis.github.io/objr/articles/authentication.html}{Authentication article} +for more information. + More details on endpoints are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/}{API documentation}. } diff --git a/man/participant_bypass_2fa.Rd b/man/participant_bypass_2fa.Rd index 9e3181c..93329fb 100644 --- a/man/participant_bypass_2fa.Rd +++ b/man/participant_bypass_2fa.Rd @@ -36,3 +36,9 @@ More information on two-factor authentication can be found in More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Participants/setParticipantBypassMfa}{API documentation}. } +\seealso{ +Other Two-factor authentication functions: +\code{\link{workgroup_bypass_2fa}()}, +\code{\link{workgroup_mandate_2fa}()} +} +\concept{Two-factor authentication functions} diff --git a/man/participants.Rd b/man/participants.Rd index 0e66ba2..bc123fb 100644 --- a/man/participants.Rd +++ b/man/participants.Rd @@ -21,3 +21,6 @@ Get data frame of workspace participants More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Participants/getParticipant}{API documentation}. } +\seealso{ +\code{\link[=add_participants]{add_participants()}} +} diff --git a/man/read_data.Rd b/man/read_data.Rd index 10146f3..11ab0ea 100644 --- a/man/read_data.Rd +++ b/man/read_data.Rd @@ -54,3 +54,10 @@ API documentation: \item \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/downloadVersion}{\code{read_data_version}} } } +\seealso{ +Other Read/write functions: +\code{\link{download_file}()}, +\code{\link{upload_file}()}, +\code{\link{write_data}()} +} +\concept{Read/write functions} diff --git a/man/rename_asset.Rd b/man/rename_asset.Rd index 195679b..77b8f7a 100644 --- a/man/rename_asset.Rd +++ b/man/rename_asset.Rd @@ -23,3 +23,11 @@ Rename an asset More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/updateAssetName}{API documentation}. } +\seealso{ +Other Asset functions: +\code{\link{asset_info}()}, +\code{\link{assets}()}, +\code{\link{create_folder}()}, +\code{\link{delete_asset}()} +} +\concept{Asset functions} diff --git a/man/rollback_to_version.Rd b/man/rollback_to_version.Rd index 77a2f23..2201d4d 100644 --- a/man/rollback_to_version.Rd +++ b/man/rollback_to_version.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/documents.R +% Please edit documentation in R/versions.R \name{rollback_to_version} \alias{rollback_to_version} \title{Rollback a document to a previous version} @@ -23,3 +23,6 @@ Rollback a document to a previous version More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/rollbackDocument}{API documentation}. } +\seealso{ +\code{\link[=versions]{versions()}} +} diff --git a/man/upload_file.Rd b/man/upload_file.Rd index 504415c..b22ee45 100644 --- a/man/upload_file.Rd +++ b/man/upload_file.Rd @@ -51,3 +51,10 @@ API documentation: \item \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/createDocumentVersion_1}{\code{upload_file_version}} } } +\seealso{ +Other Read/write functions: +\code{\link{download_file}()}, +\code{\link{read_data}()}, +\code{\link{write_data}()} +} +\concept{Read/write functions} diff --git a/man/versions.Rd b/man/versions.Rd index 55cb093..5f72a03 100644 --- a/man/versions.Rd +++ b/man/versions.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/documents.R +% Please edit documentation in R/versions.R \name{versions} \alias{versions} \title{Get data frame of document versions} @@ -25,3 +25,6 @@ Get data frame of document versions More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/getVersionsByDocumentUuid}{API documentation}. } +\seealso{ +\code{\link[=rollback_to_version]{rollback_to_version()}} +} diff --git a/man/workgroup_bypass_2fa.Rd b/man/workgroup_bypass_2fa.Rd index 627cd74..79d16bc 100644 --- a/man/workgroup_bypass_2fa.Rd +++ b/man/workgroup_bypass_2fa.Rd @@ -27,3 +27,9 @@ More information on two-factor authentication can be found in More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Workgroups/setWorkgroupMfaBypassAllowed}{API documentation}. } +\seealso{ +Other Two-factor authentication functions: +\code{\link{participant_bypass_2fa}()}, +\code{\link{workgroup_mandate_2fa}()} +} +\concept{Two-factor authentication functions} diff --git a/man/workgroup_mandate_2fa.Rd b/man/workgroup_mandate_2fa.Rd index f3e8c73..ffc8322 100644 --- a/man/workgroup_mandate_2fa.Rd +++ b/man/workgroup_mandate_2fa.Rd @@ -27,3 +27,9 @@ More information on two-factor authentication can be found in More details on this endpoint are available in the \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html#/Workgroups/setTwoStepMandatory}{API documentation}. } +\seealso{ +Other Two-factor authentication functions: +\code{\link{participant_bypass_2fa}()}, +\code{\link{workgroup_bypass_2fa}()} +} +\concept{Two-factor authentication functions} diff --git a/man/write_data.Rd b/man/write_data.Rd index 47c3eb8..46caf3e 100644 --- a/man/write_data.Rd +++ b/man/write_data.Rd @@ -77,3 +77,10 @@ API documentation: \item \href{https://secure.objectiveconnect.co.uk/publicapi/1/swagger-ui/index.html?configUrl=/publicapi/1/v3/api-docs/swagger-config#/Assets/createDocumentVersion_1}{\code{write_data_version}} } } +\seealso{ +Other Read/write functions: +\code{\link{download_file}()}, +\code{\link{read_data}()}, +\code{\link{upload_file}()} +} +\concept{Read/write functions} diff --git a/tests/testthat/test-objr.R b/tests/testthat/test-objr.R index f7b535e..a15501e 100644 --- a/tests/testthat/test-objr.R +++ b/tests/testthat/test-objr.R @@ -28,6 +28,7 @@ with_mock_api({ test_that("Valid response", { user <- objr("me", use_proxy = TRUE) + expect_s3_class(user, "httr2_response") expect_equal(httr2::resp_body_json(user)$uuid, "1234") }) @@ -42,16 +43,41 @@ test_that("Error if invalid request supplied", { req <- httr2::request("www.example.com") -test_that("httr2 request returned", { +test_that("Success if valid token exists", { - .GlobalEnv$token <- "test" # nolint: object_name_linter + .GlobalEnv$token <- list( # nolint: object_name_linter + value = "test", + expiry = structure(Sys.time() + 60, + class = c("POSIXct", "POSIXt")) + ) expect_s3_class(objr_auth(req), "httr2_request") + # expect_equal(objr_auth(req)$headers$Authorization, "test") rm(token, pos = .GlobalEnv) }) +test_that("Uses usr/pwd if token exists but is expired", { + + .GlobalEnv$token <- list( # nolint: object_name_linter + value = "test", + expiry = structure(Sys.time() - 60, + class = c("POSIXct", "POSIXt")) + ) + + req_auth <- suppressMessages(objr_auth(req)) + + # expect_true( + # grepl("^Basic", req_auth$headers$Authorization) + # ) + + expect_false( + exists("token", where = .GlobalEnv) + ) + +}) + # test_that("Correct authentication used", { # # expect_true(grepl("^Basic ", objr_auth(req)$headers$Authorization)) @@ -100,7 +126,13 @@ test_that("Response returned invisibly", { test_that("Environment value created successfully", { httr2::response(headers = list(Authorization = "test2")) |> store_token(store_env = environment()) + expect_true(exists("token")) + expect_type(get("token"), "list") + expect_equal(names(get("token")), c("value", "expiry")) + expect_equal(get("token")$value, "test2") + expect_type(get("token")$value, "character") + expect_s3_class(get("token")$expiry, "POSIXct") }) diff --git a/vignettes/authentication.Rmd b/vignettes/authentication.Rmd index 18f16c6..4ffaffd 100644 --- a/vignettes/authentication.Rmd +++ b/vignettes/authentication.Rmd @@ -15,11 +15,13 @@ knitr::opts_chunk$set( ## How authentication works -The first time you send a request, the API requires your Objective Connect user email address and password to authenticate. This is called "Basic" authentication. +The first time you send a request, the API requires your Objective Connect user email address and password to authenticate. -Each successful response from the API includes a token. This token can then be used to authenticate subsequent requests in your session, negating the need to repeatedly supply your email address and password. This is called "Session" authentication. +Each successful response from the API includes a token. This token can then be used to authenticate subsequent requests in your session, negating the need to repeatedly supply your email address and password. -The rest of this article details how to manage authentication when using the `objr` package, including handling [multi-factor authentication](#mfa) (including two-factor and mobile authentication). +Depending on your account and/or workspace settings, you may also be required to use two-factor or mobile authentication. + +The rest of this article details how to manage authentication when using the `objr` package. ## First request @@ -83,7 +85,13 @@ Where this object exists, `objr` will automatically use it to authenticate subse If this object doesn't exist, `objr` will resort to using your username and password, as it did for your [first request](#first-request). -Tokens become invalid when your session expires. If you have an invalid token in your environment, your API request will fail and you will be prompted to remove the token and try again using your username and password. +### Token expiry + +Tokens expire when they have been unused for 20 minutes or more. You can see the expiry time of your current token by viewing `token$expiry`. + +If you try to make a request with an expired token in your environment, it will be removed, and `objr` will try to use your username and password instead. + +Note that if you are using [mobile authentication](#mobileauth), this will still fail, and you should login again using `mobile_auth_login()` to generate a new token. ## Multi-factor authentication {#mfa} @@ -113,13 +121,13 @@ If you have both enabled mobile authentication and registered a mobile device, y You can either provide the authentication code from your mobile device directly to the function: ```{r login-1} -mobile_auth_status("123456") +mobile_auth_login("123456") ``` Or, if left empty, you will be prompted to enter your code in a pop-up window: ```{r login-2} -mobile_auth_status() +mobile_auth_login() ``` ```{r} @@ -138,6 +146,6 @@ cli::cli_alert_success( ) ``` -If login is successful, the token from the API response is stored automatically and used for [subsequent requests](#subsequent-requests). Tokens become invalid when your session expires. If you have an invalid token in your environment, your API request will fail and you will need to login using your mobile authenticator again. +If login is successful, the token from the API response is stored automatically and used for [subsequent requests](#subsequent-requests). [Tokens expire](#token-expiry) when they have been unused for 20 minutes or more - if this happens, you will need to login using your mobile authenticator again. Mobile authentication login attempts are limited to a maximum of 5 failures within a 5-minute interval. After 5 failed attempts, your Objective Connect account will be locked. To regain access, wait for 5 mins and then try logging in again.