Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 4 additions & 2 deletions lib/hex/remote_converger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -758,11 +758,13 @@ defmodule Hex.RemoteConverger do
)

{:error, :no_auth} ->
# No OAuth token - this is OK, user might only be fetching public packages
# No stored OAuth token to preflight; continue unauthenticated
# and let the repo fetch fail normally if authentication is required.
:ok

{:error, _other} ->
# Other errors (shouldn't happen with prompt_auth: true, but handle gracefully)
# Do not fail dependency resolution during OAuth preflight; continue
# unauthenticated and let the repo fetch surface any auth error.
:ok
end
else
Expand Down
5 changes: 3 additions & 2 deletions lib/hex/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,12 @@ defmodule Hex.Repo do
cond do
# First priority: explicit repo auth key with OAuth exchange disabled - use API key directly
repo_config.auth_key && Map.get(repo_config, :trusted, true) &&
Map.get(repo_config, :oauth_exchange, false) == false ->
!Map.get(repo_config, :oauth_exchange, false) ->
%{config | repo_key: repo_config.auth_key}

# Second priority: Exchange API key for OAuth token if enabled
repo_config.auth_key && Map.get(repo_config, :trusted, true) ->
repo_config.auth_key && Map.get(repo_config, :trusted, true) &&
Map.get(repo_config, :oauth_exchange, false) ->
case exchange_api_key_for_token(repo_config, repo_name) do
{:ok, access_token} ->
%{config | repo_key: "Bearer #{access_token}"}
Expand Down
55 changes: 55 additions & 0 deletions test/hex/repo_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,61 @@ defmodule Hex.RepoTest do
assert Map.get(repos_after["hexpm"], :oauth_token) == nil
end

test "non-hexpm repo with oauth_exchange: false uses API key directly and does not exchange" do
bypass = Bypass.open()

Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn ->
case path do
"/public_key" ->
assert Plug.Conn.get_req_header(conn, "authorization") == ["rawkey"]
Plug.Conn.resp(conn, 200, "fake_public_key_body")

"/oauth/token" ->
flunk("OAuth exchange must not be attempted when oauth_exchange is false")
end
end)

myrepo_config = %{
url: "http://localhost:#{bypass.port}",
public_key: nil,
auth_key: "rawkey",
trusted: true,
oauth_exchange: false,
oauth_exchange_url: "http://localhost:#{bypass.port}"
}

assert {:ok, {200, _, "fake_public_key_body"}} = Hex.Repo.get_public_key(myrepo_config)
end

test "non-hexpm repo with oauth_exchange not true does not attempt API key exchange" do
bypass = Bypass.open()
test_pid = self()

Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn ->
case path do
"/public_key" ->
Plug.Conn.resp(conn, 200, "fake_public_key_body")

"/oauth/token" ->
send(test_pid, :exchange_attempted)
Plug.Conn.resp(conn, 500, "")
end
end)

myrepo_config = %{
url: "http://localhost:#{bypass.port}",
public_key: nil,
auth_key: "rawkey",
trusted: true,
oauth_exchange: nil,
oauth_exchange_url: "http://localhost:#{bypass.port}"
}

assert {:ok, {200, _, _}} = Hex.Repo.get_public_key(myrepo_config)

refute_received :exchange_attempted
end

test "fetch_repo/1" do
assert Hex.Repo.fetch_repo("foo") == :error

Expand Down
Loading