From 1be4bf14cf16c1deb3713a7ccdcb6ed305f49fdb Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 15 Jul 2025 16:50:10 -0400 Subject: [PATCH 01/13] added support for optional audience param --- R/get.R | 34 ++++++++++---- tests/testthat/test-oauth.R | 88 +++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/R/get.R b/R/get.R index 6275b115..b1894936 100644 --- a/R/get.R +++ b/R/get.R @@ -643,6 +643,9 @@ get_procs <- function(src) { #' default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, this can #' be set to `urn:ietf:params:aws:token-type:credentials` for AWS integrations or #' `urn:posit:connect:api-key` for Connect API Key integrations. +#' @param audience Optional. The audience field used for credential exchange. +#' This must be a valid integration GUID. When provided, the specified requested +#' token type will be ignored. #' #' @examples #' \dontrun{ @@ -671,7 +674,8 @@ get_procs <- function(src) { get_oauth_credentials <- function( connect, user_session_token, - requested_token_type = NULL + requested_token_type = NULL, + audience = NULL ) { validate_R6_class(connect, "Connect") url <- v1_url("oauth", "integrations", "credentials") @@ -679,7 +683,8 @@ get_oauth_credentials <- function( grant_type = "urn:ietf:params:oauth:grant-type:token-exchange", subject_token_type = "urn:posit:connect:user-session-token", subject_token = user_session_token, - requested_token_type = requested_token_type + requested_token_type = requested_token_type, + audience = audience, ) connect$POST( url, @@ -701,6 +706,9 @@ get_oauth_credentials <- function( #' will default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, #' this can be set to `urn:ietf:params:aws:token-type:credentials` for AWS #' integrations or `urn:posit:connect:api-key` for Connect API Key integrations. +#' @param audience Optional. The audience field used for credential exchange. +#' This must be a valid integration GUID. When provided, the specified requested +#' token type will be ignored. #' #' @examples #' \dontrun{ @@ -728,7 +736,8 @@ get_oauth_credentials <- function( get_oauth_content_credentials <- function( connect, content_session_token = NULL, - requested_token_type = NULL + requested_token_type = NULL, + audience = NULL ) { validate_R6_class(connect, "Connect") error_if_less_than(connect$version, "2024.12.0") @@ -745,7 +754,8 @@ get_oauth_content_credentials <- function( grant_type = "urn:ietf:params:oauth:grant-type:token-exchange", subject_token_type = "urn:posit:connect:content-session-token", subject_token = content_session_token, - requested_token_type = requested_token_type + requested_token_type = requested_token_type, + audience = audience, ) connect$POST( url, @@ -761,6 +771,9 @@ get_oauth_content_credentials <- function( #' can only be obtained when the content is running on a Connect server. The token #' identifies the user who is viewing the content interactively on the Connect server. #' Read this value from the HTTP header: `Posit-Connect-User-Session-Token` +#' @param audience Optional. The audience field used for credential exchange. +#' This must be a valid integration GUID. When provided, the specified requested +#' token type will be ignored. #' #' @return The AWS credentials as a list with fields named `access_key_id`, #' `secret_access_key`, `session_token`, and `expiration`. @@ -806,12 +819,13 @@ get_oauth_content_credentials <- function( #' } #' #' @export -get_aws_credentials <- function(connect, user_session_token) { +get_aws_credentials <- function(connect, user_session_token, audience = NULL) { error_if_less_than(connect$version, "2025.03.0") response <- get_oauth_credentials( connect, user_session_token, - requested_token_type = "urn:ietf:params:aws:token-type:credentials" + requested_token_type = "urn:ietf:params:aws:token-type:credentials", + audience = audience, ) # Extract access token and decode it @@ -834,6 +848,9 @@ get_aws_credentials <- function(connect, user_session_token) { #' token identifies the service account integration previously configured by #' the publisher on the Connect server. Defaults to the value from the #' environment variable: `CONNECT_CONTENT_SESSION_TOKEN` +#' @param audience Optional. The audience field used for credential exchange. +#' This must be a valid integration GUID. When provided, the specified requested +#' token type will be ignored. #' #' @return The AWS credentials as a list with fields named `access_key_id`, #' `secret_access_key`, `session_token`, and `expiration`. @@ -873,12 +890,13 @@ get_aws_credentials <- function(connect, user_session_token) { #' } #' #' @export -get_aws_content_credentials <- function(connect, content_session_token = NULL) { +get_aws_content_credentials <- function(connect, content_session_token = NULL, audience = NULL) { error_if_less_than(connect$version, "2025.03.0") response <- get_oauth_content_credentials( connect, content_session_token, - requested_token_type = "urn:ietf:params:aws:token-type:credentials" + requested_token_type = "urn:ietf:params:aws:token-type:credentials", + audience = audience ) # Extract access token and decode it diff --git a/tests/testthat/test-oauth.R b/tests/testthat/test-oauth.R index 872ccb45..f83d5b3a 100644 --- a/tests/testthat/test-oauth.R +++ b/tests/testthat/test-oauth.R @@ -148,4 +148,92 @@ with_mock_api({ "Could not find the CONNECT_CONTENT_SESSION_TOKEN environment variable." ) }) + + test_that("we can retrieve the oauth user credentials and audience", { + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + credentials <- get_oauth_credentials( + client, + user_session_token = "user-session-token", + audience = "audience" + ) + expect_equal( + credentials, + list( + access_token = "user-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + }) + + test_that("we can retrieve the oauth content credentials with audience", { + withr::local_options(list(rlib_warning_verbosity = "verbose")) + + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + expect_warning( + credentials <- get_oauth_content_credentials( + client, + content_session_token = "content-session-token", + audience = "audience", + ) + ) + expect_equal( + credentials, + list( + access_token = "content-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + }) + + test_that("we can retrieve the AWS viewer credentials with audience", { + withr::local_options(list(rlib_warning_verbosity = "verbose")) + + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + expect_warning( + credentials <- get_aws_credentials( + client, + user_session_token = "user-session-token", + audience = "audience" + ) + ) + expect_equal( + credentials, + list( + access_key_id = "abc123", + secret_access_key = "def456", + session_token = "ghi789", + expiration = "2025-01-01T00:00:00Z" + ) + ) + }) + + test_that("we can retrieve the AWS content credentials with audience", { + # get_aws_content_credentials produces multiple warnings about the Posit + # Connect version; suppress these + withr::local_options(list(rlib_warning_verbosity = "quiet")) + + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + + credentials <- get_aws_content_credentials( + client, + content_session_token = "content-session-token", + audience = "audience" + ) + + expect_equal( + credentials, + list( + access_key_id = "abc123", + secret_access_key = "def456", + session_token = "ghi789", + expiration = "2025-01-01T00:00:00Z" + ) + ) + }) }) From d5aa6ac5411b90700de062115b2dcdff240aa335 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 15 Jul 2025 16:51:13 -0400 Subject: [PATCH 02/13] update docs --- man/get_aws_content_credentials.Rd | 10 +++++++++- man/get_aws_credentials.Rd | 6 +++++- man/get_oauth_content_credentials.Rd | 7 ++++++- man/get_oauth_credentials.Rd | 11 ++++++++++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/man/get_aws_content_credentials.Rd b/man/get_aws_content_credentials.Rd index 5c2f4711..14cf786d 100644 --- a/man/get_aws_content_credentials.Rd +++ b/man/get_aws_content_credentials.Rd @@ -4,7 +4,11 @@ \alias{get_aws_content_credentials} \title{Obtain AWS credentials for your content.} \usage{ -get_aws_content_credentials(connect, content_session_token = NULL) +get_aws_content_credentials( + connect, + content_session_token = NULL, + audience = NULL +) } \arguments{ \item{connect}{A Connect R6 object.} @@ -14,6 +18,10 @@ can only be obtained when the content is running on a Connect server. The token identifies the service account integration previously configured by the publisher on the Connect server. Defaults to the value from the environment variable: \code{CONNECT_CONTENT_SESSION_TOKEN}} + +\item{audience}{Optional. The audience field used for credential exchange. +This must be a valid integration GUID. When provided, the specified requested +token type will be ignored.} } \value{ The AWS credentials as a list with fields named \code{access_key_id}, diff --git a/man/get_aws_credentials.Rd b/man/get_aws_credentials.Rd index 5c4ced0e..7db9f4d9 100644 --- a/man/get_aws_credentials.Rd +++ b/man/get_aws_credentials.Rd @@ -4,7 +4,7 @@ \alias{get_aws_credentials} \title{Obtain a visitor's AWS credentials} \usage{ -get_aws_credentials(connect, user_session_token) +get_aws_credentials(connect, user_session_token, audience = NULL) } \arguments{ \item{connect}{A Connect R6 object.} @@ -13,6 +13,10 @@ get_aws_credentials(connect, user_session_token) can only be obtained when the content is running on a Connect server. The token identifies the user who is viewing the content interactively on the Connect server. Read this value from the HTTP header: \code{Posit-Connect-User-Session-Token}} + +\item{audience}{Optional. The audience field used for credential exchange. +This must be a valid integration GUID. When provided, the specified requested +token type will be ignored.} } \value{ The AWS credentials as a list with fields named \code{access_key_id}, diff --git a/man/get_oauth_content_credentials.Rd b/man/get_oauth_content_credentials.Rd index 814f22b3..5d77a021 100644 --- a/man/get_oauth_content_credentials.Rd +++ b/man/get_oauth_content_credentials.Rd @@ -8,7 +8,8 @@ access token.} get_oauth_content_credentials( connect, content_session_token = NULL, - requested_token_type = NULL + requested_token_type = NULL, + audience = NULL ) } \arguments{ @@ -24,6 +25,10 @@ environment variable: \code{CONNECT_CONTENT_SESSION_TOKEN}} will default to \code{urn:ietf:params:oauth:token-type:access_token}. Otherwise, this can be set to \code{urn:ietf:params:aws:token-type:credentials} for AWS integrations or \code{urn:posit:connect:api-key} for Connect API Key integrations.} + +\item{audience}{Optional. The audience field used for credential exchange. +This must be a valid integration GUID. When provided, the specified requested +token type will be ignored.} } \value{ The OAuth credential exchange response. diff --git a/man/get_oauth_credentials.Rd b/man/get_oauth_credentials.Rd index 1255fdb6..05b8382d 100644 --- a/man/get_oauth_credentials.Rd +++ b/man/get_oauth_credentials.Rd @@ -4,7 +4,12 @@ \alias{get_oauth_credentials} \title{Perform an OAuth credential exchange to obtain a visitor's OAuth access token.} \usage{ -get_oauth_credentials(connect, user_session_token, requested_token_type = NULL) +get_oauth_credentials( + connect, + user_session_token, + requested_token_type = NULL, + audience = NULL +) } \arguments{ \item{connect}{A Connect R6 object.} @@ -18,6 +23,10 @@ Read this value from the HTTP header: \code{Posit-Connect-User-Session-Token}} default to \code{urn:ietf:params:oauth:token-type:access_token}. Otherwise, this can be set to \code{urn:ietf:params:aws:token-type:credentials} for AWS integrations or \code{urn:posit:connect:api-key} for Connect API Key integrations.} + +\item{audience}{Optional. The audience field used for credential exchange. +This must be a valid integration GUID. When provided, the specified requested +token type will be ignored.} } \value{ The OAuth credential exchange response. From 275527533ed8cde52184867ea341d8ecfd10e550 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 15 Jul 2025 17:03:17 -0400 Subject: [PATCH 03/13] add audience field to the visitor api key logic --- R/connect.R | 7 ++++++- man/connect.Rd | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/R/connect.R b/R/connect.R index 5dd08546..220561b9 100644 --- a/R/connect.R +++ b/R/connect.R @@ -962,6 +962,9 @@ Connect <- R6::R6Class( #' key here you can test a visitor client with differently-scoped #' permissions. #' @param prefix The prefix used to determine environment variables +#' @param audience Optional. The audience field used for credential exchange. +#' This must be a valid integration GUID. When provided in conjunction with the +#' token field, it will use the Connect API integation based on the specified GUID. #' @param ... Additional arguments. Not used at present #' @param .check_is_fatal Whether to fail if "check" requests fail. Useful in #' rare cases where more http request customization is needed for requests to @@ -998,6 +1001,7 @@ connect <- function( token, token_local_testing_key = api_key, prefix = "CONNECT", + audience = NULL, ..., .check_is_fatal = TRUE ) { @@ -1017,7 +1021,8 @@ connect <- function( visitor_creds <- get_oauth_credentials( con, user_session_token = token, - requested_token_type = "urn:posit:connect:api-key" + requested_token_type = "urn:posit:connect:api-key", + audience = audience, ) con <- connect(server = server, api_key = visitor_creds$access_token) } else { diff --git a/man/connect.Rd b/man/connect.Rd index 5367f3b9..1f30606e 100644 --- a/man/connect.Rd +++ b/man/connect.Rd @@ -10,6 +10,7 @@ connect( token, token_local_testing_key = api_key, prefix = "CONNECT", + audience = NULL, ..., .check_is_fatal = TRUE ) @@ -33,6 +34,10 @@ permissions.} \item{prefix}{The prefix used to determine environment variables} +\item{audience}{Optional. The audience field used for credential exchange. +This must be a valid integration GUID. When provided in conjunction with the +token field, it will use the Connect API integation based on the specified GUID.} + \item{...}{Additional arguments. Not used at present} \item{.check_is_fatal}{Whether to fail if "check" requests fail. Useful in From dbf62f96f9f5530f86bec1790040b168fc59f7f7 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 22 Jul 2025 10:00:26 -0400 Subject: [PATCH 04/13] Update R/connect.R Co-authored-by: Toph Allen --- R/connect.R | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/R/connect.R b/R/connect.R index 220561b9..ae620dcc 100644 --- a/R/connect.R +++ b/R/connect.R @@ -962,9 +962,7 @@ Connect <- R6::R6Class( #' key here you can test a visitor client with differently-scoped #' permissions. #' @param prefix The prefix used to determine environment variables -#' @param audience Optional. The audience field used for credential exchange. -#' This must be a valid integration GUID. When provided in conjunction with the -#' token field, it will use the Connect API integation based on the specified GUID. +#' @param integration_guid Optional. The GUID of a Connect API integration associated with this piece of content. #' @param ... Additional arguments. Not used at present #' @param .check_is_fatal Whether to fail if "check" requests fail. Useful in #' rare cases where more http request customization is needed for requests to From e328e4fea56aa426bafc4ec9d1fb422d07918c70 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 22 Jul 2025 10:33:29 -0400 Subject: [PATCH 05/13] syntax issues; added tests --- R/connect.R | 2 +- R/get.R | 6 ++--- tests/testthat/test-oauth.R | 44 ++++++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/R/connect.R b/R/connect.R index ae620dcc..bcac8b0d 100644 --- a/R/connect.R +++ b/R/connect.R @@ -1020,7 +1020,7 @@ connect <- function( con, user_session_token = token, requested_token_type = "urn:posit:connect:api-key", - audience = audience, + audience = audience ) con <- connect(server = server, api_key = visitor_creds$access_token) } else { diff --git a/R/get.R b/R/get.R index b1894936..9fb12195 100644 --- a/R/get.R +++ b/R/get.R @@ -684,7 +684,7 @@ get_oauth_credentials <- function( subject_token_type = "urn:posit:connect:user-session-token", subject_token = user_session_token, requested_token_type = requested_token_type, - audience = audience, + audience = audience ) connect$POST( url, @@ -755,7 +755,7 @@ get_oauth_content_credentials <- function( subject_token_type = "urn:posit:connect:content-session-token", subject_token = content_session_token, requested_token_type = requested_token_type, - audience = audience, + audience = audience ) connect$POST( url, @@ -825,7 +825,7 @@ get_aws_credentials <- function(connect, user_session_token, audience = NULL) { connect, user_session_token, requested_token_type = "urn:ietf:params:aws:token-type:credentials", - audience = audience, + audience = audience ) # Extract access token and decode it diff --git a/tests/testthat/test-oauth.R b/tests/testthat/test-oauth.R index f83d5b3a..dcef5cd5 100644 --- a/tests/testthat/test-oauth.R +++ b/tests/testthat/test-oauth.R @@ -149,7 +149,7 @@ with_mock_api({ ) }) - test_that("we can retrieve the oauth user credentials and audience", { + test_that("we can retrieve the oauth user credentials with audience", { client <- Connect$new(server = "https://connect.example", api_key = "fake") expect_true(validate_R6_class(client, "Connect")) credentials <- get_oauth_credentials( @@ -167,6 +167,25 @@ with_mock_api({ ) }) + test_that("we can retrieve the oauth user credentials with audience and req token type", { + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + credentials <- get_oauth_credentials( + client, + user_session_token = "user-session-token", + requested_token_type = "urn:ietf:params:oauth:token-type:access_token", + audience = "audience" + ) + expect_equal( + credentials, + list( + access_token = "user-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + }) + test_that("we can retrieve the oauth content credentials with audience", { withr::local_options(list(rlib_warning_verbosity = "verbose")) @@ -189,6 +208,29 @@ with_mock_api({ ) }) + test_that("we can retrieve the oauth content credentials with audience and req token type", { + withr::local_options(list(rlib_warning_verbosity = "verbose")) + + client <- Connect$new(server = "https://connect.example", api_key = "fake") + expect_true(validate_R6_class(client, "Connect")) + expect_warning( + credentials <- get_oauth_content_credentials( + client, + content_session_token = "content-session-token", + requested_token_type = "urn:ietf:params:oauth:token-type:access_token", + audience = "audience", + ) + ) + expect_equal( + credentials, + list( + access_token = "content-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + }) + test_that("we can retrieve the AWS viewer credentials with audience", { withr::local_options(list(rlib_warning_verbosity = "verbose")) From 1acb42f475e4b9aba6db3488d40dba9c28b13a2b Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 22 Jul 2025 10:37:16 -0400 Subject: [PATCH 06/13] updates docs for clarity --- R/connect.R | 4 ++-- R/get.R | 20 ++++++++------------ man/connect.Rd | 6 ++---- man/get_aws_content_credentials.Rd | 5 ++--- man/get_aws_credentials.Rd | 5 ++--- man/get_oauth_content_credentials.Rd | 5 ++--- man/get_oauth_credentials.Rd | 5 ++--- 7 files changed, 20 insertions(+), 30 deletions(-) diff --git a/R/connect.R b/R/connect.R index bcac8b0d..9c44292a 100644 --- a/R/connect.R +++ b/R/connect.R @@ -999,7 +999,7 @@ connect <- function( token, token_local_testing_key = api_key, prefix = "CONNECT", - audience = NULL, + integration_guid = NULL, ..., .check_is_fatal = TRUE ) { @@ -1020,7 +1020,7 @@ connect <- function( con, user_session_token = token, requested_token_type = "urn:posit:connect:api-key", - audience = audience + audience = integration_guid ) con <- connect(server = server, api_key = visitor_creds$access_token) } else { diff --git a/R/get.R b/R/get.R index 9fb12195..6ea42a5f 100644 --- a/R/get.R +++ b/R/get.R @@ -643,9 +643,8 @@ get_procs <- function(src) { #' default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, this can #' be set to `urn:ietf:params:aws:token-type:credentials` for AWS integrations or #' `urn:posit:connect:api-key` for Connect API Key integrations. -#' @param audience Optional. The audience field used for credential exchange. -#' This must be a valid integration GUID. When provided, the specified requested -#' token type will be ignored. +#' @param audience Optional. The GUID of an OAuth integration associated with +#' this piece of content. #' #' @examples #' \dontrun{ @@ -706,9 +705,8 @@ get_oauth_credentials <- function( #' will default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, #' this can be set to `urn:ietf:params:aws:token-type:credentials` for AWS #' integrations or `urn:posit:connect:api-key` for Connect API Key integrations. -#' @param audience Optional. The audience field used for credential exchange. -#' This must be a valid integration GUID. When provided, the specified requested -#' token type will be ignored. +#' @param audience Optional. The GUID of an OAuth integration associated with +#' this piece of content. #' #' @examples #' \dontrun{ @@ -771,9 +769,8 @@ get_oauth_content_credentials <- function( #' can only be obtained when the content is running on a Connect server. The token #' identifies the user who is viewing the content interactively on the Connect server. #' Read this value from the HTTP header: `Posit-Connect-User-Session-Token` -#' @param audience Optional. The audience field used for credential exchange. -#' This must be a valid integration GUID. When provided, the specified requested -#' token type will be ignored. +#' @param audience Optional. The GUID of an OAuth integration associated with +#' this piece of content. #' #' @return The AWS credentials as a list with fields named `access_key_id`, #' `secret_access_key`, `session_token`, and `expiration`. @@ -848,9 +845,8 @@ get_aws_credentials <- function(connect, user_session_token, audience = NULL) { #' token identifies the service account integration previously configured by #' the publisher on the Connect server. Defaults to the value from the #' environment variable: `CONNECT_CONTENT_SESSION_TOKEN` -#' @param audience Optional. The audience field used for credential exchange. -#' This must be a valid integration GUID. When provided, the specified requested -#' token type will be ignored. +#' @param audience Optional. The GUID of an OAuth integration associated with +#' this piece of content. #' #' @return The AWS credentials as a list with fields named `access_key_id`, #' `secret_access_key`, `session_token`, and `expiration`. diff --git a/man/connect.Rd b/man/connect.Rd index 1f30606e..9fd35e95 100644 --- a/man/connect.Rd +++ b/man/connect.Rd @@ -10,7 +10,7 @@ connect( token, token_local_testing_key = api_key, prefix = "CONNECT", - audience = NULL, + integration_guid = NULL, ..., .check_is_fatal = TRUE ) @@ -34,9 +34,7 @@ permissions.} \item{prefix}{The prefix used to determine environment variables} -\item{audience}{Optional. The audience field used for credential exchange. -This must be a valid integration GUID. When provided in conjunction with the -token field, it will use the Connect API integation based on the specified GUID.} +\item{integration_guid}{Optional. The GUID of a Connect API integration associated with this piece of content.} \item{...}{Additional arguments. Not used at present} diff --git a/man/get_aws_content_credentials.Rd b/man/get_aws_content_credentials.Rd index 14cf786d..a3c30f03 100644 --- a/man/get_aws_content_credentials.Rd +++ b/man/get_aws_content_credentials.Rd @@ -19,9 +19,8 @@ token identifies the service account integration previously configured by the publisher on the Connect server. Defaults to the value from the environment variable: \code{CONNECT_CONTENT_SESSION_TOKEN}} -\item{audience}{Optional. The audience field used for credential exchange. -This must be a valid integration GUID. When provided, the specified requested -token type will be ignored.} +\item{audience}{Optional. The GUID of an OAuth integration associated with +this piece of content.} } \value{ The AWS credentials as a list with fields named \code{access_key_id}, diff --git a/man/get_aws_credentials.Rd b/man/get_aws_credentials.Rd index 7db9f4d9..f5f82c76 100644 --- a/man/get_aws_credentials.Rd +++ b/man/get_aws_credentials.Rd @@ -14,9 +14,8 @@ can only be obtained when the content is running on a Connect server. The token identifies the user who is viewing the content interactively on the Connect server. Read this value from the HTTP header: \code{Posit-Connect-User-Session-Token}} -\item{audience}{Optional. The audience field used for credential exchange. -This must be a valid integration GUID. When provided, the specified requested -token type will be ignored.} +\item{audience}{Optional. The GUID of an OAuth integration associated with +this piece of content.} } \value{ The AWS credentials as a list with fields named \code{access_key_id}, diff --git a/man/get_oauth_content_credentials.Rd b/man/get_oauth_content_credentials.Rd index 5d77a021..fda3c2e2 100644 --- a/man/get_oauth_content_credentials.Rd +++ b/man/get_oauth_content_credentials.Rd @@ -26,9 +26,8 @@ will default to \code{urn:ietf:params:oauth:token-type:access_token}. Otherwise, this can be set to \code{urn:ietf:params:aws:token-type:credentials} for AWS integrations or \code{urn:posit:connect:api-key} for Connect API Key integrations.} -\item{audience}{Optional. The audience field used for credential exchange. -This must be a valid integration GUID. When provided, the specified requested -token type will be ignored.} +\item{audience}{Optional. The GUID of an OAuth integration associated with +this piece of content.} } \value{ The OAuth credential exchange response. diff --git a/man/get_oauth_credentials.Rd b/man/get_oauth_credentials.Rd index 05b8382d..05b54efb 100644 --- a/man/get_oauth_credentials.Rd +++ b/man/get_oauth_credentials.Rd @@ -24,9 +24,8 @@ default to \code{urn:ietf:params:oauth:token-type:access_token}. Otherwise, this be set to \code{urn:ietf:params:aws:token-type:credentials} for AWS integrations or \code{urn:posit:connect:api-key} for Connect API Key integrations.} -\item{audience}{Optional. The audience field used for credential exchange. -This must be a valid integration GUID. When provided, the specified requested -token type will be ignored.} +\item{audience}{Optional. The GUID of an OAuth integration associated with +this piece of content.} } \value{ The OAuth credential exchange response. From 92fbeac699bee321364e6fcfeba6a2c9b8f2200f Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Tue, 22 Jul 2025 12:01:58 -0400 Subject: [PATCH 07/13] fixed tests and lint --- R/connect.R | 6 +- R/get.R | 8 +-- tests/testthat/test-connect.R | 25 ++++++++ tests/testthat/test-oauth.R | 114 ++++++++++++++++++++++------------ 4 files changed, 107 insertions(+), 46 deletions(-) diff --git a/R/connect.R b/R/connect.R index 9c44292a..38aefc33 100644 --- a/R/connect.R +++ b/R/connect.R @@ -962,7 +962,7 @@ Connect <- R6::R6Class( #' key here you can test a visitor client with differently-scoped #' permissions. #' @param prefix The prefix used to determine environment variables -#' @param integration_guid Optional. The GUID of a Connect API integration associated with this piece of content. +#' @param audience Optional. The GUID of a Connect API integration associated with this piece of content. #' @param ... Additional arguments. Not used at present #' @param .check_is_fatal Whether to fail if "check" requests fail. Useful in #' rare cases where more http request customization is needed for requests to @@ -999,7 +999,7 @@ connect <- function( token, token_local_testing_key = api_key, prefix = "CONNECT", - integration_guid = NULL, + audience = NULL, ..., .check_is_fatal = TRUE ) { @@ -1020,7 +1020,7 @@ connect <- function( con, user_session_token = token, requested_token_type = "urn:posit:connect:api-key", - audience = integration_guid + audience = audience ) con <- connect(server = server, api_key = visitor_creds$access_token) } else { diff --git a/R/get.R b/R/get.R index 6ea42a5f..5490fc76 100644 --- a/R/get.R +++ b/R/get.R @@ -643,7 +643,7 @@ get_procs <- function(src) { #' default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, this can #' be set to `urn:ietf:params:aws:token-type:credentials` for AWS integrations or #' `urn:posit:connect:api-key` for Connect API Key integrations. -#' @param audience Optional. The GUID of an OAuth integration associated with +#' @param audience Optional. The GUID of an OAuth integration associated with #' this piece of content. #' #' @examples @@ -705,7 +705,7 @@ get_oauth_credentials <- function( #' will default to `urn:ietf:params:oauth:token-type:access_token`. Otherwise, #' this can be set to `urn:ietf:params:aws:token-type:credentials` for AWS #' integrations or `urn:posit:connect:api-key` for Connect API Key integrations. -#' @param audience Optional. The GUID of an OAuth integration associated with +#' @param audience Optional. The GUID of an OAuth integration associated with #' this piece of content. #' #' @examples @@ -769,7 +769,7 @@ get_oauth_content_credentials <- function( #' can only be obtained when the content is running on a Connect server. The token #' identifies the user who is viewing the content interactively on the Connect server. #' Read this value from the HTTP header: `Posit-Connect-User-Session-Token` -#' @param audience Optional. The GUID of an OAuth integration associated with +#' @param audience Optional. The GUID of an OAuth integration associated with #' this piece of content. #' #' @return The AWS credentials as a list with fields named `access_key_id`, @@ -845,7 +845,7 @@ get_aws_credentials <- function(connect, user_session_token, audience = NULL) { #' token identifies the service account integration previously configured by #' the publisher on the Connect server. Defaults to the value from the #' environment variable: `CONNECT_CONTENT_SESSION_TOKEN` -#' @param audience Optional. The GUID of an OAuth integration associated with +#' @param audience Optional. The GUID of an OAuth integration associated with #' this piece of content. #' #' @return The AWS credentials as a list with fields named `access_key_id`, diff --git a/tests/testthat/test-connect.R b/tests/testthat/test-connect.R index 38ac8560..4ccb6930 100644 --- a/tests/testthat/test-connect.R +++ b/tests/testthat/test-connect.R @@ -199,6 +199,31 @@ test_that("Visitor client can successfully be created running on Connect", { }) }) +test_that("Visitor client can successfully be created running on Connect with audience", { + with_mock_api({ + withr::local_options(list(rlib_warning_verbosity = "verbose")) + withr::local_envvar( + CONNECT_SERVER = "https://connect.example", + CONNECT_API_KEY = "fake", + RSTUDIO_PRODUCT = "CONNECT" + ) + + expect_warning( + client <- connect(token = "my-token", audience = "audience"), + "This feature requires Posit Connect version" + ) + + expect_equal( + client$server, + "https://connect.example" + ) + expect_equal( + client$api_key, + "visitor-api-key" + ) + }) +}) + test_that("Visitor client uses fallback api key when running locally", { with_mock_api({ withr::local_options(list(rlib_warning_verbosity = "verbose")) diff --git a/tests/testthat/test-oauth.R b/tests/testthat/test-oauth.R index dcef5cd5..ebfeafa0 100644 --- a/tests/testthat/test-oauth.R +++ b/tests/testthat/test-oauth.R @@ -150,8 +150,17 @@ with_mock_api({ }) test_that("we can retrieve the oauth user credentials with audience", { - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "user-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + credentials <- get_oauth_credentials( client, user_session_token = "user-session-token", @@ -168,12 +177,21 @@ with_mock_api({ }) test_that("we can retrieve the oauth user credentials with audience and req token type", { - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "user-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" + ) + ) + credentials <- get_oauth_credentials( client, user_session_token = "user-session-token", - requested_token_type = "urn:ietf:params:oauth:token-type:access_token", + requested_token_type = "urn:ietf:params:oauth:token-type:access_token", audience = "audience" ) expect_equal( @@ -187,17 +205,21 @@ with_mock_api({ }) test_that("we can retrieve the oauth content credentials with audience", { - withr::local_options(list(rlib_warning_verbosity = "verbose")) - - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) - expect_warning( - credentials <- get_oauth_content_credentials( - client, - content_session_token = "content-session-token", - audience = "audience", + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "content-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" ) ) + credentials <- get_oauth_content_credentials( + client, + content_session_token = "content-session-token", + audience = "audience", + ) expect_equal( credentials, list( @@ -209,18 +231,23 @@ with_mock_api({ }) test_that("we can retrieve the oauth content credentials with audience and req token type", { - withr::local_options(list(rlib_warning_verbosity = "verbose")) - - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) - expect_warning( - credentials <- get_oauth_content_credentials( - client, - content_session_token = "content-session-token", - requested_token_type = "urn:ietf:params:oauth:token-type:access_token", - audience = "audience", + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "content-access-token", + issued_token_type = "urn:ietf:params:oauth:token-type:access_token", + token_type = "Bearer" ) ) + + credentials <- get_oauth_content_credentials( + client, + content_session_token = "content-session-token", + requested_token_type = "urn:ietf:params:oauth:token-type:access_token", + audience = "audience" + ) expect_equal( credentials, list( @@ -232,17 +259,22 @@ with_mock_api({ }) test_that("we can retrieve the AWS viewer credentials with audience", { - withr::local_options(list(rlib_warning_verbosity = "verbose")) - - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) - expect_warning( - credentials <- get_aws_credentials( - client, - user_session_token = "user-session-token", - audience = "audience" + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", + issued_token_type = "urn:ietf:params:aws:token-type:credentials", + token_type = "aws_credentials" ) ) + + credentials <- get_aws_credentials( + client, + user_session_token = "user-session-token", + audience = "audience" + ) expect_equal( credentials, list( @@ -255,12 +287,16 @@ with_mock_api({ }) test_that("we can retrieve the AWS content credentials with audience", { - # get_aws_content_credentials produces multiple warnings about the Posit - # Connect version; suppress these - withr::local_options(list(rlib_warning_verbosity = "quiet")) - - client <- Connect$new(server = "https://connect.example", api_key = "fake") - expect_true(validate_R6_class(client, "Connect")) + client <- MockConnect$new("2025.07.0") + client$mock_response( + "POST", + v1_url("oauth", "integrations", "credentials"), + content = list( + access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", + issued_token_type = "urn:ietf:params:aws:token-type:credentials", + token_type = "aws_credentials" + ) + ) credentials <- get_aws_content_credentials( client, From 4707789e8fbdfd9d24ae11eed111352576da1d62 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Wed, 23 Jul 2025 16:10:51 -0400 Subject: [PATCH 08/13] updated docsw --- man/connect.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/connect.Rd b/man/connect.Rd index 9fd35e95..70c3fd7a 100644 --- a/man/connect.Rd +++ b/man/connect.Rd @@ -10,7 +10,7 @@ connect( token, token_local_testing_key = api_key, prefix = "CONNECT", - integration_guid = NULL, + audience = NULL, ..., .check_is_fatal = TRUE ) @@ -34,7 +34,7 @@ permissions.} \item{prefix}{The prefix used to determine environment variables} -\item{integration_guid}{Optional. The GUID of a Connect API integration associated with this piece of content.} +\item{audience}{Optional. The GUID of a Connect API integration associated with this piece of content.} \item{...}{Additional arguments. Not used at present} From 59e140e40a3661e45e9bab18b747879ebef864e6 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Wed, 23 Jul 2025 16:54:19 -0400 Subject: [PATCH 09/13] fix test --- .../v1/oauth/integrations/credentials-224fa9-POST.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json diff --git a/tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json b/tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json new file mode 100644 index 00000000..b3bc152c --- /dev/null +++ b/tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json @@ -0,0 +1,5 @@ +{ + "access_token": "visitor-api-key", + "issued_token_type": "urn:posit:connect:api-key", + "token_type": "Key" +} \ No newline at end of file From 78467e7f7df84342fc8cbd88ea112196f24d8fac Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Thu, 24 Jul 2025 10:27:59 -0400 Subject: [PATCH 10/13] ignore long line linter for tests --- tests/testthat/test-oauth.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-oauth.R b/tests/testthat/test-oauth.R index ebfeafa0..30ccf8c0 100644 --- a/tests/testthat/test-oauth.R +++ b/tests/testthat/test-oauth.R @@ -264,7 +264,7 @@ with_mock_api({ "POST", v1_url("oauth", "integrations", "credentials"), content = list( - access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", + access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", # nolint: line_length_linter issued_token_type = "urn:ietf:params:aws:token-type:credentials", token_type = "aws_credentials" ) @@ -292,7 +292,7 @@ with_mock_api({ "POST", v1_url("oauth", "integrations", "credentials"), content = list( - access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", + access_token = "eyJhY2Nlc3NLZXlJZCI6ICJhYmMxMjMiLCAic2VjcmV0QWNjZXNzS2V5IjogImRlZjQ1NiIsICJzZXNzaW9uVG9rZW4iOiAiZ2hpNzg5IiwgImV4cGlyYXRpb24iOiAiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==", # nolint: line_length_linter issued_token_type = "urn:ietf:params:aws:token-type:credentials", token_type = "aws_credentials" ) From 5da5e4e76a5498f933107ffd905b053b40290639 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Thu, 24 Jul 2025 11:45:52 -0400 Subject: [PATCH 11/13] add error check for unsupported versions for audience param --- R/get.R | 20 ++++++++++++++ tests/testthat/test-oauth.R | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/R/get.R b/R/get.R index 5490fc76..10a9882e 100644 --- a/R/get.R +++ b/R/get.R @@ -677,6 +677,11 @@ get_oauth_credentials <- function( audience = NULL ) { validate_R6_class(connect, "Connect") + + if (!is.null(audience)) { + error_if_less_than(connect$version, "2025.07.0") + } + url <- v1_url("oauth", "integrations", "credentials") body <- list( grant_type = "urn:ietf:params:oauth:grant-type:token-exchange", @@ -739,6 +744,11 @@ get_oauth_content_credentials <- function( ) { validate_R6_class(connect, "Connect") error_if_less_than(connect$version, "2024.12.0") + + if (!is.null(audience)) { + error_if_less_than(connect$version, "2025.07.0") + } + if (is.null(content_session_token)) { content_session_token <- Sys.getenv("CONNECT_CONTENT_SESSION_TOKEN") if (nchar(content_session_token) == 0) { @@ -818,6 +828,11 @@ get_oauth_content_credentials <- function( #' @export get_aws_credentials <- function(connect, user_session_token, audience = NULL) { error_if_less_than(connect$version, "2025.03.0") + + if (!is.null(audience)) { + error_if_less_than(connect$version, "2025.07.0") + } + response <- get_oauth_credentials( connect, user_session_token, @@ -888,6 +903,11 @@ get_aws_credentials <- function(connect, user_session_token, audience = NULL) { #' @export get_aws_content_credentials <- function(connect, content_session_token = NULL, audience = NULL) { error_if_less_than(connect$version, "2025.03.0") + + if (!is.null(audience)) { + error_if_less_than(connect$version, "2025.07.0") + } + response <- get_oauth_content_credentials( connect, content_session_token, diff --git a/tests/testthat/test-oauth.R b/tests/testthat/test-oauth.R index 30ccf8c0..90e86753 100644 --- a/tests/testthat/test-oauth.R +++ b/tests/testthat/test-oauth.R @@ -230,6 +230,19 @@ with_mock_api({ ) }) + test_that("oauth viewer credentials retrieval with audience on unsupported version fails", { + client <- MockConnect$new("2025.06.0") + + expect_error( + get_oauth_credentials( + client, + user_session_token = "user-session-token", + audience = "audience" + ), + "ERROR: This feature requires Posit Connect version 2025.07" + ) + }) + test_that("we can retrieve the oauth content credentials with audience and req token type", { client <- MockConnect$new("2025.07.0") client$mock_response( @@ -258,6 +271,19 @@ with_mock_api({ ) }) + test_that("oauth content credentials retrieval with audience on unsupported version fails", { + client <- MockConnect$new("2025.06.0") + + expect_error( + get_oauth_content_credentials( + client, + content_session_token = "content-session-token", + audience = "audience" + ), + "ERROR: This feature requires Posit Connect version 2025.07" + ) + }) + test_that("we can retrieve the AWS viewer credentials with audience", { client <- MockConnect$new("2025.07.0") client$mock_response( @@ -286,6 +312,19 @@ with_mock_api({ ) }) + test_that("AWS viewer credentials retrieval with audience on unsupported version fails", { + client <- MockConnect$new("2025.06.0") + + expect_error( + get_aws_credentials( + client, + user_session_token = "user-session-token", + audience = "audience" + ), + "ERROR: This feature requires Posit Connect version 2025.07" + ) + }) + test_that("we can retrieve the AWS content credentials with audience", { client <- MockConnect$new("2025.07.0") client$mock_response( @@ -314,4 +353,17 @@ with_mock_api({ ) ) }) + + test_that("AWS content credentials retrieval with audience on unsupported version fails", { + client <- MockConnect$new("2025.06.0") + + expect_error( + get_aws_content_credentials( + client, + content_session_token = "content-session-token", + audience = "audience" + ), + "ERROR: This feature requires Posit Connect version 2025.07" + ) + }) }) From 8075d9a9d3b994df1da8a1a02521cfc370d1852e Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Fri, 25 Jul 2025 15:16:20 -0400 Subject: [PATCH 12/13] move mock response to proper folder --- tests/testthat/2025.07.0/__api__/server_settings.json | 3 +++ .../v1/oauth/integrations/credentials-224fa9-POST.json | 0 tests/testthat/2025.07.0/__ping__.json | 3 +++ tests/testthat/test-connect.R | 9 +++------ 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 tests/testthat/2025.07.0/__api__/server_settings.json rename tests/testthat/{2024.08.0 => 2025.07.0}/__api__/v1/oauth/integrations/credentials-224fa9-POST.json (100%) create mode 100644 tests/testthat/2025.07.0/__ping__.json diff --git a/tests/testthat/2025.07.0/__api__/server_settings.json b/tests/testthat/2025.07.0/__api__/server_settings.json new file mode 100644 index 00000000..357f91a6 --- /dev/null +++ b/tests/testthat/2025.07.0/__api__/server_settings.json @@ -0,0 +1,3 @@ +{ + "version": "2025.07.0" +} diff --git a/tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json b/tests/testthat/2025.07.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json similarity index 100% rename from tests/testthat/2024.08.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json rename to tests/testthat/2025.07.0/__api__/v1/oauth/integrations/credentials-224fa9-POST.json diff --git a/tests/testthat/2025.07.0/__ping__.json b/tests/testthat/2025.07.0/__ping__.json new file mode 100644 index 00000000..0db3279e --- /dev/null +++ b/tests/testthat/2025.07.0/__ping__.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/tests/testthat/test-connect.R b/tests/testthat/test-connect.R index 4ccb6930..19f650ea 100644 --- a/tests/testthat/test-connect.R +++ b/tests/testthat/test-connect.R @@ -200,18 +200,15 @@ test_that("Visitor client can successfully be created running on Connect", { }) test_that("Visitor client can successfully be created running on Connect with audience", { - with_mock_api({ + with_mock_dir("2025.07.0", { withr::local_options(list(rlib_warning_verbosity = "verbose")) withr::local_envvar( CONNECT_SERVER = "https://connect.example", CONNECT_API_KEY = "fake", RSTUDIO_PRODUCT = "CONNECT" ) - - expect_warning( - client <- connect(token = "my-token", audience = "audience"), - "This feature requires Posit Connect version" - ) + + client <- connect(token = "my-token", audience = "audience") expect_equal( client$server, From b4c2132b630352538801efab8af4073483356ab5 Mon Sep 17 00:00:00 2001 From: Matt Conflitti Date: Fri, 25 Jul 2025 15:17:17 -0400 Subject: [PATCH 13/13] fix lint --- tests/testthat/test-connect.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-connect.R b/tests/testthat/test-connect.R index 19f650ea..27a427ba 100644 --- a/tests/testthat/test-connect.R +++ b/tests/testthat/test-connect.R @@ -207,7 +207,7 @@ test_that("Visitor client can successfully be created running on Connect with au CONNECT_API_KEY = "fake", RSTUDIO_PRODUCT = "CONNECT" ) - + client <- connect(token = "my-token", audience = "audience") expect_equal(