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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions R/oauth-client.R
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ oauth_client <- function(
"{.code auth = 'jwt_sig'} requires a claim specification in {.arg auth_params}."
)
}

if (is_list(auth_params$claim)) {
auth_params$claim <- exec("jwt_claim", !!!auth_params$claim)
} else if (is.function(auth_params$claim)) {
auth_params$claim <- auth_params$claim()
} else {
cli::cli_abort(
"{.value claim} in {.arg auth_params}
must be a list or function."
)
}
}

auth <- paste0("oauth_client_req_auth_", auth)
Expand Down
38 changes: 19 additions & 19 deletions R/oauth-flow-jwt.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@
#' @family OAuth flows
#' @inheritParams req_perform
#' @inheritParams req_oauth_auth_code
#' @param claim A list of claims. If all elements of the claim set are static
#' apart from `iat`, `nbf`, `exp`, or `jti`, provide a list and
#' [jwt_claim()] will automatically fill in the dynamic components.
#' If other components need to vary, you can instead provide a zero-argument
#' callback function which should call `jwt_claim()`.
#' @param signature Function use to sign `claim`, e.g. [jwt_encode_sig()].
#' @param signature_params Additional arguments passed to `signature`, e.g.
#' `size`, `header`.
Expand All @@ -27,8 +22,13 @@
#' req_auth <- function(req) {
#' req_oauth_bearer_jwt(
#' req,
#' client = oauth_client("example", "https://example.com/get_token"),
#' claim = jwt_claim()
#' client = oauth_client(
#' "example",
#' "https://example.com/get_token",
#' auth_params = list(
#' claim = jwt_claim()
#' )
#' )
#' )
#' }
#'
Expand All @@ -37,49 +37,49 @@
req_oauth_bearer_jwt <- function(
req,
client,
claim,
signature = "jwt_encode_sig",
signature_params = list(),
scope = NULL,
token_params = list()
) {
params <- list(
client = client,
claim = claim,
signature = signature,
signature_params = signature_params,
scope = scope,
token_params = token_params
)

cache <- cache_mem(client, claim)
cache <- cache_mem(client)
req_oauth(req, "oauth_flow_bearer_jwt", params, cache = cache)
}

#' @export
#' @rdname req_oauth_bearer_jwt
oauth_flow_bearer_jwt <- function(
client,
claim,
signature = "jwt_encode_sig",
signature_params = list(),
scope = NULL,
token_params = list()
) {
check_installed("jose")
if (is.null(client$key)) {
cli::cli_abort("JWT flow requires {.arg client} with a key.")
cli::cli_abort("JWT flow requires {.arg client} with a key and a claim.")
}

if (is_list(claim)) {
claim <- exec("jwt_claim", !!!claim)
} else if (is.function(claim)) {
claim <- claim()
} else {
cli::cli_abort("{.arg claim} must be a list or function.")
if (is.null(client$auth_params$claim)) {
cli::cli_abort(
"JWT flow requires {.arg client} with a claim in {.arg auth_params}."
)
}

jwt <- exec(signature, claim = claim, key = client$key, !!!signature_params)
jwt <- exec(
signature,
claim = client$auth_params$claim,
key = client$key,
!!!signature_params
)

# https://datatracker.ietf.org/doc/html/rfc7523#section-2.1
oauth_client_get_token(
Expand Down
17 changes: 7 additions & 10 deletions man/req_oauth_bearer_jwt.Rd

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

37 changes: 37 additions & 0 deletions tests/testthat/_snaps/oauth-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,40 @@
* token_url: "http://example.com"
* auth : <function>

# validates claim

Code
oauth_client("test", "http://example.com", key = "abc", auth_params = list(
claim = "123"))
Condition
Error in `oauth_client()`:
! claim in `auth_params` must be a list or function.

---

Code
oauth_client("test", "http://example.com", key = "abc", auth_params = list(
claim = jwt_claim()))
Output
<httr2_oauth_client>
* name : "b3d60321e62eb364e9c8dc12ffeec242"
* id : "test"
* key : <REDACTED>
* token_url : "http://example.com"
* auth : "oauth_client_req_auth_jwt_sig"
* auth_params: <list>

---

Code
oauth_client("test", "http://example.com", key = "abc", auth_params = list(
claim = jwt_claim))
Output
<httr2_oauth_client>
* name : "b3d60321e62eb364e9c8dc12ffeec242"
* id : "test"
* key : <REDACTED>
* token_url : "http://example.com"
* auth : "oauth_client_req_auth_jwt_sig"
* auth_params: <list>

16 changes: 0 additions & 16 deletions tests/testthat/_snaps/oauth-flow-jwt.md

This file was deleted.

35 changes: 35 additions & 0 deletions tests/testthat/_snaps/req-body.new.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# can't change body type

Code
req_body_json(req, list(x = 1))
Condition
Error in `req_body_json()`:
! Can't change body type from raw to json.
i You must use only one type of `req_body_*()` per request.

# errors on invalid input

Code
req_body_file(request_test(), 1)
Condition
Error in `example_url()`:
! The package "webfakes" is required.
Code
req_body_file(request_test(), "doesntexist")
Condition
Error in `example_url()`:
! The package "webfakes" is required.
Code
req_body_file(request_test(), ".")
Condition
Error in `example_url()`:
! The package "webfakes" is required.

# non-json type errors

Code
req_body_json(request_test(), mtcars, type = "application/xml")
Condition
Error in `example_url()`:
! The package "webfakes" is required.

13 changes: 13 additions & 0 deletions tests/testthat/_snaps/req-options.new.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# validates inputs

Code
req_timeout(request_test(), "x")
Condition
Error in `example_url()`:
! The package "webfakes" is required.
Code
req_timeout(request_test(), 0)
Condition
Error in `example_url()`:
! The package "webfakes" is required.

41 changes: 41 additions & 0 deletions tests/testthat/_snaps/req-perform-connection.new.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# validates inputs

Code
req_perform_connection(1)
Condition
Error in `req_perform_connection()`:
! `req` must be an HTTP request object, not the number 1.
Code
req_perform_connection(request_test(), 1)
Condition
Error in `example_url()`:
! The package "webfakes" is required.

# curl errors become errors

Code
req_perform_connection(req)
Condition
Error in `req_perform_connection()`:
! Failed to perform HTTP request.
Caused by error in `open()`:
! Failed to connect

# correclty reports curl error with retries (#817)

Code
req_perform_connection(req)
Condition
Error in `req_perform_connection()`:
! Failed to perform HTTP request.
Caused by error in `open()`:
! Failed to connect

# validates its input

Code
StreamingBody$new(1)
Condition
Error in `StreamingBody$new()`:
! `conn` must be a connection, not the number 1.

26 changes: 26 additions & 0 deletions tests/testthat/_snaps/resp-body.new.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# check argument types before caching

Code
resp_body_json(1)
Condition
Error in `resp_body_json()`:
! `resp` must be an HTTP response object, not the number 1.
Code
resp_body_xml(1)
Condition
Error in `resp_body_xml()`:
! `resp` must be an HTTP response object, not the number 1.

# content types are checked

Code
resp_body_json(req_perform(request_test("/xml")))
Condition
Error in `example_url()`:
! The package "webfakes" is required.
Code
resp_body_xml(req_perform(request_test("/json")))
Condition
Error in `example_url()`:
! The package "webfakes" is required.

32 changes: 32 additions & 0 deletions tests/testthat/test-oauth-client.R
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,35 @@ test_that("can authenticate using header or body", {
list(client_id = I("id"), client_secret = I("secret"))
)
})

test_that("validates claim", {
expect_snapshot(
oauth_client(
"test",
"http://example.com",
key = "abc",
auth_params = list(claim = "123")
),
error = TRUE
)

expect_snapshot(
oauth_client(
"test",
"http://example.com",
key = "abc",
auth_params = list(claim = jwt_claim())
),
error = FALSE
)

expect_snapshot(
oauth_client(
"test",
"http://example.com",
key = "abc",
auth_params = list(claim = jwt_claim)
),
error = FALSE
)
})
13 changes: 0 additions & 13 deletions tests/testthat/test-oauth-flow-jwt.R
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,3 @@ test_that("can generate token and use it automatically", {
expect_type(resp, "list")
expect_equal(resp$email_verified, TRUE)
})

test_that("validates inputs", {
client1 <- oauth_client("test", "http://example.com")
expect_snapshot(oauth_flow_bearer_jwt(client1), error = TRUE)

client2 <- oauth_client(
"test",
"http://example.com",
key = "abc",
auth_params = list(claim = "123")
)
expect_snapshot(oauth_flow_bearer_jwt(client2, claim = NULL), error = TRUE)
})
Loading