@@ -59,25 +59,27 @@ req_oauth_auth_code <- function(req, client,
59
59
pkce = TRUE ,
60
60
auth_params = list (),
61
61
token_params = list (),
62
- type = c(" desktop" , " web" ),
63
- host_name = " localhost" ,
64
- host_ip = " 127.0.0.1" ,
65
- port = httpuv :: randomPort(),
66
- redirect_uri = " http://localhost"
62
+ redirect_uri = " http://localhost" ,
63
+ host_name = deprecated(),
64
+ host_ip = deprecated(),
65
+ port = deprecated()
67
66
) {
68
67
68
+ redirect <- normalize_redirect_uri(
69
+ redirect_uri = redirect_uri ,
70
+ host_name = host_name ,
71
+ host_ip = host_ip ,
72
+ port = port
73
+ )
74
+
69
75
params <- list (
70
76
client = client ,
71
77
auth_url = auth_url ,
72
78
scope = scope ,
73
79
pkce = pkce ,
74
80
auth_params = auth_params ,
75
81
token_params = token_params ,
76
- type = type ,
77
- host_name = host_name ,
78
- host_ip = host_ip ,
79
- port = port ,
80
- redirect_uri = redirect_uri
82
+ redirect_uri = redirect $ uri
81
83
)
82
84
83
85
cache <- cache_choose(client , cache_disk , cache_key )
@@ -123,20 +125,27 @@ req_oauth_auth_code <- function(req, client,
123
125
# ' @param auth_params List containing additional parameters passed to `oauth_flow_auth_code_url()`
124
126
# ' @param token_params List containing additional parameters passed to the
125
127
# ' `token_url`.
126
- # ' @param host_name `r lifecycle::badge("deprecated")` Use `redirect_uri `
127
- # ' instead.
128
+ # ' @param host_name,host_ip,port `r lifecycle::badge("deprecated")`
129
+ # ' Now use `redirect_uri` instead.
128
130
# ' @param host_ip IP address for the temporary webserver used to capture the
129
131
# ' authorization code.
130
- # ' @param type Either `desktop` or `web`. Use desktop when running on the
131
- # ' desktop in an environment where you can redirect the user to `localhost`.
132
- # ' Use `web` when running in a hosted web environment.
133
- # ' @param port Port to bind the temporary webserver to. Used only when
134
- # ' `redirect_uri` is `"http(s)://localhost"`. By default, this uses a random
135
- # ' port. You may need to set it to a fixed port if the API requires that the
136
- # ' `redirect_uri` specified in the client exactly matches the `redirect_uri`
137
- # ' generated by this function.
132
+ # ' @param port Port to bind the temporary webserver to.
138
133
# ' @param redirect_uri URL to redirect back to after authorization is complete.
139
134
# ' Often this must be registered with the API in advance.
135
+ # '
136
+ # ' httr2 supports two forms of redirect. Firstly, you can use a `localhost`
137
+ # ' url (the default), where httr2 will set up a temporary webserver to listen
138
+ # ' for the OAuth redirect. In this case, httr2 will automatically append a
139
+ # ' random port. If you need to set it to a fixed port because the API requires
140
+ # ' it, then specify it with (e.g.) `"http://localhost:1011"`. This technique
141
+ # ' works well when you are working on your own computer.
142
+ # '
143
+ # ' Alternatively, you can provide a URL to a website that uses javascript to
144
+ # ' give the user a code to copy and paste back into the R session (see
145
+ # ' <https://www.tidyverse.org/google-callback/>, for an example). This is
146
+ # ' less convenient (because it requires more user interaction) but also works
147
+ # ' in hosted environments.
148
+ # '
140
149
# ' @returns An [oauth_token].
141
150
# ' @export
142
151
# ' @keywords internal
@@ -160,38 +169,20 @@ oauth_flow_auth_code <- function(client,
160
169
pkce = TRUE ,
161
170
auth_params = list (),
162
171
token_params = list (),
172
+ redirect_uri = " http://localhost" ,
163
173
host_name = deprecated(),
164
- host_ip = " 127.0.0.1" ,
165
- type = c(" desktop" , " web" ),
166
- port = httpuv :: randomPort(),
167
- redirect_uri = " http://localhost"
174
+ host_ip = deprecated(),
175
+ port = deprecated()
168
176
) {
169
177
170
- type <- arg_match(type )
171
- if (type == " desktop" ) {
172
- check_installed(" httpuv" , " desktop OAuth" )
173
- if (is_hosted_session()) {
174
- cli :: cli_abort(" Only type='web' is supported in the current session" )
175
- }
176
- }
177
-
178
178
oauth_flow_check(" authorization code" , client , interactive = TRUE )
179
179
180
- # For backwards compatibility, fall back to the original redirect URL
181
- # construction.
182
- if (lifecycle :: is_present(host_name )) {
183
- lifecycle :: deprecate_warn(
184
- when = " 0.3.0" ,
185
- what = " oauth_flow_auth_code(host_name)" ,
186
- with = " oauth_flow_auth_code(redirect_uri)"
187
- )
188
- redirect_uri <- paste0(" http://" , host_name , " :" , port , " /" )
189
- }
190
-
191
- # Only append a port if we have a bare HTTP(s) localhost redirect.
192
- if (grepl(" https?://localhost$" , redirect_uri )) {
193
- redirect_uri <- paste0(redirect_uri , " :" , port , " /" )
194
- }
180
+ redirect <- normalize_redirect_uri(
181
+ redirect_uri = redirect_uri ,
182
+ host_name = host_name ,
183
+ host_ip = host_ip ,
184
+ port = port
185
+ )
195
186
196
187
if (pkce ) {
197
188
code <- oauth_flow_auth_code_pkce()
@@ -205,16 +196,19 @@ oauth_flow_auth_code <- function(client,
205
196
# Redirect user to authorisation url.
206
197
user_url <- oauth_flow_auth_code_url(client ,
207
198
auth_url = auth_url ,
208
- redirect_uri = redirect_uri ,
199
+ redirect_uri = redirect $ uri ,
209
200
scope = scope ,
210
201
state = state ,
211
202
auth_params = auth_params
212
203
)
213
204
utils :: browseURL(user_url )
214
205
215
- if (type == " desktop" ) {
216
- # Listen on localhost for the result.
217
- result <- oauth_flow_auth_code_listen(host_ip , port )
206
+ if (redirect $ localhost ) {
207
+ # Listen on localhost for the result
208
+ result <- oauth_flow_auth_code_listen(
209
+ port = redirect $ port ,
210
+ path = redirect $ path
211
+ )
218
212
code <- oauth_flow_auth_code_parse(result , state )
219
213
} else {
220
214
# Allow the user to retrieve the token out of band manually and enter it
@@ -233,6 +227,61 @@ oauth_flow_auth_code <- function(client,
233
227
)
234
228
}
235
229
230
+ normalize_redirect_uri <- function (redirect_uri ,
231
+ host_name = deprecated(),
232
+ host_ip = deprecated(),
233
+ port = deprecated(),
234
+ error_call = caller_env()) {
235
+
236
+ parsed <- url_parse(redirect_uri )
237
+
238
+ if (lifecycle :: is_present(host_name )) {
239
+ lifecycle :: deprecate_warn(
240
+ when = " 0.3.0" ,
241
+ what = " oauth_flow_auth_code(host_name)" ,
242
+ with = " oauth_flow_auth_code(redirect_uri)"
243
+ )
244
+ parsed $ hostname <- host_name
245
+ }
246
+
247
+ if (lifecycle :: is_present(port )) {
248
+ lifecycle :: deprecate_warn(
249
+ when = " 0.3.0" ,
250
+ what = " oauth_flow_auth_code(port)" ,
251
+ with = " oauth_flow_auth_code(redirect_uri)"
252
+ )
253
+ parsed $ port <- port
254
+ }
255
+
256
+ if (lifecycle :: is_present(host_ip )) {
257
+ lifecycle :: deprecate_warn(" 0.3.0" , " oauth_flow_auth_code(host_ip)" )
258
+ }
259
+
260
+ localhost <- parsed $ hostname == " localhost"
261
+
262
+ if (localhost ) {
263
+ check_installed(" httpuv" , " desktop OAuth" )
264
+ if (is_hosted_session()) {
265
+ cli :: cli_abort(
266
+ " Can't use localhost {.arg redirect_uri} in a hosted environment" ,
267
+ call = error_call
268
+ )
269
+ }
270
+
271
+ if (is.null(parsed $ port )) {
272
+ parsed $ port <- httpuv :: randomPort()
273
+ }
274
+ }
275
+
276
+ list (
277
+ uri = url_build(parsed ),
278
+ localhost = localhost ,
279
+ port = parsed $ port ,
280
+ path = parsed $ path %|| % " /"
281
+ )
282
+
283
+ }
284
+
236
285
# Authorisation request: make a url that the user navigates to
237
286
# https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
238
287
# ' @export
@@ -261,11 +310,11 @@ oauth_flow_auth_code_url <- function(client,
261
310
262
311
# ' @export
263
312
# ' @rdname oauth_flow_auth_code
264
- oauth_flow_auth_code_listen <- function (host_ip = " 127.0.0.1" , port = 1410 ) {
313
+ oauth_flow_auth_code_listen <- function (host_ip = " 127.0.0.1" , port = 1410 , path = " / " ) {
265
314
complete <- FALSE
266
315
info <- NULL
267
316
listen <- function (env ) {
268
- if (! identical(env $ PATH_INFO , " / " )) {
317
+ if (! identical(env $ PATH_INFO , path )) {
269
318
return (list (
270
319
status = 404L ,
271
320
headers = list (" Content-Type" = " text/plain" ),
0 commit comments