Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Changed

* The string `repr` of `DatasetVersion` (e.g. `dataset.versions`) is now valid Julia code. ([#84])
* `JuliaHub.authenticate` will now fall back to force-authentication if the token in an existing `auth.toml` file is found to be invalid during authentication.

### Fixed

Expand Down Expand Up @@ -150,4 +151,4 @@ Initial package release.
[#58]: https://github.com/JuliaComputing/JuliaHub.jl/issues/58
[#74]: https://github.com/JuliaComputing/JuliaHub.jl/issues/74
[#83]: https://github.com/JuliaComputing/JuliaHub.jl/issues/83
[#84]: https://github.com/JuliaComputing/JuliaHub.jl/issues/84
[#84]: https://github.com/JuliaComputing/JuliaHub.jl/issues/84
53 changes: 48 additions & 5 deletions src/authentication.jl
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,15 @@ as different authentication calls may clash.
function authenticate end

function authenticate(server::AbstractString, token::Union{AbstractString, Secret})
auth = _authentication(
_juliahub_uri(server);
token=isa(token, Secret) ? token : Secret(token),
)
auth = try
_authentication(
_juliahub_uri(server);
token=isa(token, Secret) ? token : Secret(token),
)
catch e
isa(e, InvalidAuthentication) || rethrow()
throw(AuthenticationError("The authentication token is invalid"))
end
global __AUTH__[] = auth
return auth
end
Expand Down Expand Up @@ -259,7 +264,44 @@ function _authenticate(
# _authenticate either returns a valid token, or throws
auth_toml = _authenticate_retry(string(server_uri), 1; force, maxcount)
# Note: _authentication may throw, which gets passed on to the user
_authentication(server_uri; auth_toml...)
try
_authentication(server_uri; auth_toml...)
catch e
# If the token in auth.toml is invalid, but it hasn't expired,
# PkgAuthentication won't catch that, and we attempt to use it (to get the
# API version etc). If the token is invalid, that fails with a 401 and
# _authentication() throws. In this case, we will go ahead and remove the token
# and try again (which should lead to the interactive authentication flow).
if !isa(e, InvalidAuthentication) || (maxcount <= 1)
rethrow()
end
# We'll back up the old auth.toml though, because the user did not ask
# us to remove it, so we don't want to delete the token for them either.
# To avoid overwriting an existing backup, we generate a unique name
# by hashing the file contents.
Comment on lines +278 to +281
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know the token is invalid though, so why do we want to keep it around?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to delete user data if they didn't explicitly ask for it. And right now we delete iff force=true.

backup_path = string(
auth_toml.tokenpath,
".",
bytes2hex(open(SHA.sha1, "CHANGELOG.md"))[1:8],
".backup",
)
mv(auth_toml.tokenpath, backup_path; force=true)
@warn """
Existing token appears invalid, retrying with `force=true`.
Existing auth.toml backed up in: $(backup_path)
"""
# We assume that _authenticate_retry immediately returned the token,
# and didn't retry multiple times. So we just bump `count` by one here.
auth_toml = _authenticate_retry(string(server_uri), 2; force=true, maxcount)
try
_authentication(server_uri; auth_toml...)
catch e
# If it again fails with InvalidAuthentication, we give up. But we
# need to throw AuthenticationError.
isa(e, InvalidAuthentication) || rethrow()
throw(AuthenticationError("JuliaHub returned an invalid authentication token"))
end
end
finally
isnothing(hook) || PkgAuthentication.clear_open_browser_hook()
end
Expand Down Expand Up @@ -340,6 +382,7 @@ function _authentication(
api = try
_get_api_information(string(server), token)
catch e
isa(e, InvalidAuthentication) && rethrow()
errmsg = """
Unable to determine JuliaHub API version.
_get_api_information failed with an exception:
Expand Down
7 changes: 7 additions & 0 deletions test/authentication.jl
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,12 @@ end
delete!(MOCK_JULIAHUB_STATE, :auth_v1_status)
MOCK_JULIAHUB_STATE[:auth_v1_username] = nothing
@test_throws JuliaHub.AuthenticationError JuliaHub.authenticate(server, token)

# Test that we handle InvalidAuthentication correctly in _authentication()
empty!(MOCK_JULIAHUB_STATE)
MOCK_JULIAHUB_STATE[:auth_v1_status] = 401
@test_throws JuliaHub.AuthenticationError(
"The authentication token is invalid"
) JuliaHub.authenticate(server, token)
end
end
Loading