Skip to content

Commit 4400fa4

Browse files
committed
further cleanup
1 parent 602b1e1 commit 4400fa4

File tree

2 files changed

+130
-11
lines changed

2 files changed

+130
-11
lines changed

src/PkgAuthentication.jl

Lines changed: 121 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,31 @@ struct NoAuthentication <: State
175175
end
176176
Base.show(io::IO, s::NoAuthentication) = print(io, "NoAuthentication($(s.server), $(s.auth_suffix))")
177177

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+
end
181+
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)
180203
end
181204

182205
# Query the /auth/configuration endpoint to get the refresh url and
@@ -231,7 +254,14 @@ function step(state::NoAuthentication)::Union{RequestLogin, Failure}
231254
initiate_browser_challenge(state)
232255
end
233256
if success
234-
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+
)
235265
else
236266
return HttpError(body_or_response)
237267
end
@@ -242,7 +272,10 @@ function fetch_device_code(state::NoAuthentication, device_endpoint::AbstractStr
242272
response = Downloads.request(
243273
device_endpoint,
244274
method = "POST",
245-
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+
),
246279
output = output,
247280
throw = false,
248281
headers = Dict("Accept" => "application/json", "Content-Type" => "application/x-www-form-urlencoded"),
@@ -422,9 +455,29 @@ function step(state::RequestLogin)::Union{ClaimToken, Failure}
422455
success = open_browser(url)
423456
if success && is_device
424457
# In case of device tokens, timeout for challenge is received in the initial request.
425-
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+
)
426472
elseif success
427-
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+
)
428481
else # this can only happen for the browser hook
429482
return GenericError("Failed to execute open_browser hook.")
430483
end
@@ -472,7 +525,12 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
472525
response = Downloads.request(
473526
state.device_token_endpoint,
474527
method = "POST",
475-
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+
#scope = "openid profile offline_access",
533+
),
476534
output = output,
477535
throw = false,
478536
headers = Dict("Accept" => "application/json", "Content-Type" => "application/x-www-form-urlencoded"),
@@ -495,15 +553,54 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
495553
body = try
496554
JSON.parse(String(take!(output)))
497555
catch err
498-
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)
556+
return ClaimToken(
557+
state.server,
558+
state.auth_suffix,
559+
state.challenge,
560+
state.response,
561+
state.expiry,
562+
state.start_time,
563+
state.timeout,
564+
state.poll_interval,
565+
state.failures + 1,
566+
state.max_failures,
567+
state.device_token_endpoint,
568+
state.device_token_refresh_url,
569+
)
499570
end
500571

501572
if haskey(body, "token")
502573
return HasNewToken(state.server, body["token"])
503574
elseif haskey(body, "expiry") # time at which the response/challenge pair will expire on the server
504-
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)
575+
return ClaimToken(
576+
state.server,
577+
state.auth_suffix,
578+
state.challenge,
579+
state.response,
580+
body["expiry"],
581+
state.start_time,
582+
state.timeout,
583+
state.poll_interval,
584+
state.failures,
585+
state.max_failures,
586+
state.device_token_endpoint,
587+
state.device_token_refresh_url,
588+
)
505589
else
506-
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)
590+
return ClaimToken(
591+
state.server,
592+
state.auth_suffix,
593+
state.challenge,
594+
state.response,
595+
state.expiry,
596+
state.start_time,
597+
state.timeout,
598+
state.poll_interval,
599+
state.failures + 1,
600+
state.max_failures,
601+
state.device_token_endpoint,
602+
state.device_token_refresh_url
603+
)
507604
end
508605
elseif response isa Downloads.Response && response.status == 200
509606
body = JSON.parse(String(take!(output)))
@@ -512,7 +609,20 @@ function step(state::ClaimToken)::Union{ClaimToken, HasNewToken, Failure}
512609
body["refresh_url"] = state.device_token_refresh_url
513610
return HasNewToken(state.server, body)
514611
elseif response isa Downloads.Response && response.status in [401, 400] && is_device
515-
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)
612+
return ClaimToken(
613+
state.server,
614+
state.auth_suffix,
615+
state.challenge,
616+
state.response,
617+
state.expiry,
618+
state.start_time,
619+
state.timeout,
620+
state.poll_interval,
621+
state.failures + 1,
622+
state.max_failures,
623+
state.device_token_endpoint,
624+
state.device_token_refresh_url,
625+
)
516626
else
517627
return HttpError(response)
518628
end

test/utilities_test.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,12 @@
1111

1212
@test PkgAuthentication.detectwsl() isa Bool
1313
end
14+
15+
@testset "device_token_request_body" begin
16+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo"))) == "client_id=foo"
17+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo", scope="bar"))) == "client_id=foo&scope=bar"
18+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo", device_code="bar"))) == "client_id=foo&device_code=bar"
19+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo", grant_type="bar"))) == "client_id=foo&grant_type=bar"
20+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo", scope="bar", device_code="baz", grant_type="qux"))) == "client_id=foo&scope=bar&device_code=baz&grant_type=qux"
21+
@test String(take!(PkgAuthentication.device_token_request_body(client_id="foo", scope=nothing, device_code=nothing, grant_type=nothing))) == "client_id=foo"
22+
end

0 commit comments

Comments
 (0)