diff --git a/internal/registry/auth.go b/internal/registry/auth.go index e15a139cb471..4844710e6646 100644 --- a/internal/registry/auth.go +++ b/internal/registry/auth.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/containerd/errdefs/pkg/errhttp" "github.com/containerd/log" "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth/challenge" @@ -67,7 +68,7 @@ func loginV2(ctx context.Context, authConfig *registry.AuthConfig, endpoint APIE if resp.StatusCode != http.StatusOK { // TODO(dmcgowan): Attempt to further interpret result, status code and error code string - return "", fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) + return "", fmt.Errorf("login attempt to %s failed with status: %d %s: %w", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode), errhttp.ToNative(resp.StatusCode)) } return credentialAuthConfig.IdentityToken, nil diff --git a/internal/registry/auth_test.go b/internal/registry/auth_test.go new file mode 100644 index 000000000000..44632df42e39 --- /dev/null +++ b/internal/registry/auth_test.go @@ -0,0 +1,41 @@ +package registry + +import ( + "context" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/containerd/errdefs" + "github.com/moby/moby/api/types/registry" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestLoginV2BasicAuthUnauthorized(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != "alice" || pass != "secret" { + w.Header().Set("WWW-Authenticate", `Basic realm="test"`) + http.Error(w, "401 Unauthorized", http.StatusUnauthorized) + return + } + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(srv.Close) + + u, err := url.Parse(srv.URL) + assert.NilError(t, err) + endpoint := APIEndpoint{URL: u} + ctx := context.Background() + + _, err = loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "wrong"}, endpoint, "docker-test") + assert.ErrorContains(t, err, "401") + assert.Check(t, errdefs.IsUnauthorized(err)) + assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized)) + + token, err := loginV2(ctx, ®istry.AuthConfig{Username: "alice", Password: "secret"}, endpoint, "docker-test") + assert.NilError(t, err) + assert.Check(t, is.Equal("", token)) +}