-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve control over redirects during auth code flows.
When sheparding a user through an interactive authorisation code flow to access APIs protected by OAuth2, the oauth_flow_auth_code() function runs a temporary webserver on localhost to capture the code. This is a very common technique for OAuth client libraries. Unfortunately, this doesn't work in environments where localhost is probably inaccessible to users -- which is normally the case on RStudio Server and Posit Workbench. Previously the oauth_flow_auth_code() function would hang indefinitely in these environments. This commit fixes this failure mode by attempting to detect when this temporary localhost server will actually work and only runs one if it will. Of course, this doesn't resolve the issue for users that can't run a localhost server -- so in this situation, we now allow users to manually paste the authorisation code into the console from... somewhere. In the likely event that they don't want to be redirected to localhost for this step, this commit also fulfills the longstanding request for control over the redirect URL. The combination of a manual copy & paste step with a custom redirect URL is what the gargle package terms the "pseudo out-of-band" flow, and the design used here is largely based on that package. Because of this compatibility, users could register the existing public page at https://www.tidyverse.org/google-callback/ with their OAuth provider, host their own clone, or create a variant. Control over the redirect URL is backwards-compatible but does require making the 'host_name' parameter deprecated, unfortunately. Unit tests for new functions are included, as are various updates to the documentation. Fixes #165 and #176. Signed-off-by: Aaron Jacobs <[email protected]>
- Loading branch information
Showing
7 changed files
with
187 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ Imports: | |
cli (>= 3.0.0), | ||
curl, | ||
glue, | ||
lifecycle, | ||
magrittr, | ||
openssl, | ||
R6, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
test_that("so-called 'hosted' sessions are detected correctly", { | ||
withr::with_envvar(c("RSTUDIO_PROGRAM_MODE" = "server"), { | ||
expect_true(is_hosted_session()) | ||
}) | ||
# Emulate running outside RStudio Server if we happen to be running our tests | ||
# under it. | ||
withr::with_envvar(c("RSTUDIO_PROGRAM_MODE" = NA), { | ||
expect_false(is_hosted_session()) | ||
}) | ||
}) | ||
|
||
test_that("JSON-encoded authorisation codes can be input manually", { | ||
state <- base64_url_rand(32) | ||
input <- list(state = state, code = "abc123") | ||
encoded <- openssl::base64_encode(jsonlite::toJSON(input)) | ||
local_mocked_bindings( | ||
read_line = function(prompt = "") encoded | ||
) | ||
expect_equal(oauth_flow_auth_code_read(state), "abc123") | ||
expect_error(oauth_flow_auth_code_read("invalid"), "state does not match") | ||
}) | ||
|
||
test_that("bare authorisation codes can be input manually", { | ||
state <- base64_url_rand(32) | ||
sent_code <- FALSE | ||
local_mocked_bindings( | ||
read_line = function(prompt = "") { | ||
if (sent_code) { | ||
state | ||
} else { | ||
sent_code <<- TRUE | ||
"zyx987" | ||
} | ||
} | ||
) | ||
expect_equal(oauth_flow_auth_code_read(state), "zyx987") | ||
expect_error(oauth_flow_auth_code_read("invalid"), "state does not match") | ||
}) |