Skip to content
This repository has been archived by the owner on Feb 14, 2023. It is now read-only.

Commit

Permalink
fix: url encode UAA oAuth client credentials
Browse files Browse the repository at this point in the history
Although this header was base64 encoded, the oAuth spec requires
that is also be url encoded. This was causing client credentials
that contained special characters (e.g. `+`) to be rejected by UAA.

See:
> The client identifier is encoded using the
   "application/x-www-form-urlencoded" encoding algorithm per
   Appendix B, and the encoded value is used as the username; the client
   password is encoded using the same algorithm and used as the
   password.

https://tools.ietf.org/html/rfc6749#section-2.3.1

Related:
golang/oauth2#320

[#171268188](https://www.pivotaltracker.com/story/show/171268188_
  • Loading branch information
tcdowney committed Feb 19, 2020
1 parent 080fa57 commit a4bc892
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 3 deletions.
7 changes: 5 additions & 2 deletions cfroutesync/uaaclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package uaaclient
import (
"fmt"
"net/http"
"net/url"
"strings"
)

Expand All @@ -20,12 +21,14 @@ type jsonClient interface {

func (c *Client) GetToken() (string, error) {
reqURL := fmt.Sprintf("%s/oauth/token", c.BaseURL)
bodyString := fmt.Sprintf("client_id=%s&grant_type=client_credentials", c.Name)
bodyString := fmt.Sprintf("grant_type=client_credentials")

request, err := http.NewRequest("POST", reqURL, strings.NewReader(bodyString))
if err != nil {
return "", err
}
request.SetBasicAuth(c.Name, c.Secret)

request.SetBasicAuth(url.QueryEscape(c.Name), url.QueryEscape(c.Secret))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

type getTokenResponse struct {
Expand Down
26 changes: 25 additions & 1 deletion cfroutesync/uaaclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var _ = Describe("Client", func() {
Expect(receivedRequest.Method).To(Equal("POST"))
Expect(receivedRequest.URL.RawQuery).To(BeEmpty())
receivedBytes, _ := ioutil.ReadAll(receivedRequest.Body)
Expect(receivedBytes).To(Equal([]byte("client_id=some-name&grant_type=client_credentials")))
Expect(receivedBytes).To(Equal([]byte("grant_type=client_credentials")))

authHeader := receivedRequest.Header["Authorization"]
Expect(authHeader).To(HaveLen(1))
Expand All @@ -60,6 +60,30 @@ var _ = Describe("Client", func() {
Expect(contentType).To(Equal("application/x-www-form-urlencoded"))
})

Context("when the client credentials contain special characters", func() {
It("escapes the characters before constructing the basic auth header", func() {
client = &uaaclient.Client{
BaseURL: "https://some.base.url",
Name: "some=+/name",
Secret: "some%!@secret",
JSONClient: jsonClient,
}

_, err := client.GetToken()
Expect(err).NotTo(HaveOccurred())
Expect(jsonClient.MakeRequestCallCount()).To(Equal(1))
receivedRequest, _ := jsonClient.MakeRequestArgsForCall(0)
Expect(receivedRequest.Method).To(Equal("POST"))
Expect(receivedRequest.URL.RawQuery).To(BeEmpty())
receivedBytes, _ := ioutil.ReadAll(receivedRequest.Body)
Expect(receivedBytes).To(Equal([]byte("grant_type=client_credentials")))

authHeader := receivedRequest.Header["Authorization"]
Expect(authHeader).To(HaveLen(1))
Expect(authHeader[0]).To(Equal("Basic c29tZSUzRCUyQiUyRm5hbWU6c29tZSUyNSUyMSU0MHNlY3JldA=="))
})
})

Context("when the json client returns an error", func() {
BeforeEach(func() {
jsonClient.MakeRequestReturns(errors.New("potato"))
Expand Down

0 comments on commit a4bc892

Please sign in to comment.