diff --git a/auther.go b/auther.go index 681724f..5a0f0fc 100644 --- a/auther.go +++ b/auther.go @@ -216,20 +216,30 @@ func collectParameters(req *http.Request, oauthParams map[string]string) (map[st // most backends do not accept duplicate query keys params[key] = value[0] } - if req.Body != nil && req.Header.Get(contentType) == formContentType { + if req.Body != nil { // reads data to a []byte, draining req.Body b, err := ioutil.ReadAll(req.Body) if err != nil { return nil, err } - values, err := url.ParseQuery(string(b)) - if err != nil { - return nil, err - } - for key, value := range values { - // not supporting params with duplicate keys - params[key] = value[0] + + if req.Header.Get(contentType) == formContentType { + values, err := url.ParseQuery(string(b)) + if err != nil { + return nil, err + } + for key, value := range values { + // not supporting params with duplicate keys + params[key] = value[0] + } + } else { + // providing body hash for requests other than x-www-form-urlencoded + // as described in https://tools.ietf.org/html/draft-eaton-oauth-bodyhash-00#section-4.1.1 + hash := sha1.Sum(b) + hash64 := base64.StdEncoding.EncodeToString(hash[:]) + params[oauthBodyHash] = hash64 } + // reinitialize Body with ReadCloser over the []byte req.Body = ioutil.NopCloser(bytes.NewReader(b)) } diff --git a/auther_test.go b/auther_test.go index 2e4cd04..84f0d3e 100644 --- a/auther_test.go +++ b/auther_test.go @@ -203,6 +203,38 @@ func TestCollectParameters(t *testing.T) { // http://golang.org/src/net/http/request.go#L837 } +func TestCollectParametersNonWwwFormUrlencoded(t *testing.T) { + oauthParams := map[string]string{ + "oauth_token": "kkk9d7dh3k39sjv7", + "oauth_consumer_key": "9djdj82h48djs9d2", + "oauth_signature_method": "HMAC-SHA1", + "oauth_timestamp": "137131201", + "oauth_nonce": "7d8f3e4a", + "realm": "photos", + } + jsonBody := "{ \"test\": \"foobar\" }" + req, err := http.NewRequest("POST", "/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b", strings.NewReader(jsonBody)) + assert.Nil(t, err) + req.Header.Set(contentType, "application/json") + params, err := collectParameters(req, oauthParams) + // assert parameters were collected from oauthParams, the query, and form body + // excluding the realm parameter + expected := map[string]string{ + "b5": "=%3D", + "a3": "a", + "c@": "", + "a2": "r b", + "oauth_token": "kkk9d7dh3k39sjv7", + "oauth_consumer_key": "9djdj82h48djs9d2", + "oauth_signature_method": "HMAC-SHA1", + "oauth_timestamp": "137131201", + "oauth_nonce": "7d8f3e4a", + "oauth_body_hash": "MFIr5skk2mB20S82rxAYkx9ql/A=", + } + assert.Nil(t, err) + assert.Equal(t, expected, params) +} + func TestSignatureBase(t *testing.T) { reqA, err := http.NewRequest("get", "HTTPS://HELLO.IO?q=test", nil) assert.Nil(t, err)