oauth_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // Copyright 2010 Gary Burd
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package oauth
  15. import (
  16. "bytes"
  17. "crypto/x509"
  18. "encoding/pem"
  19. "io"
  20. "io/ioutil"
  21. "net/http"
  22. "net/http/cookiejar"
  23. "net/http/httptest"
  24. "net/url"
  25. "strings"
  26. "testing"
  27. "golang.org/x/net/context"
  28. )
  29. func parseURL(urlStr string) *url.URL {
  30. u, err := url.Parse(urlStr)
  31. if err != nil {
  32. panic(err)
  33. }
  34. return u
  35. }
  36. var oauthTests = []struct {
  37. method string
  38. url *url.URL
  39. form url.Values
  40. nonce string
  41. timestamp string
  42. clientCredentials Credentials
  43. credentials Credentials
  44. signatureMethod SignatureMethod
  45. base string
  46. header string
  47. }{
  48. {
  49. // Simple example from Twitter OAuth tool
  50. method: "GET",
  51. url: parseURL("https://api.twitter.com/1/"),
  52. form: url.Values{"page": {"10"}},
  53. nonce: "8067e8abc6bdca2006818132445c8f4c",
  54. timestamp: "1355795903",
  55. clientCredentials: Credentials{"kMViZR2MHk2mM7hUNVw9A", "56Fgl58yOfqXOhHXX0ybvOmSnPQFvR2miYmm30A"},
  56. credentials: Credentials{"10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", "yF75mvq4LZMHj9O0DXwoC3ZxUnN1ptvieThYuOAYM"},
  57. base: `GET&https%3A%2F%2Fapi.twitter.com%2F1%2F&oauth_consumer_key%3DkMViZR2MHk2mM7hUNVw9A%26oauth_nonce%3D8067e8abc6bdca2006818132445c8f4c%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355795903%26oauth_token%3D10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU%26oauth_version%3D1.0%26page%3D10`,
  58. header: `OAuth oauth_consumer_key="kMViZR2MHk2mM7hUNVw9A", oauth_nonce="8067e8abc6bdca2006818132445c8f4c", oauth_signature="o5cx1ggJrY9ognZuVVeUwglKV8U%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355795903", oauth_token="10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", oauth_version="1.0"`,
  59. },
  60. {
  61. // Test case and port insensitivity.
  62. method: "GeT",
  63. url: parseURL("https://apI.twItter.com:443/1/"),
  64. form: url.Values{"page": {"10"}},
  65. nonce: "8067e8abc6bdca2006818132445c8f4c",
  66. timestamp: "1355795903",
  67. clientCredentials: Credentials{"kMViZR2MHk2mM7hUNVw9A", "56Fgl58yOfqXOhHXX0ybvOmSnPQFvR2miYmm30A"},
  68. credentials: Credentials{"10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", "yF75mvq4LZMHj9O0DXwoC3ZxUnN1ptvieThYuOAYM"},
  69. base: `GET&https%3A%2F%2Fapi.twitter.com%2F1%2F&oauth_consumer_key%3DkMViZR2MHk2mM7hUNVw9A%26oauth_nonce%3D8067e8abc6bdca2006818132445c8f4c%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355795903%26oauth_token%3D10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU%26oauth_version%3D1.0%26page%3D10`,
  70. header: `OAuth oauth_consumer_key="kMViZR2MHk2mM7hUNVw9A", oauth_nonce="8067e8abc6bdca2006818132445c8f4c", oauth_signature="o5cx1ggJrY9ognZuVVeUwglKV8U%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355795903", oauth_token="10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", oauth_version="1.0"`,
  71. },
  72. {
  73. // Example generated using the Netflix OAuth tool.
  74. method: "GET",
  75. url: parseURL("http://api-public.netflix.com/catalog/titles"),
  76. form: url.Values{"term": {"Dark Knight"}, "count": {"2"}},
  77. nonce: "1234",
  78. timestamp: "1355850443",
  79. clientCredentials: Credentials{"apiKey001", "sharedSecret002"},
  80. credentials: Credentials{"accessToken003", "accessSecret004"},
  81. base: `GET&http%3A%2F%2Fapi-public.netflix.com%2Fcatalog%2Ftitles&count%3D2%26oauth_consumer_key%3DapiKey001%26oauth_nonce%3D1234%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355850443%26oauth_token%3DaccessToken003%26oauth_version%3D1.0%26term%3DDark%2520Knight`,
  82. header: `OAuth oauth_consumer_key="apiKey001", oauth_nonce="1234", oauth_signature="0JAoaqt6oz6TJx8N%2B06XmhPjcOs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355850443", oauth_token="accessToken003", oauth_version="1.0"`,
  83. },
  84. {
  85. // Special characters in form values.
  86. method: "GET",
  87. url: parseURL("http://PHOTOS.example.net:8001/Photos"),
  88. form: url.Values{"photo size": {"300%"}, "title": {"Back of $100 Dollars Bill"}},
  89. nonce: "kllo~9940~pd9333jh",
  90. timestamp: "1191242096",
  91. clientCredentials: Credentials{"dpf43f3++p+#2l4k3l03", "secret01"},
  92. credentials: Credentials{"nnch734d(0)0sl2jdk", "secret02"},
  93. base: "GET&http%3A%2F%2Fphotos.example.net%3A8001%2FPhotos&oauth_consumer_key%3Ddpf43f3%252B%252Bp%252B%25232l4k3l03%26oauth_nonce%3Dkllo~9940~pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d%25280%25290sl2jdk%26oauth_version%3D1.0%26photo%2520size%3D300%2525%26title%3DBack%2520of%2520%2524100%2520Dollars%2520Bill",
  94. header: `OAuth oauth_consumer_key="dpf43f3%2B%2Bp%2B%232l4k3l03", oauth_nonce="kllo~9940~pd9333jh", oauth_signature="n1UAoQy2PoIYizZUiWvkdCxM3P0%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d%280%290sl2jdk", oauth_version="1.0"`,
  95. },
  96. {
  97. // Special characters in path, multiple values for same key in form.
  98. method: "GET",
  99. url: parseURL("http://EXAMPLE.COM:80/Space%20Craft"),
  100. form: url.Values{"name": {"value", "value"}},
  101. nonce: "Ix4U1Ei3RFL",
  102. timestamp: "1327384901",
  103. clientCredentials: Credentials{"abcd", "efgh"},
  104. credentials: Credentials{"ijkl", "mnop"},
  105. base: "GET&http%3A%2F%2Fexample.com%2FSpace%2520Craft&name%3Dvalue%26name%3Dvalue%26oauth_consumer_key%3Dabcd%26oauth_nonce%3DIx4U1Ei3RFL%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1327384901%26oauth_token%3Dijkl%26oauth_version%3D1.0",
  106. header: `OAuth oauth_consumer_key="abcd", oauth_nonce="Ix4U1Ei3RFL", oauth_signature="TZZ5u7qQorLnmKs%2Biqunb8gqkh4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1327384901", oauth_token="ijkl", oauth_version="1.0"`,
  107. },
  108. {
  109. // Query string in URL.
  110. method: "GET",
  111. url: parseURL("http://EXAMPLE.COM:80/Space%20Craft?name=value"),
  112. form: url.Values{"name": {"value"}},
  113. nonce: "Ix4U1Ei3RFL",
  114. timestamp: "1327384901",
  115. clientCredentials: Credentials{"abcd", "efgh"},
  116. credentials: Credentials{"ijkl", "mnop"},
  117. base: "GET&http%3A%2F%2Fexample.com%2FSpace%2520Craft&name%3Dvalue%26name%3Dvalue%26oauth_consumer_key%3Dabcd%26oauth_nonce%3DIx4U1Ei3RFL%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1327384901%26oauth_token%3Dijkl%26oauth_version%3D1.0",
  118. header: `OAuth oauth_consumer_key="abcd", oauth_nonce="Ix4U1Ei3RFL", oauth_signature="TZZ5u7qQorLnmKs%2Biqunb8gqkh4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1327384901", oauth_token="ijkl", oauth_version="1.0"`,
  119. },
  120. {
  121. // "/" in form value.
  122. method: "POST",
  123. url: parseURL("https://stream.twitter.com/1.1/statuses/filter.json"),
  124. form: url.Values{"track": {"example.com/abcd"}},
  125. nonce: "bf2cb6d611e59f99103238fc9a3bb8d8",
  126. timestamp: "1362434376",
  127. clientCredentials: Credentials{"consumer_key", "consumer_secret"},
  128. credentials: Credentials{"token", "secret"},
  129. base: "POST&https%3A%2F%2Fstream.twitter.com%2F1.1%2Fstatuses%2Ffilter.json&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3Dbf2cb6d611e59f99103238fc9a3bb8d8%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1362434376%26oauth_token%3Dtoken%26oauth_version%3D1.0%26track%3Dexample.com%252Fabcd",
  130. header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="bf2cb6d611e59f99103238fc9a3bb8d8", oauth_signature="LcxylEOnNdgoKSJi7jX07mxcvfM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1362434376", oauth_token="token", oauth_version="1.0"`,
  131. },
  132. {
  133. // "/" in query string
  134. method: "POST",
  135. url: parseURL("https://stream.twitter.com/1.1/statuses/filter.json?track=example.com/query"),
  136. form: url.Values{},
  137. nonce: "884275759fbab914654b50ae643c563a",
  138. timestamp: "1362435218",
  139. clientCredentials: Credentials{"consumer_key", "consumer_secret"},
  140. credentials: Credentials{"token", "secret"},
  141. base: "POST&https%3A%2F%2Fstream.twitter.com%2F1.1%2Fstatuses%2Ffilter.json&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3D884275759fbab914654b50ae643c563a%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1362435218%26oauth_token%3Dtoken%26oauth_version%3D1.0%26track%3Dexample.com%252Fquery",
  142. header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="884275759fbab914654b50ae643c563a", oauth_signature="OAldqvRrKDXRGZ9BqSi2CqeVH0g%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1362435218", oauth_token="token", oauth_version="1.0"`,
  143. },
  144. {
  145. // QuickBooks query string
  146. method: "GET",
  147. url: parseURL("https://qb.sbfinance.intuit.com/v3/company/1273852765/query"),
  148. form: url.Values{"query": {"select * from account"}},
  149. nonce: "12345678",
  150. timestamp: "1409876517",
  151. clientCredentials: Credentials{"consumer_key", "consumer_secret"},
  152. credentials: Credentials{"token", "secret"},
  153. base: "GET&https%3A%2F%2Fqb.sbfinance.intuit.com%2Fv3%2Fcompany%2F1273852765%2Fquery&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3D12345678%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1409876517%26oauth_token%3Dtoken%26oauth_version%3D1.0%26query%3Dselect%2520%252A%2520from%2520account",
  154. header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="12345678", oauth_signature="7crYee%2BJLvg7dksQiHbarUHN3rY%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1409876517", oauth_token="token", oauth_version="1.0"`,
  155. },
  156. {
  157. // Plain text signature method
  158. signatureMethod: PLAINTEXT,
  159. method: "GET",
  160. url: parseURL("http://example.com/"),
  161. clientCredentials: Credentials{"key", "secret"},
  162. credentials: Credentials{"accesskey", "accesssecret"},
  163. header: `OAuth oauth_consumer_key="key", oauth_signature="secret%26accesssecret", oauth_signature_method="PLAINTEXT", oauth_token="accesskey", oauth_version="1.0"`,
  164. },
  165. {
  166. // RSA-SHA1 signature method
  167. signatureMethod: RSASHA1,
  168. method: "GET",
  169. url: parseURL("http://term.ie/oauth/example/echo_api.php"),
  170. form: url.Values{"method": {"foo%20bar"}, "bar": {"baz"}},
  171. nonce: "a7da4d14579d61886be9d596d1a6a720",
  172. timestamp: "1420240290",
  173. clientCredentials: Credentials{Token: "key"},
  174. credentials: Credentials{Token: "accesskey"},
  175. base: `GET&http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Fecho_api.php&bar%3Dbaz%26method%3Dfoo%252520bar%26oauth_consumer_key%3Dkey%26oauth_nonce%3Da7da4d14579d61886be9d596d1a6a720%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1420240290%26oauth_token%3Daccesskey%26oauth_version%3D1.0`,
  176. header: `OAuth oauth_consumer_key="key", oauth_nonce="a7da4d14579d61886be9d596d1a6a720", oauth_signature="jPun728OkfFo7BjZiaQ5UBVChwk6tf0uKNFDmNKVb%2Bd6aWYEzsDVkqqjcgTrCRNabK8ubAnhyprafk0mk3zEJe%2BxGb9GKauqwUJ6ZZoGJNYYZg3BZUQvdxSKFs1M4MUMv3fxntmD%2BoyE8jPbrVM2zD1G1AAPm79sX%2B8XE25tBE8%3D", oauth_signature_method="RSA-SHA1", oauth_timestamp="1420240290", oauth_token="accesskey", oauth_version="1.0"`,
  177. },
  178. }
  179. func TestBaseString(t *testing.T) {
  180. for _, ot := range oauthTests {
  181. if ot.signatureMethod == PLAINTEXT {
  182. // PLAINTEXT signature does not use the base string.
  183. continue
  184. }
  185. oauthParams := map[string]string{
  186. "oauth_consumer_key": ot.clientCredentials.Token,
  187. "oauth_nonce": ot.nonce,
  188. "oauth_signature_method": ot.signatureMethod.String(),
  189. "oauth_timestamp": ot.timestamp,
  190. "oauth_token": ot.credentials.Token,
  191. "oauth_version": "1.0",
  192. }
  193. var buf bytes.Buffer
  194. writeBaseString(&buf, ot.method, ot.url, ot.form, oauthParams)
  195. base := buf.String()
  196. if base != ot.base {
  197. t.Errorf("base string for %s %s\n = %q,\n want %q", ot.method, ot.url, base, ot.base)
  198. }
  199. }
  200. }
  201. var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
  202. MIICXAIBAAKBgQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3
  203. gYytUvtC2JlYzypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp
  204. /IpH7kH41EtbmUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQAB
  205. AoGAWFlbZXlM2r5G6z48tE+RTKLvB1/btgAtq8vLw/5e3KnnbcDD6fZO07m4DRaP
  206. jRryrJdsp8qazmUdcY0O1oK4FQfpprknDjP+R1XHhbhkQ4WEwjmxPstZMUZaDWF5
  207. 8d3otc23mCzwh3YcUWFu09KnMpzZsK59OfyjtkS44EDWpbECQQDXgN0ODboKsuEA
  208. VAhAtPUqspU9ivRa6yLai9kCnPb9GcztrsJZQm4NHcKVbmD2F2L4pDRx4Pmglhfl
  209. V7G/a6T7AkEA1kfU0+DkXc6I/jXHJ6pDLA5s7dBHzWgDsBzplSdkVQbKT3MbeYje
  210. ByOxzXhulOWLBQW/vxmW4HwU95KTRlj06QJASPoBYY3yb0cN/J94P/lHgJMDCNky
  211. UEuJ/PoYndLrrN/8zow8kh91xwlJ6HJ9cTiQMmTgwaOOxPuu0eI1df4M2wJBAJJS
  212. WrKUT1z/O+zbLDOZwGTFNPzvzRgmft4z4A1J6OlmyZ+XKpvDKloVtcRpCJoEZPn5
  213. AwaroquID4k/PfI7rIECQHeWa6+kPADv9IrK/92mujujS0MSEiynDw5NjTnHAH0v
  214. 8TrXzs+LCWDN/gbOCKPfnWRkgwgOeC8NN3h0zUIIUtA=
  215. -----END RSA PRIVATE KEY-----
  216. `
  217. func TestAuthorizationHeader(t *testing.T) {
  218. originalTestHook := testHook
  219. defer func() {
  220. testHook = originalTestHook
  221. }()
  222. block, _ := pem.Decode([]byte(pemPrivateKey))
  223. privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  224. if err != nil {
  225. t.Fatal(err)
  226. }
  227. for _, ot := range oauthTests {
  228. testHook = func(p map[string]string) {
  229. if _, ok := p["oauth_nonce"]; ok {
  230. p["oauth_nonce"] = ot.nonce
  231. }
  232. if _, ok := p["oauth_timestamp"]; ok {
  233. p["oauth_timestamp"] = ot.timestamp
  234. }
  235. }
  236. c := Client{Credentials: ot.clientCredentials, SignatureMethod: ot.signatureMethod, PrivateKey: privateKey}
  237. header, err := c.authorizationHeader(&request{credentials: &ot.credentials, method: ot.method, u: ot.url, form: ot.form})
  238. if err != nil {
  239. t.Errorf("authorizationHeader(&cred, %q, %q, %v) returned error %v", ot.method, ot.url.String(), ot.form, err)
  240. continue
  241. }
  242. if header != ot.header {
  243. t.Errorf("authorizationHeader(&cred, %q, %q, %v) =\n %s\nwant: %s", ot.method, ot.url.String(), ot.form, header, ot.header)
  244. }
  245. }
  246. }
  247. func TestNonce(t *testing.T) {
  248. // This test is flaky, but failures should be very rare.
  249. n := nonce()
  250. if len(n) < 8 {
  251. t.Fatalf("nonce is %s, exected something longer", n)
  252. }
  253. }
  254. func TestRequestToken(t *testing.T) {
  255. var method string
  256. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  257. expectedMethod := method
  258. if method == "" {
  259. expectedMethod = http.MethodPost
  260. }
  261. if r.Method != expectedMethod {
  262. t.Errorf("got method %s, want %s", r.Method, expectedMethod)
  263. }
  264. expectedContenType := ""
  265. if r.Method != http.MethodGet {
  266. expectedContenType = "application/x-www-form-urlencoded"
  267. }
  268. if contentType := r.Header.Get("Content-Type"); contentType != expectedContenType {
  269. t.Errorf("got content type %q, want %q", contentType, expectedContenType)
  270. }
  271. if auth := r.Header.Get("Authorization"); !strings.Contains(auth, `oauth_verifier="verifier"`) {
  272. t.Errorf("verifier missing from auth header %q", auth)
  273. }
  274. v := url.Values{}
  275. v.Set("oauth_token", "token")
  276. v.Set("oauth_token_secret", "secret")
  277. io.WriteString(w, v.Encode())
  278. }))
  279. defer ts.Close()
  280. for _, method = range []string{"", "GET", "POST"} {
  281. c := Client{TokenRequestURI: ts.URL, TokenCredentailsMethod: method}
  282. cred, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier")
  283. if err != nil {
  284. t.Errorf("returned error %v", err)
  285. }
  286. if cred.Token != "token" {
  287. t.Errorf("token for %s want %s", cred.Token, "token")
  288. }
  289. if cred.Secret != "secret" {
  290. t.Errorf("secret for %s want %s", cred.Secret, "secret")
  291. }
  292. }
  293. }
  294. func TestRenewRequestCredentials(t *testing.T) {
  295. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  296. a := r.Header.Get("Authorization")
  297. if !strings.Contains(a, `oauth_token="token"`) {
  298. t.Errorf("Authorization header %q should contains %q", a, `oauth_token="token"`)
  299. }
  300. if !strings.Contains(a, `oauth_session_handle="session-handle"`) {
  301. t.Errorf("Authorization header %q should contains %q", a, `oauth_session_handle="session-handle"`)
  302. }
  303. v := url.Values{}
  304. v.Set("oauth_token", "response-token")
  305. v.Set("oauth_token_secret", "response-token-secret")
  306. v.Set("oauth_session_handle", "response-session-handle")
  307. io.WriteString(w, v.Encode())
  308. }))
  309. defer ts.Close()
  310. c := Client{RenewCredentialRequestURI: ts.URL}
  311. cred, rv, err := c.RenewRequestCredentials(http.DefaultClient, &Credentials{Token: "token"}, "session-handle")
  312. if err != nil {
  313. t.Errorf("returned error %v", err)
  314. }
  315. if cred.Token != "response-token" {
  316. t.Errorf("token for %s want %s", cred.Token, "response-token")
  317. }
  318. if cred.Secret != "response-token-secret" {
  319. t.Errorf("secret for %s want %s", cred.Secret, "response-token-secret")
  320. }
  321. if rv.Get("oauth_session_handle") != "response-session-handle" {
  322. t.Errorf("session handle for %s want %s", rv.Get("oauth_session_handle"), "response-session-handle")
  323. }
  324. }
  325. func TestGet(t *testing.T) {
  326. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  327. if r.Method != http.MethodGet {
  328. t.Errorf("got method %s, want %s", r.Method, http.MethodGet)
  329. }
  330. if err := r.ParseForm(); err != nil {
  331. t.Errorf("returned error %v", err)
  332. }
  333. if form := r.Form.Get("form"); form != "foo" {
  334. t.Errorf("form %s, want %s", form, "foo")
  335. }
  336. cookie, err := r.Cookie("client-cookie")
  337. if err != nil {
  338. t.Errorf("returned error %v", err)
  339. }
  340. if cookie.Value != "foobar" {
  341. t.Errorf("client-cookie %s, want %s", cookie.Value, "foobar")
  342. }
  343. io.WriteString(w, "bar")
  344. }))
  345. defer ts.Close()
  346. u, err := url.Parse(ts.URL)
  347. if err != nil {
  348. t.Errorf("returned error %v", err)
  349. }
  350. jar, err := cookiejar.New(nil)
  351. if err != nil {
  352. t.Errorf("returned error %v", err)
  353. }
  354. jar.SetCookies(u, []*http.Cookie{&http.Cookie{Name: "client-cookie", Value: "foobar"}})
  355. v := url.Values{}
  356. v.Set("form", "foo")
  357. c := Client{}
  358. resp, err := c.Get(&http.Client{Jar: jar}, &Credentials{}, u.String(), v)
  359. if err != nil {
  360. t.Errorf("returned error %v", err)
  361. }
  362. defer resp.Body.Close()
  363. b, err := ioutil.ReadAll(resp.Body)
  364. if err != nil {
  365. t.Errorf("returned error %v", err)
  366. }
  367. if string(b) != "bar" {
  368. t.Errorf("body %s, want %s", string(b), "bar")
  369. }
  370. }
  371. func TestGet_ClientNil(t *testing.T) {
  372. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  373. if r.Method != http.MethodGet {
  374. t.Errorf("got method %s, want %s", r.Method, http.MethodGet)
  375. }
  376. io.WriteString(w, "bar")
  377. }))
  378. defer ts.Close()
  379. c := Client{}
  380. resp, err := c.Get(nil, &Credentials{}, ts.URL, nil)
  381. if err != nil {
  382. t.Errorf("returned error %v", err)
  383. }
  384. defer resp.Body.Close()
  385. b, err := ioutil.ReadAll(resp.Body)
  386. if err != nil {
  387. t.Errorf("returned error %v", err)
  388. }
  389. if string(b) != "bar" {
  390. t.Errorf("body %s, want %s", string(b), "bar")
  391. }
  392. }
  393. func TestGetContext(t *testing.T) {
  394. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  395. if r.Method != http.MethodGet {
  396. t.Errorf("got method %s, want %s", r.Method, http.MethodGet)
  397. }
  398. cookie, err := r.Cookie("client-cookie")
  399. if err != nil {
  400. t.Errorf("returned error %v", err)
  401. }
  402. if cookie.Value != "foobar" {
  403. t.Errorf("client-cookie %s, want %s", cookie.Value, "foobar")
  404. }
  405. io.WriteString(w, "bar")
  406. }))
  407. defer ts.Close()
  408. u, err := url.Parse(ts.URL)
  409. if err != nil {
  410. t.Errorf("returned error %v", err)
  411. }
  412. jar, err := cookiejar.New(nil)
  413. if err != nil {
  414. t.Errorf("returned error %v", err)
  415. }
  416. jar.SetCookies(u, []*http.Cookie{&http.Cookie{Name: "client-cookie", Value: "foobar"}})
  417. ctx := context.WithValue(context.Background(), HTTPClient, &http.Client{Jar: jar})
  418. c := Client{}
  419. resp, err := c.GetContext(ctx, &Credentials{}, u.String(), nil)
  420. if err != nil {
  421. t.Errorf("returned error %v", err)
  422. }
  423. defer resp.Body.Close()
  424. b, err := ioutil.ReadAll(resp.Body)
  425. if err != nil {
  426. t.Errorf("returned error %v", err)
  427. }
  428. if string(b) != "bar" {
  429. t.Errorf("body %s, want %s", string(b), "bar")
  430. }
  431. }
  432. func TestRequestCredentialsError(t *testing.T) {
  433. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  434. w.Header().Set("WWW-Authenticate", "oauth_problem=token_rejected")
  435. w.WriteHeader(http.StatusUnauthorized)
  436. io.WriteString(w, "oauth_problem=token_rejected")
  437. }))
  438. defer ts.Close()
  439. c := Client{TokenRequestURI: ts.URL}
  440. _, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier")
  441. if err == nil {
  442. t.Error("error should not be nil")
  443. }
  444. if rce, ok := err.(RequestCredentialsError); ok {
  445. if rce.StatusCode != http.StatusUnauthorized {
  446. t.Errorf("status code %d, want %d", rce.StatusCode, http.StatusUnauthorized)
  447. }
  448. wa := rce.Header.Get("WWW-Authenticate")
  449. if wa != "oauth_problem=token_rejected" {
  450. t.Errorf("WWW-Authenticate header %s, want %s", wa, "oauth_problem=token_rejected")
  451. }
  452. if string(rce.Body) != "oauth_problem=token_rejected" {
  453. t.Errorf("body %s,want %s", rce.Body, "oauth_problem=token_rejected")
  454. }
  455. } else {
  456. t.Error("error should be assertable RequestCredentialsError")
  457. }
  458. }