Change:
What happened: I configured my API server with a Webhook token authentication server by way of the "authentication-token-webhook-config-file" command-line flag, and confirmed that the API server does pass bearer tokens through, issuing HTTP POST requests with a JSON-encoded body. When my Webhook server indicates authentication failure, it responds with HTTP status code 200 and a JSON-encoded in the body, this time with the object's "Error" field populated. Here's an example: When the API server receives this response, it refuses the original motivating request with an HTTP status code 401 response, but the API server writes the following into its log file: This use of this default error message is due to an omission in file , where , and . What you expected to happen: The API server should instead log a message that includes the value from . Per a brief discussion with , he agreed that the API server log message should include such an error message from the Webhook server, provided it's present and nonempty. How to reproduce it (as minimally and precisely as possible): Configure an API server to delegate to a Webhook token authentication server. Force that Webhook server to respond "successfully" with an HTTP 200 response, but with the "Authenticated" field set to false (or omitted) and with a particular error message in the "Error" field. Make an HTTP request of the API server with an HTTP "Authentication" header with a value starting with the string "Bearer " followed by at least one more character. Inspect the logs from the API server, looking for a message that begins with "Unable to authenticate the request due to an error." Anything else we need to know?: Environment: Kubernetes version (use ):Cloud provider or hardware configuration: AWS EC2 OS (e.g. from /etc/os-release):Kernel (e.g. ):Install tools: kubeadm_
/sig auth
/assign

Discussion:
], importmap = "k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/errors", importpath = "k8s.io/apimachinery/pkg/util/errors", <ins> deps = ["//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library"], </ins> ) filegroup(

import ( "errors" "fmt" <ins> "k8s.io/apimachinery/pkg/util/sets" </ins> ) // MessageCountMap contains occurrence for each error message.

if len(agg) == 1 { return agg[0].Error() } <del> result := fmt.Sprintf("[%s", agg[0].Error()) for i := 1; i < len(agg); i++ { result += fmt.Sprintf(", %s", agg[i].Error()) </del> <ins> seenerrs := sets.NewString() result := "" agg.visit(func(err error) { msg := err.Error() if seenerrs.Has(msg) { return } seenerrs.Insert(msg) if len(seenerrs) > 1 { result += ", " } result += msg }) if len(seenerrs) == 1 { return result } return "[" + result + "]" } func (agg aggregate) visit(f func(err error)) { for _, err := range agg { switch err := err.(type) { case aggregate: err.visit(f) case Aggregate: for _, nestedErr := range err.Errors() { f(nestedErr) } default: f(err) } </ins> } <del> result += "]" return result </del> } // Errors is part of the Aggregate interface.

} } <ins> func TestDedupeAggregate(t *testing.T) { var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc")} var agg Aggregate agg = NewAggregate(slice) if agg == nil { t.Errorf("expected non-nil") } if s := agg.Error(); s != "abc" { t.Errorf("expected 'abc', got %q", s) } if s := agg.Errors(); len(s) != 2 { t.Errorf("expected two-elements slice, got %#v", s) } } func TestDedupePluralAggregate(t *testing.T) { var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), fmt.Errorf("123")} var agg Aggregate agg = NewAggregate(slice) if agg == nil { t.Errorf("expected non-nil") } if s := agg.Error(); s != "[abc, 123]" { t.Errorf("expected '[abc, 123]', got %q", s) } if s := agg.Errors(); len(s) != 3 { t.Errorf("expected three-elements slice, got %#v", s) } } func TestFlattenAndDedupeAggregate(t *testing.T) { var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc")})} var agg Aggregate agg = NewAggregate(slice) if agg == nil { t.Errorf("expected non-nil") } if s := agg.Error(); s != "abc" { t.Errorf("expected 'abc', got %q", s) } if s := agg.Errors(); len(s) != 3 { t.Errorf("expected three-elements slice, got %#v", s) } } func TestFlattenAggregate(t *testing.T) { var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc"), fmt.Errorf("def"), NewAggregate([]error{fmt.Errorf("def"), fmt.Errorf("ghi")})})} var agg Aggregate agg = NewAggregate(slice) if agg == nil { t.Errorf("expected non-nil") } if s := agg.Error(); s != "[abc, def, ghi]" { t.Errorf("expected '[abc, def, ghi]', got %q", s) } if s := agg.Errors(); len(s) != 3 { t.Errorf("expected three-elements slice, got %#v", s) } } </ins> func TestFilterOut(t *testing.T) { testCases := []struct { err      error

import ( "context" <ins> "errors" </ins> "time" authentication "k8s.io/api/authentication/v1beta1"

r.Status = result.Status if !r.Status.Authenticated { <del> return nil, false, nil </del> <ins> var err error if len(r.Status.Error) != 0 { err = errors.New(r.Status.Error) } return nil, false, err </ins> } var extra map[string][]string