From 9d3c98232bef02c2d6da8366722fe3f7be7e957e Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Thu, 10 Dec 2020 10:09:42 -0600 Subject: [PATCH] Fix bug in handling response content-type in oidcclient. Before this, we weren't properly parsing the `Content-Type` header. This breaks in integration with the Supervisor since it sends an extra encoding parameter like `application/json;charset=UTF-8`. This change switches to properly parsing with the `mime.ParseMediaType` function, and adds test cases to match the supervisor behavior. Signed-off-by: Matt Moyer --- pkg/oidcclient/login.go | 9 +++++++-- pkg/oidcclient/login_test.go | 28 +++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/pkg/oidcclient/login.go b/pkg/oidcclient/login.go index 98146fa5..4d92a452 100644 --- a/pkg/oidcclient/login.go +++ b/pkg/oidcclient/login.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "fmt" + "mime" "net" "net/http" "net/url" @@ -364,8 +365,12 @@ func (h *handlerState) tokenExchangeRFC8693(baseToken *oidctypes.Token) (*oidcty if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected HTTP response status %d", resp.StatusCode) } - if contentType := resp.Header.Get("content-type"); contentType != "application/json" { - return nil, fmt.Errorf("unexpected HTTP response content type %q", contentType) + mediaType, _, err := mime.ParseMediaType(resp.Header.Get("content-type")) + if err != nil { + return nil, fmt.Errorf("failed to decode content-type header: %w", err) + } + if mediaType != "application/json" { + return nil, fmt.Errorf("unexpected HTTP response content type %q", mediaType) } // Decode the JSON response body. diff --git a/pkg/oidcclient/login_test.go b/pkg/oidcclient/login_test.go index 10daf6ab..cc0edea5 100644 --- a/pkg/oidcclient/login_test.go +++ b/pkg/oidcclient/login_test.go @@ -164,11 +164,14 @@ func TestLogin(t *testing.T) { case "test-audience-produce-http-400": http.Error(w, "some server error", http.StatusBadRequest) return + case "test-audience-produce-invalid-content-type": + w.Header().Set("content-type", "invalid/invalid;=") + return case "test-audience-produce-wrong-content-type": w.Header().Set("content-type", "invalid") return case "test-audience-produce-invalid-json": - w.Header().Set("content-type", "application/json") + w.Header().Set("content-type", "application/json;charset=UTF-8") _, _ = w.Write([]byte(`{`)) return case "test-audience-produce-invalid-tokentype": @@ -601,6 +604,29 @@ func TestLogin(t *testing.T) { }, wantErr: `failed to exchange token: unexpected HTTP response status 400`, }, + { + name: "with requested audience, session cache hit with valid token, but token exchange request returns invalid content-type header", + issuer: successServer.URL, + clientID: "test-client-id", + opt: func(t *testing.T) Option { + return func(h *handlerState) error { + cache := &mockSessionCache{t: t, getReturnsToken: &testToken} + t.Cleanup(func() { + require.Equal(t, []SessionCacheKey{{ + Issuer: successServer.URL, + ClientID: "test-client-id", + Scopes: []string{"test-scope"}, + RedirectURI: "http://localhost:0/callback", + }}, cache.sawGetKeys) + require.Empty(t, cache.sawPutTokens) + }) + require.NoError(t, WithSessionCache(cache)(h)) + require.NoError(t, WithRequestAudience("test-audience-produce-invalid-content-type")(h)) + return nil + } + }, + wantErr: `failed to exchange token: failed to decode content-type header: mime: invalid media parameter`, + }, { name: "with requested audience, session cache hit with valid token, but token exchange request returns wrong content-type", issuer: successServer.URL,