Skip to content

feat: added support for optional audience param #423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 25, 2025
Merged
5 changes: 4 additions & 1 deletion R/connect.R
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +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 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
Expand Down Expand Up @@ -998,6 +999,7 @@ connect <- function(
token,
token_local_testing_key = api_key,
prefix = "CONNECT",
audience = NULL,
...,
.check_is_fatal = TRUE
) {
Expand All @@ -1017,7 +1019,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 {
Expand Down
50 changes: 42 additions & 8 deletions R/get.R
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +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 GUID of an OAuth integration associated with
#' this piece of content.
#'
#' @examples
#' \dontrun{
Expand Down Expand Up @@ -671,15 +673,22 @@ 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")

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",
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,
Expand All @@ -701,6 +710,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 GUID of an OAuth integration associated with
#' this piece of content.
#'
#' @examples
#' \dontrun{
Expand Down Expand Up @@ -728,10 +739,16 @@ 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")

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) {
Expand All @@ -745,7 +762,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,
Expand All @@ -761,6 +779,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 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`.
Expand Down Expand Up @@ -806,12 +826,18 @@ 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")

if (!is.null(audience)) {
error_if_less_than(connect$version, "2025.07.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
Expand All @@ -834,6 +860,8 @@ 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 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`.
Expand Down Expand Up @@ -873,12 +901,18 @@ 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")

if (!is.null(audience)) {
error_if_less_than(connect$version, "2025.07.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
Expand Down
3 changes: 3 additions & 0 deletions man/connect.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion man/get_aws_content_credentials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion man/get_aws_credentials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/get_oauth_content_credentials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion man/get_oauth_credentials.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tests/testthat/2025.07.0/__api__/server_settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": "2025.07.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"access_token": "visitor-api-key",
"issued_token_type": "urn:posit:connect:api-key",
"token_type": "Key"
}
3 changes: 3 additions & 0 deletions tests/testthat/2025.07.0/__ping__.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{

}
22 changes: 22 additions & 0 deletions tests/testthat/test-connect.R
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,28 @@ 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_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"
)

client <- connect(token = "my-token", audience = "audience")

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"))
Expand Down
Loading
Loading