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 <moyerm@vmware.com>
This commit is contained in:
Matt Moyer 2020-12-10 10:09:42 -06:00
parent 5a0918afde
commit 9d3c98232b
No known key found for this signature in database
GPG Key ID: EAE88AD172C5AE2D
2 changed files with 34 additions and 3 deletions

View File

@ -8,6 +8,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"mime"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -364,8 +365,12 @@ func (h *handlerState) tokenExchangeRFC8693(baseToken *oidctypes.Token) (*oidcty
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected HTTP response status %d", resp.StatusCode) return nil, fmt.Errorf("unexpected HTTP response status %d", resp.StatusCode)
} }
if contentType := resp.Header.Get("content-type"); contentType != "application/json" { mediaType, _, err := mime.ParseMediaType(resp.Header.Get("content-type"))
return nil, fmt.Errorf("unexpected HTTP response content type %q", contentType) 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. // Decode the JSON response body.

View File

@ -164,11 +164,14 @@ func TestLogin(t *testing.T) {
case "test-audience-produce-http-400": case "test-audience-produce-http-400":
http.Error(w, "some server error", http.StatusBadRequest) http.Error(w, "some server error", http.StatusBadRequest)
return return
case "test-audience-produce-invalid-content-type":
w.Header().Set("content-type", "invalid/invalid;=")
return
case "test-audience-produce-wrong-content-type": case "test-audience-produce-wrong-content-type":
w.Header().Set("content-type", "invalid") w.Header().Set("content-type", "invalid")
return return
case "test-audience-produce-invalid-json": 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(`{`)) _, _ = w.Write([]byte(`{`))
return return
case "test-audience-produce-invalid-tokentype": 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`, 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", name: "with requested audience, session cache hit with valid token, but token exchange request returns wrong content-type",
issuer: successServer.URL, issuer: successServer.URL,