@@ -175,12 +175,31 @@ struct NoAuthentication <: State
175
175
end
176
176
Base. show (io:: IO , s:: NoAuthentication ) = print (io, " NoAuthentication($(s. server) , $(s. auth_suffix) )" )
177
177
178
- function get_device_auth_client_id ()
179
- return get (ENV , " JULIA_PKG_AUTHENTICATION_DEVICE_CLIENT_ID" , " " )
178
+ function device_client_id ()
179
+ return get (ENV , " JULIA_PKG_AUTHENTICATION_DEVICE_CLIENT_ID" , " device " )
180
180
end
181
181
182
- function should_use_device_auth ()
183
- return ! isempty (get_device_auth_client_id ())
182
+ # Constructs the body if the device authentication flow requests, in accordance with
183
+ # the Sections 3.1 and 3.4 of RFC8628 (https://datatracker.ietf.org/doc/html/rfc8628).
184
+ # Returns an IOBuffer() object that can be passed to Downloads.download(input=...).
185
+ function device_token_request_body (;
186
+ client_id:: AbstractString ,
187
+ scope:: Union{AbstractString, Nothing} = nothing ,
188
+ device_code:: Union{AbstractString, Nothing} = nothing ,
189
+ grant_type:: Union{AbstractString, Nothing} = nothing ,
190
+ )
191
+ b = IOBuffer ()
192
+ write (b, " client_id=" , client_id)
193
+ if ! isnothing (scope)
194
+ write (b, " &scope=" , scope)
195
+ end
196
+ if ! isnothing (device_code)
197
+ write (b, " &device_code=" , device_code)
198
+ end
199
+ if ! isnothing (grant_type)
200
+ write (b, " &grant_type=" , grant_type)
201
+ end
202
+ return seek (b, 0 )
184
203
end
185
204
186
205
# Query the /auth/configuration endpoint to get the refresh url and
@@ -235,7 +254,14 @@ function step(state::NoAuthentication)::Union{RequestLogin, Failure}
235
254
initiate_browser_challenge (state)
236
255
end
237
256
if success
238
- return RequestLogin (state. server, state. auth_suffix, challenge, body_or_response, get (auth_config, " device_token_endpoint" , " " ), get (auth_config, " device_token_refresh_url" , " " ))
257
+ return RequestLogin (
258
+ state. server,
259
+ state. auth_suffix,
260
+ challenge,
261
+ body_or_response,
262
+ get (auth_config, " device_token_endpoint" , " " ),
263
+ get (auth_config, " device_token_refresh_url" , " " ),
264
+ )
239
265
else
240
266
return HttpError (body_or_response)
241
267
end
@@ -246,7 +272,10 @@ function fetch_device_code(state::NoAuthentication, device_endpoint::AbstractStr
246
272
response = Downloads. request (
247
273
device_endpoint,
248
274
method = " POST" ,
249
- input = IOBuffer (" client_id=$(get (ENV , " JULIA_PKG_AUTHENTICATION_DEVICE_CLIENT_ID" , " device" )) &scope=openid email profile offline_access" ),
275
+ input = device_token_request_body (
276
+ client_id = device_client_id (),
277
+ scope = " openid profile offline_access" ,
278
+ ),
250
279
output = output,
251
280
throw = false ,
252
281
headers = Dict (" Accept" => " application/json" , " Content-Type" => " application/x-www-form-urlencoded" ),
@@ -426,9 +455,29 @@ function step(state::RequestLogin)::Union{ClaimToken, Failure}
426
455
success = open_browser (url)
427
456
if success && is_device
428
457
# In case of device tokens, timeout for challenge is received in the initial request.
429
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, Inf , time (), state. response[" expires_in" ], 2 , 0 , 10 , state. device_token_endpoint, state. device_token_refresh_url)
458
+ return ClaimToken (
459
+ state. server,
460
+ state. auth_suffix,
461
+ state. challenge,
462
+ state. response,
463
+ Inf ,
464
+ time (),
465
+ state. response[" expires_in" ],
466
+ 2 ,
467
+ 0 ,
468
+ 10 ,
469
+ state. device_token_endpoint,
470
+ state. device_token_refresh_url,
471
+ )
430
472
elseif success
431
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, state. device_token_endpoint, state. device_token_refresh_url)
473
+ return ClaimToken (
474
+ state. server,
475
+ state. auth_suffix,
476
+ state. challenge,
477
+ state. response,
478
+ state. device_token_endpoint,
479
+ state. device_token_refresh_url
480
+ )
432
481
else # this can only happen for the browser hook
433
482
return GenericError (" Failed to execute open_browser hook." )
434
483
end
@@ -476,7 +525,11 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
476
525
response = Downloads. request (
477
526
state. device_token_endpoint,
478
527
method = " POST" ,
479
- input = IOBuffer (" client_id=$(get (ENV , " JULIA_PKG_AUTHENTICATION_DEVICE_CLIENT_ID" , " device" )) &scope=openid profile offline_access&grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=$(state. response[" device_code" ]) " ),
528
+ input = device_token_request_body (
529
+ client_id = device_client_id (),
530
+ device_code = state. response[" device_code" ],
531
+ grant_type = " urn:ietf:params:oauth:grant-type:device_code" ,
532
+ ),
480
533
output = output,
481
534
throw = false ,
482
535
headers = Dict (" Accept" => " application/json" , " Content-Type" => " application/x-www-form-urlencoded" ),
@@ -499,15 +552,54 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
499
552
body = try
500
553
JSON. parse (String (take! (output)))
501
554
catch err
502
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, state. expiry, state. start_time, state. timeout, state. poll_interval, state. failures + 1 , state. max_failures, state. device_token_endpoint, state. device_token_refresh_url)
555
+ return ClaimToken (
556
+ state. server,
557
+ state. auth_suffix,
558
+ state. challenge,
559
+ state. response,
560
+ state. expiry,
561
+ state. start_time,
562
+ state. timeout,
563
+ state. poll_interval,
564
+ state. failures + 1 ,
565
+ state. max_failures,
566
+ state. device_token_endpoint,
567
+ state. device_token_refresh_url,
568
+ )
503
569
end
504
570
505
571
if haskey (body, " token" )
506
572
return HasNewToken (state. server, body[" token" ])
507
573
elseif haskey (body, " expiry" ) # time at which the response/challenge pair will expire on the server
508
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, body[" expiry" ], state. start_time, state. timeout, state. poll_interval, state. failures, state. max_failures, state. device_token_endpoint, state. device_token_refresh_url)
574
+ return ClaimToken (
575
+ state. server,
576
+ state. auth_suffix,
577
+ state. challenge,
578
+ state. response,
579
+ body[" expiry" ],
580
+ state. start_time,
581
+ state. timeout,
582
+ state. poll_interval,
583
+ state. failures,
584
+ state. max_failures,
585
+ state. device_token_endpoint,
586
+ state. device_token_refresh_url,
587
+ )
509
588
else
510
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, state. expiry, state. start_time, state. timeout, state. poll_interval, state. failures + 1 , state. max_failures, state. device_token_endpoint, state. device_token_refresh_url)
589
+ return ClaimToken (
590
+ state. server,
591
+ state. auth_suffix,
592
+ state. challenge,
593
+ state. response,
594
+ state. expiry,
595
+ state. start_time,
596
+ state. timeout,
597
+ state. poll_interval,
598
+ state. failures + 1 ,
599
+ state. max_failures,
600
+ state. device_token_endpoint,
601
+ state. device_token_refresh_url
602
+ )
511
603
end
512
604
elseif response isa Downloads. Response && response. status == 200
513
605
body = JSON. parse (String (take! (output)))
@@ -516,7 +608,20 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
516
608
body[" refresh_url" ] = state. device_token_refresh_url
517
609
return HasNewToken (state. server, body)
518
610
elseif response isa Downloads. Response && response. status in [401 , 400 ] && is_device
519
- return ClaimToken (state. server, state. auth_suffix, state. challenge, state. response, state. expiry, state. start_time, state. timeout, state. poll_interval, state. failures + 1 , state. max_failures, state. device_token_endpoint, state. device_token_refresh_url)
611
+ return ClaimToken (
612
+ state. server,
613
+ state. auth_suffix,
614
+ state. challenge,
615
+ state. response,
616
+ state. expiry,
617
+ state. start_time,
618
+ state. timeout,
619
+ state. poll_interval,
620
+ state. failures + 1 ,
621
+ state. max_failures,
622
+ state. device_token_endpoint,
623
+ state. device_token_refresh_url,
624
+ )
520
625
else
521
626
return HttpError (response)
522
627
end
0 commit comments