oauth.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  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 is consumer interface for OAuth 1.0, OAuth 1.0a and RFC 5849.
  15. //
  16. // Redirection-based Authorization
  17. //
  18. // This section outlines how to use the oauth package in redirection-based
  19. // authorization (http://tools.ietf.org/html/rfc5849#section-2).
  20. //
  21. // Step 1: Create a Client using credentials and URIs provided by the server.
  22. // The Client can be initialized once at application startup and stored in a
  23. // package-level variable.
  24. //
  25. // Step 2: Request temporary credentials using the Client
  26. // RequestTemporaryCredentials method. The callbackURL parameter is the URL of
  27. // the callback handler in step 4. Save the returned credential secret so that
  28. // it can be later found using credential token as a key. The secret can be
  29. // stored in a database keyed by the token. Another option is to store the
  30. // token and secret in session storage or a cookie.
  31. //
  32. // Step 3: Redirect the user to URL returned from AuthorizationURL method. The
  33. // AuthorizationURL method uses the temporary credentials from step 2 and other
  34. // parameters as specified by the server.
  35. //
  36. // Step 4: The server redirects back to the callback URL specified in step 2
  37. // with the temporary token and a verifier. Use the temporary token to find the
  38. // temporary secret saved in step 2. Using the temporary token, temporary
  39. // secret and verifier, request token credentials using the client RequestToken
  40. // method. Save the returned credentials for later use in the application.
  41. //
  42. // Signing Requests
  43. //
  44. // The Client type has two low-level methods for signing requests, SignForm and
  45. // SetAuthorizationHeader.
  46. //
  47. // The SignForm method adds an OAuth signature to a form. The application makes
  48. // an authenticated request by encoding the modified form to the query string
  49. // or request body.
  50. //
  51. // The SetAuthorizationHeader method adds an OAuth siganture to a request
  52. // header. The SetAuthorizationHeader method is the only way to correctly sign
  53. // a request if the application sets the URL Opaque field when making a
  54. // request.
  55. //
  56. // The Get, Put, Post and Delete methods sign and invoke a request using the
  57. // supplied net/http Client. These methods are easy to use, but not as flexible
  58. // as constructing a request using one of the low-level methods.
  59. //
  60. // Context With HTTP Client
  61. //
  62. // A context-enabled method can include a custom HTTP client in the
  63. // context and execute an HTTP request using the included HTTP client.
  64. //
  65. // hc := &http.Client{Timeout: 2 * time.Second}
  66. // ctx := context.WithValue(context.Background(), oauth.HTTPClient, hc)
  67. // c := oauth.Client{ /* Any settings */ }
  68. // resp, err := c.GetContext(ctx, &oauth.Credentials{}, rawurl, nil)
  69. package oauth // import "github.com/garyburd/go-oauth/oauth"
  70. import (
  71. "bytes"
  72. "crypto"
  73. "crypto/hmac"
  74. "crypto/rand"
  75. "crypto/rsa"
  76. "crypto/sha1"
  77. "encoding/base64"
  78. "encoding/binary"
  79. "errors"
  80. "fmt"
  81. "io"
  82. "io/ioutil"
  83. "net/http"
  84. "net/url"
  85. "sort"
  86. "strconv"
  87. "strings"
  88. "sync/atomic"
  89. "time"
  90. "golang.org/x/net/context"
  91. )
  92. // noscape[b] is true if b should not be escaped per section 3.6 of the RFC.
  93. var noEscape = [256]bool{
  94. 'A': true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
  95. 'a': true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
  96. '0': true, true, true, true, true, true, true, true, true, true,
  97. '-': true,
  98. '.': true,
  99. '_': true,
  100. '~': true,
  101. }
  102. // encode encodes string per section 3.6 of the RFC. If double is true, then
  103. // the encoding is applied twice.
  104. func encode(s string, double bool) []byte {
  105. // Compute size of result.
  106. m := 3
  107. if double {
  108. m = 5
  109. }
  110. n := 0
  111. for i := 0; i < len(s); i++ {
  112. if noEscape[s[i]] {
  113. n++
  114. } else {
  115. n += m
  116. }
  117. }
  118. p := make([]byte, n)
  119. // Encode it.
  120. j := 0
  121. for i := 0; i < len(s); i++ {
  122. b := s[i]
  123. if noEscape[b] {
  124. p[j] = b
  125. j++
  126. } else if double {
  127. p[j] = '%'
  128. p[j+1] = '2'
  129. p[j+2] = '5'
  130. p[j+3] = "0123456789ABCDEF"[b>>4]
  131. p[j+4] = "0123456789ABCDEF"[b&15]
  132. j += 5
  133. } else {
  134. p[j] = '%'
  135. p[j+1] = "0123456789ABCDEF"[b>>4]
  136. p[j+2] = "0123456789ABCDEF"[b&15]
  137. j += 3
  138. }
  139. }
  140. return p
  141. }
  142. type keyValue struct{ key, value []byte }
  143. type byKeyValue []keyValue
  144. func (p byKeyValue) Len() int { return len(p) }
  145. func (p byKeyValue) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  146. func (p byKeyValue) Less(i, j int) bool {
  147. sgn := bytes.Compare(p[i].key, p[j].key)
  148. if sgn == 0 {
  149. sgn = bytes.Compare(p[i].value, p[j].value)
  150. }
  151. return sgn < 0
  152. }
  153. func (p byKeyValue) appendValues(values url.Values) byKeyValue {
  154. for k, vs := range values {
  155. k := encode(k, true)
  156. for _, v := range vs {
  157. v := encode(v, true)
  158. p = append(p, keyValue{k, v})
  159. }
  160. }
  161. return p
  162. }
  163. // writeBaseString writes method, url, and params to w using the OAuth signature
  164. // base string computation described in section 3.4.1 of the RFC.
  165. func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oauthParams map[string]string) {
  166. // Method
  167. w.Write(encode(strings.ToUpper(method), false))
  168. w.Write([]byte{'&'})
  169. // URL
  170. scheme := strings.ToLower(u.Scheme)
  171. host := strings.ToLower(u.Host)
  172. uNoQuery := *u
  173. uNoQuery.RawQuery = ""
  174. path := uNoQuery.RequestURI()
  175. switch {
  176. case scheme == "http" && strings.HasSuffix(host, ":80"):
  177. host = host[:len(host)-len(":80")]
  178. case scheme == "https" && strings.HasSuffix(host, ":443"):
  179. host = host[:len(host)-len(":443")]
  180. }
  181. w.Write(encode(scheme, false))
  182. w.Write(encode("://", false))
  183. w.Write(encode(host, false))
  184. w.Write(encode(path, false))
  185. w.Write([]byte{'&'})
  186. // Create sorted slice of encoded parameters. Parameter keys and values are
  187. // double encoded in a single step. This is safe because double encoding
  188. // does not change the sort order.
  189. queryParams := u.Query()
  190. p := make(byKeyValue, 0, len(form)+len(queryParams)+len(oauthParams))
  191. p = p.appendValues(form)
  192. p = p.appendValues(queryParams)
  193. for k, v := range oauthParams {
  194. p = append(p, keyValue{encode(k, true), encode(v, true)})
  195. }
  196. sort.Sort(p)
  197. // Write the parameters.
  198. encodedAmp := encode("&", false)
  199. encodedEqual := encode("=", false)
  200. sep := false
  201. for _, kv := range p {
  202. if sep {
  203. w.Write(encodedAmp)
  204. } else {
  205. sep = true
  206. }
  207. w.Write(kv.key)
  208. w.Write(encodedEqual)
  209. w.Write(kv.value)
  210. }
  211. }
  212. var nonceCounter uint64
  213. func init() {
  214. if err := binary.Read(rand.Reader, binary.BigEndian, &nonceCounter); err != nil {
  215. // fallback to time if rand reader is broken
  216. nonceCounter = uint64(time.Now().UnixNano())
  217. }
  218. }
  219. // nonce returns a unique string.
  220. func nonce() string {
  221. return strconv.FormatUint(atomic.AddUint64(&nonceCounter, 1), 16)
  222. }
  223. // SignatureMethod identifies a signature method.
  224. type SignatureMethod int
  225. func (sm SignatureMethod) String() string {
  226. switch sm {
  227. case RSASHA1:
  228. return "RSA-SHA1"
  229. case HMACSHA1:
  230. return "HMAC-SHA1"
  231. case PLAINTEXT:
  232. return "PLAINTEXT"
  233. default:
  234. return "unknown"
  235. }
  236. }
  237. const (
  238. HMACSHA1 SignatureMethod = iota // HMAC-SHA1
  239. RSASHA1 // RSA-SHA1
  240. PLAINTEXT // Plain text
  241. )
  242. // Credentials represents client, temporary and token credentials.
  243. type Credentials struct {
  244. Token string // Also known as consumer key or access token.
  245. Secret string // Also known as consumer secret or access token secret.
  246. }
  247. // Client represents an OAuth client.
  248. type Client struct {
  249. // Credentials specifies the client key and secret.
  250. // Also known as the consumer key and secret
  251. Credentials Credentials
  252. // TemporaryCredentialRequestURI is the endpoint used by the client to
  253. // obtain a set of temporary credentials. Also known as the request token
  254. // URL.
  255. TemporaryCredentialRequestURI string
  256. // ResourceOwnerAuthorizationURI is the endpoint to which the resource
  257. // owner is redirected to grant authorization. Also known as authorization
  258. // URL.
  259. ResourceOwnerAuthorizationURI string
  260. // TokenRequestURI is the endpoint used by the client to request a set of
  261. // token credentials using a set of temporary credentials. Also known as
  262. // access token URL.
  263. TokenRequestURI string
  264. // RenewCredentialRequestURI is the endpoint the client uses to
  265. // request a new set of token credentials using the old set of credentials.
  266. RenewCredentialRequestURI string
  267. // TemporaryCredentialsMethod is the HTTP method used by the client to
  268. // obtain a set of temporary credentials. If this field is the empty
  269. // string, then POST is used.
  270. TemporaryCredentialsMethod string
  271. // TokenCredentailsMethod is the HTTP method used by the client to request
  272. // a set of token credentials. If this field is the empty string, then POST
  273. // is used.
  274. TokenCredentailsMethod string
  275. // Header specifies optional extra headers for requests.
  276. Header http.Header
  277. // SignatureMethod specifies the method for signing a request.
  278. SignatureMethod SignatureMethod
  279. // PrivateKey is the private key to use for RSA-SHA1 signatures. This field
  280. // must be set for RSA-SHA1 signatures and ignored for other signature
  281. // methods.
  282. PrivateKey *rsa.PrivateKey
  283. }
  284. type request struct {
  285. credentials *Credentials
  286. method string
  287. u *url.URL
  288. form url.Values
  289. verifier string
  290. sessionHandle string
  291. callbackURL string
  292. }
  293. var testHook = func(map[string]string) {}
  294. // oauthParams returns the OAuth request parameters for the given credentials,
  295. // method, URL and application params. See
  296. // http://tools.ietf.org/html/rfc5849#section-3.4 for more information about
  297. // signatures.
  298. func (c *Client) oauthParams(r *request) (map[string]string, error) {
  299. oauthParams := map[string]string{
  300. "oauth_consumer_key": c.Credentials.Token,
  301. "oauth_signature_method": c.SignatureMethod.String(),
  302. "oauth_version": "1.0",
  303. }
  304. if c.SignatureMethod != PLAINTEXT {
  305. oauthParams["oauth_timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
  306. oauthParams["oauth_nonce"] = nonce()
  307. }
  308. if r.credentials != nil {
  309. oauthParams["oauth_token"] = r.credentials.Token
  310. }
  311. if r.verifier != "" {
  312. oauthParams["oauth_verifier"] = r.verifier
  313. }
  314. if r.sessionHandle != "" {
  315. oauthParams["oauth_session_handle"] = r.sessionHandle
  316. }
  317. if r.callbackURL != "" {
  318. oauthParams["oauth_callback"] = r.callbackURL
  319. }
  320. testHook(oauthParams)
  321. var signature string
  322. switch c.SignatureMethod {
  323. case HMACSHA1:
  324. key := encode(c.Credentials.Secret, false)
  325. key = append(key, '&')
  326. if r.credentials != nil {
  327. key = append(key, encode(r.credentials.Secret, false)...)
  328. }
  329. h := hmac.New(sha1.New, key)
  330. writeBaseString(h, r.method, r.u, r.form, oauthParams)
  331. signature = base64.StdEncoding.EncodeToString(h.Sum(key[:0]))
  332. case RSASHA1:
  333. if c.PrivateKey == nil {
  334. return nil, errors.New("oauth: private key not set")
  335. }
  336. h := sha1.New()
  337. writeBaseString(h, r.method, r.u, r.form, oauthParams)
  338. rawSignature, err := rsa.SignPKCS1v15(rand.Reader, c.PrivateKey, crypto.SHA1, h.Sum(nil))
  339. if err != nil {
  340. return nil, err
  341. }
  342. signature = base64.StdEncoding.EncodeToString(rawSignature)
  343. case PLAINTEXT:
  344. rawSignature := encode(c.Credentials.Secret, false)
  345. rawSignature = append(rawSignature, '&')
  346. if r.credentials != nil {
  347. rawSignature = append(rawSignature, encode(r.credentials.Secret, false)...)
  348. }
  349. signature = string(rawSignature)
  350. default:
  351. return nil, errors.New("oauth: unknown signature method")
  352. }
  353. oauthParams["oauth_signature"] = signature
  354. return oauthParams, nil
  355. }
  356. // SignForm adds an OAuth signature to form. The urlStr argument must not
  357. // include a query string.
  358. //
  359. // See http://tools.ietf.org/html/rfc5849#section-3.5.2 for
  360. // information about transmitting OAuth parameters in a request body and
  361. // http://tools.ietf.org/html/rfc5849#section-3.5.2 for information about
  362. // transmitting OAuth parameters in a query string.
  363. func (c *Client) SignForm(credentials *Credentials, method, urlStr string, form url.Values) error {
  364. u, err := url.Parse(urlStr)
  365. switch {
  366. case err != nil:
  367. return err
  368. case u.RawQuery != "":
  369. return errors.New("oauth: urlStr argument to SignForm must not include a query string")
  370. }
  371. p, err := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: form})
  372. if err != nil {
  373. return err
  374. }
  375. for k, v := range p {
  376. form.Set(k, v)
  377. }
  378. return nil
  379. }
  380. // SignParam is deprecated. Use SignForm instead.
  381. func (c *Client) SignParam(credentials *Credentials, method, urlStr string, params url.Values) {
  382. u, _ := url.Parse(urlStr)
  383. u.RawQuery = ""
  384. p, _ := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: params})
  385. for k, v := range p {
  386. params.Set(k, v)
  387. }
  388. }
  389. var oauthKeys = []string{
  390. "oauth_consumer_key",
  391. "oauth_nonce",
  392. "oauth_signature",
  393. "oauth_signature_method",
  394. "oauth_timestamp",
  395. "oauth_token",
  396. "oauth_version",
  397. "oauth_callback",
  398. "oauth_verifier",
  399. "oauth_session_handle",
  400. }
  401. func (c *Client) authorizationHeader(r *request) (string, error) {
  402. p, err := c.oauthParams(r)
  403. if err != nil {
  404. return "", err
  405. }
  406. var h []byte
  407. // Append parameters in a fixed order to support testing.
  408. for _, k := range oauthKeys {
  409. if v, ok := p[k]; ok {
  410. if h == nil {
  411. h = []byte(`OAuth `)
  412. } else {
  413. h = append(h, ", "...)
  414. }
  415. h = append(h, k...)
  416. h = append(h, `="`...)
  417. h = append(h, encode(v, false)...)
  418. h = append(h, '"')
  419. }
  420. }
  421. return string(h), nil
  422. }
  423. // AuthorizationHeader returns the HTTP authorization header value for given
  424. // method, URL and parameters.
  425. //
  426. // AuthorizationHeader is deprecated. Use SetAuthorizationHeader instead.
  427. func (c *Client) AuthorizationHeader(credentials *Credentials, method string, u *url.URL, params url.Values) string {
  428. // Signing a request can return an error. This method is deprecated because
  429. // this method does not return an error.
  430. v, _ := c.authorizationHeader(&request{credentials: credentials, method: method, u: u, form: params})
  431. return v
  432. }
  433. // SetAuthorizationHeader adds an OAuth signature to a request header.
  434. //
  435. // See http://tools.ietf.org/html/rfc5849#section-3.5.1 for information about
  436. // transmitting OAuth parameters in an HTTP request header.
  437. func (c *Client) SetAuthorizationHeader(header http.Header, credentials *Credentials, method string, u *url.URL, form url.Values) error {
  438. v, err := c.authorizationHeader(&request{credentials: credentials, method: method, u: u, form: form})
  439. if err != nil {
  440. return err
  441. }
  442. header.Set("Authorization", v)
  443. return nil
  444. }
  445. func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Response, error) {
  446. var body io.Reader
  447. if r.method != http.MethodGet {
  448. body = strings.NewReader(r.form.Encode())
  449. }
  450. req, err := http.NewRequest(r.method, urlStr, body)
  451. if err != nil {
  452. return nil, err
  453. }
  454. if req.URL.RawQuery != "" {
  455. return nil, errors.New("oauth: url must not contain a query string")
  456. }
  457. for k, v := range c.Header {
  458. req.Header[k] = v
  459. }
  460. r.u = req.URL
  461. auth, err := c.authorizationHeader(r)
  462. if err != nil {
  463. return nil, err
  464. }
  465. req.Header.Set("Authorization", auth)
  466. if r.method == http.MethodGet {
  467. req.URL.RawQuery = r.form.Encode()
  468. } else {
  469. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  470. }
  471. req = requestWithContext(ctx, req)
  472. client := contextClient(ctx)
  473. return client.Do(req)
  474. }
  475. // Get issues a GET to the specified URL with form added as a query string.
  476. func (c *Client) Get(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  477. ctx := context.WithValue(context.Background(), HTTPClient, client)
  478. return c.GetContext(ctx, credentials, urlStr, form)
  479. }
  480. // GetContext uses Context to perform Get.
  481. func (c *Client) GetContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  482. return c.do(ctx, urlStr, &request{method: http.MethodGet, credentials: credentials, form: form})
  483. }
  484. // Post issues a POST with the specified form.
  485. func (c *Client) Post(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  486. ctx := context.WithValue(context.Background(), HTTPClient, client)
  487. return c.PostContext(ctx, credentials, urlStr, form)
  488. }
  489. // PostContext uses Context to perform Post.
  490. func (c *Client) PostContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  491. return c.do(ctx, urlStr, &request{method: http.MethodPost, credentials: credentials, form: form})
  492. }
  493. // Delete issues a DELETE with the specified form.
  494. func (c *Client) Delete(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  495. ctx := context.WithValue(context.Background(), HTTPClient, client)
  496. return c.DeleteContext(ctx, credentials, urlStr, form)
  497. }
  498. // DeleteContext uses Context to perform Delete.
  499. func (c *Client) DeleteContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  500. return c.do(ctx, urlStr, &request{method: http.MethodDelete, credentials: credentials, form: form})
  501. }
  502. // Put issues a PUT with the specified form.
  503. func (c *Client) Put(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  504. ctx := context.WithValue(context.Background(), HTTPClient, client)
  505. return c.PutContext(ctx, credentials, urlStr, form)
  506. }
  507. // PutContext uses Context to perform Put.
  508. func (c *Client) PutContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) {
  509. return c.do(ctx, urlStr, &request{method: http.MethodPut, credentials: credentials, form: form})
  510. }
  511. func (c *Client) requestCredentials(ctx context.Context, u string, r *request) (*Credentials, url.Values, error) {
  512. if r.method == "" {
  513. r.method = http.MethodPost
  514. }
  515. resp, err := c.do(ctx, u, r)
  516. if err != nil {
  517. return nil, nil, err
  518. }
  519. p, err := ioutil.ReadAll(resp.Body)
  520. resp.Body.Close()
  521. if err != nil {
  522. return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header,
  523. Body: p, msg: err.Error()}
  524. }
  525. if resp.StatusCode != 200 && resp.StatusCode != 201 {
  526. return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header,
  527. Body: p, msg: fmt.Sprintf("OAuth server status %d, %s", resp.StatusCode, string(p))}
  528. }
  529. m, err := url.ParseQuery(string(p))
  530. if err != nil {
  531. return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header,
  532. Body: p, msg: err.Error()}
  533. }
  534. tokens := m["oauth_token"]
  535. if len(tokens) == 0 || tokens[0] == "" {
  536. return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header,
  537. Body: p, msg: "oauth: token missing from server result"}
  538. }
  539. secrets := m["oauth_token_secret"]
  540. if len(secrets) == 0 { // allow "" as a valid secret.
  541. return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header,
  542. Body: p, msg: "oauth: secret missing from server result"}
  543. }
  544. return &Credentials{Token: tokens[0], Secret: secrets[0]}, m, nil
  545. }
  546. // RequestTemporaryCredentials requests temporary credentials from the server.
  547. // See http://tools.ietf.org/html/rfc5849#section-2.1 for information about
  548. // temporary credentials.
  549. func (c *Client) RequestTemporaryCredentials(client *http.Client, callbackURL string, additionalParams url.Values) (*Credentials, error) {
  550. ctx := context.WithValue(context.Background(), HTTPClient, client)
  551. return c.RequestTemporaryCredentialsContext(ctx, callbackURL, additionalParams)
  552. }
  553. // RequestTemporaryCredentialsContext uses Context to perform RequestTemporaryCredentials.
  554. func (c *Client) RequestTemporaryCredentialsContext(ctx context.Context, callbackURL string, additionalParams url.Values) (*Credentials, error) {
  555. credentials, _, err := c.requestCredentials(ctx, c.TemporaryCredentialRequestURI,
  556. &request{method: c.TemporaryCredentialsMethod, form: additionalParams, callbackURL: callbackURL})
  557. return credentials, err
  558. }
  559. // RequestToken requests token credentials from the server. See
  560. // http://tools.ietf.org/html/rfc5849#section-2.3 for information about token
  561. // credentials.
  562. func (c *Client) RequestToken(client *http.Client, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) {
  563. ctx := context.WithValue(context.Background(), HTTPClient, client)
  564. return c.RequestTokenContext(ctx, temporaryCredentials, verifier)
  565. }
  566. // RequestTokenContext uses Context to perform RequestToken.
  567. func (c *Client) RequestTokenContext(ctx context.Context, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) {
  568. return c.requestCredentials(ctx, c.TokenRequestURI,
  569. &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, verifier: verifier})
  570. }
  571. // RenewRequestCredentials requests new token credentials from the server.
  572. // See http://wiki.oauth.net/w/page/12238549/ScalableOAuth#AccessTokenRenewal
  573. // for information about access token renewal.
  574. func (c *Client) RenewRequestCredentials(client *http.Client, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) {
  575. ctx := context.WithValue(context.Background(), HTTPClient, client)
  576. return c.RenewRequestCredentialsContext(ctx, credentials, sessionHandle)
  577. }
  578. // RenewRequestCredentialsContext uses Context to perform RenewRequestCredentials.
  579. func (c *Client) RenewRequestCredentialsContext(ctx context.Context, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) {
  580. return c.requestCredentials(ctx, c.RenewCredentialRequestURI, &request{credentials: credentials, sessionHandle: sessionHandle})
  581. }
  582. // RequestTokenXAuth requests token credentials from the server using the xAuth protocol.
  583. // See https://dev.twitter.com/oauth/xauth for information on xAuth.
  584. func (c *Client) RequestTokenXAuth(client *http.Client, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) {
  585. ctx := context.WithValue(context.Background(), HTTPClient, client)
  586. return c.RequestTokenXAuthContext(ctx, temporaryCredentials, user, password)
  587. }
  588. // RequestTokenXAuthContext uses Context to perform RequestTokenXAuth.
  589. func (c *Client) RequestTokenXAuthContext(ctx context.Context, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) {
  590. form := make(url.Values)
  591. form.Set("x_auth_mode", "client_auth")
  592. form.Set("x_auth_username", user)
  593. form.Set("x_auth_password", password)
  594. return c.requestCredentials(ctx, c.TokenRequestURI,
  595. &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, form: form})
  596. }
  597. // AuthorizationURL returns the URL for resource owner authorization. See
  598. // http://tools.ietf.org/html/rfc5849#section-2.2 for information about
  599. // resource owner authorization.
  600. func (c *Client) AuthorizationURL(temporaryCredentials *Credentials, additionalParams url.Values) string {
  601. params := make(url.Values)
  602. for k, vs := range additionalParams {
  603. params[k] = vs
  604. }
  605. params.Set("oauth_token", temporaryCredentials.Token)
  606. return c.ResourceOwnerAuthorizationURI + "?" + params.Encode()
  607. }
  608. // HTTPClient is the context key to use with context's
  609. // WithValue function to associate an *http.Client value with a context.
  610. var HTTPClient contextKey
  611. type contextKey struct{}
  612. func contextClient(ctx context.Context) *http.Client {
  613. if ctx != nil {
  614. if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok && hc != nil {
  615. return hc
  616. }
  617. }
  618. return http.DefaultClient
  619. }
  620. // RequestCredentialsError is an error containing
  621. // response information when requesting credentials.
  622. type RequestCredentialsError struct {
  623. StatusCode int
  624. Header http.Header
  625. Body []byte
  626. msg string
  627. }
  628. func (e RequestCredentialsError) Error() string {
  629. return e.msg
  630. }