From 7cb062b0512c71615c3332d7b461efb9c41a6473 Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 12:05:34 +0200 Subject: [PATCH 1/9] rsa-sha256 + oauth_body_hash --- .gitignore | 1 + examples/appengine/app.go | 2 +- go.mod | 5 + go.sum | 10 ++ oauth/oauth.go | 362 ++++++++++++++++++++++++-------------- oauth/oauth_test.go | 2 +- 6 files changed, 249 insertions(+), 133 deletions(-) create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index e45d6ac..2c3fd86 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ _testmain.go *.a 6.out example +.idea diff --git a/examples/appengine/app.go b/examples/appengine/app.go index 080f8cd..20be8cc 100644 --- a/examples/appengine/app.go +++ b/examples/appengine/app.go @@ -39,7 +39,7 @@ var oauthClient = oauth.Client{ TokenRequestURI: "https://api.twitter.com/oauth/access_token", } -// context stores context associated with an HTTP request. +// Context stores context associated with an HTTP request. type Context struct { c context.Context r *http.Request diff --git a/go.mod b/go.mod index 61b3d35..80fd254 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,6 @@ module github.com/gomodule/oauth1 + +require ( + golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 + google.golang.org/appengine v1.5.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0fd2110 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/oauth/oauth.go b/oauth/oauth.go index 3562ff1..f6bd251 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -77,10 +77,12 @@ import ( "crypto/rand" "crypto/rsa" "crypto/sha1" + "crypto/sha256" "encoding/base64" "encoding/binary" "errors" "fmt" + "hash" "io" "io/ioutil" "net/http" @@ -92,7 +94,48 @@ import ( "time" ) -// noscape[b] is true if b should not be escaped per section 3.6 of the RFC. +// Lists of supported HTTP header and OAuth parameters. +const ( + HTTPHeader = "Authorization" + Header = "OAuth " + BodyHashParam = "oauth_body_hash" + CallbackParam = "oauth_callback" + ConsumerKeyParam = "oauth_consumer_key" + NonceParam = "oauth_nonce" + SessionHandleParam = "oauth_session_handle" + SignatureParam = "oauth_signature" + SignatureMethodParam = "oauth_signature_method" + TSParam = "oauth_timestamp" + TokenParam = "oauth_token" + TokenSecretParam = "oauth_token_secret" + VerifierParam = "oauth_verifier" + VersionParam = "oauth_version" +) + +// List of common errors +var ( + ErrPrivateKey = errors.New("oauth: private key not set") + ErrSignature = errors.New("oauth: unknown signature method") + ErrURL = errors.New("oauth: url must not contain a query string") +) + +func newRequestCredentialsError(msg string, resp *http.Response, body []byte) RequestCredentialsError { + return RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, Body: body, msg: msg} +} + +// RequestCredentialsError is an error containing response information when requesting credentials. +type RequestCredentialsError struct { + StatusCode int + Header http.Header + Body []byte + msg string +} + +func (e RequestCredentialsError) Error() string { + return e.msg +} + +// noEscape[b] is true if b should not be escaped per section 3.6 of the RFC. var noEscape = [256]bool{ '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, '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, @@ -175,8 +218,8 @@ func (p byKeyValue) appendValues(values url.Values) byKeyValue { // base string computation described in section 3.4.1 of the RFC. func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oauthParams map[string]string) { // Method - w.Write(encode(strings.ToUpper(method), false)) - w.Write([]byte{'&'}) + write(w, encode(strings.ToUpper(method), false)) + write(w, []byte{'&'}) // URL scheme := strings.ToLower(u.Scheme) @@ -193,11 +236,11 @@ func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oa host = host[:len(host)-len(":443")] } - w.Write(encode(scheme, false)) - w.Write(encode("://", false)) - w.Write(encode(host, false)) - w.Write(encode(path, false)) - w.Write([]byte{'&'}) + write(w, encode(scheme, false)) + write(w, encode("://", false)) + write(w, encode(host, false)) + write(w, encode(path, false)) + write(w, []byte{'&'}) // Create sorted slice of encoded parameters. Parameter keys and values are // double encoded in a single step. This is safe because double encoding @@ -217,13 +260,20 @@ func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oa sep := false for _, kv := range p { if sep { - w.Write(encodedAmp) + write(w, encodedAmp) } else { sep = true } - w.Write(kv.key) - w.Write(encodedEqual) - w.Write(kv.value) + write(w, kv.key) + write(w, encodedEqual) + write(w, kv.value) + } +} + +func write(to io.Writer, data []byte) { + _, err := to.Write(data) + if err != nil { + panic(err) } } @@ -244,23 +294,52 @@ func nonce() string { // SignatureMethod identifies a signature method. type SignatureMethod int +func (sm SignatureMethod) hash(r io.Reader) ([]byte, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + var h hash.Hash + switch sm { + case HMACSHA1, RSASHA1: + h = sha1.New() + case RSASHA256: + h = sha256.New() + default: + return nil, nil + } + _, err = h.Write(b) + if err != nil { + return nil, err + } + return h.Sum(nil), nil +} + func (sm SignatureMethod) String() string { switch sm { case RSASHA1: return "RSA-SHA1" + case RSASHA256: + return "RSA-SHA256" case HMACSHA1: return "HMAC-SHA1" case PLAINTEXT: return "PLAINTEXT" default: - return "unknown" + return "UNKNOWN" } } +// List of supported signature methods. const ( - HMACSHA1 SignatureMethod = iota // HMAC-SHA1 - RSASHA1 // RSA-SHA1 - PLAINTEXT // Plain text + // HMACSHA1 for HMAC-SHA1. + HMACSHA1 SignatureMethod = iota + // RSASHA1 for RSA-SHA1. + RSASHA1 + // PLAINTEXT for plaintext. + PLAINTEXT + // RSASHA256 for RSA-SHA256. + RSASHA256 ) // Credentials represents client, temporary and token credentials. @@ -299,10 +378,10 @@ type Client struct { // string, then POST is used. TemporaryCredentialsMethod string - // TokenCredentailsMethod is the HTTP method used by the client to request + // TokenCredentialsMethod is the HTTP method used by the client to request // a set of token credentials. If this field is the empty string, then POST // is used. - TokenCredentailsMethod string + TokenCredentialsMethod string // Header specifies optional extra headers for requests. Header http.Header @@ -321,49 +400,55 @@ type request struct { method string u *url.URL form url.Values - verifier string + body io.Reader sessionHandle string + verifier string callbackURL string } var testHook = func(map[string]string) {} // oauthParams returns the OAuth request parameters for the given credentials, -// method, URL and application params. See -// http://tools.ietf.org/html/rfc5849#section-3.4 for more information about -// signatures. +// method, URL and application params. +// See http://tools.ietf.org/html/rfc5849#section-3.4 for more information about signatures. func (c *Client) oauthParams(r *request) (map[string]string, error) { oauthParams := map[string]string{ - "oauth_consumer_key": c.Credentials.Token, - "oauth_signature_method": c.SignatureMethod.String(), - "oauth_version": "1.0", + ConsumerKeyParam: c.Credentials.Token, + SignatureMethodParam: c.SignatureMethod.String(), + VersionParam: "1.0", } - if c.SignatureMethod != PLAINTEXT { - oauthParams["oauth_timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) - oauthParams["oauth_nonce"] = nonce() + oauthParams[TSParam] = strconv.FormatInt(time.Now().Unix(), 10) + oauthParams[NonceParam] = nonce() } - if r.credentials != nil { - oauthParams["oauth_token"] = r.credentials.Token + oauthParams[TokenParam] = r.credentials.Token } - if r.verifier != "" { - oauthParams["oauth_verifier"] = r.verifier + oauthParams[VerifierParam] = r.verifier } - if r.sessionHandle != "" { - oauthParams["oauth_session_handle"] = r.sessionHandle + oauthParams[SessionHandleParam] = r.sessionHandle } - if r.callbackURL != "" { - oauthParams["oauth_callback"] = r.callbackURL + oauthParams[CallbackParam] = r.callbackURL + } + if r.body != nil { + src, err := c.SignatureMethod.hash(r.body) + if err != nil { + return nil, err + } + if src != nil { + oauthParams[BodyHashParam] = base64.StdEncoding.EncodeToString(src) + } } testHook(oauthParams) - var signature string - + var ( + signature string + err error + ) switch c.SignatureMethod { case HMACSHA1: key := encode(c.Credentials.Secret, false) @@ -375,16 +460,15 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { writeBaseString(h, r.method, r.u, r.form, oauthParams) signature = base64.StdEncoding.EncodeToString(h.Sum(key[:0])) case RSASHA1: - if c.PrivateKey == nil { - return nil, errors.New("oauth: private key not set") + signature, err = rsaSignAndEncode(c.PrivateKey, crypto.SHA1, r, oauthParams) + if err != nil { + return nil, err } - h := sha1.New() - writeBaseString(h, r.method, r.u, r.form, oauthParams) - rawSignature, err := rsa.SignPKCS1v15(rand.Reader, c.PrivateKey, crypto.SHA1, h.Sum(nil)) + case RSASHA256: + signature, err = rsaSignAndEncode(c.PrivateKey, crypto.SHA256, r, oauthParams) if err != nil { return nil, err } - signature = base64.StdEncoding.EncodeToString(rawSignature) case PLAINTEXT: rawSignature := encode(c.Credentials.Secret, false) rawSignature = append(rawSignature, '&') @@ -393,13 +477,34 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { } signature = string(rawSignature) default: - return nil, errors.New("oauth: unknown signature method") + return nil, ErrSignature } - oauthParams["oauth_signature"] = signature + return oauthParams, nil } +func rsaSignAndEncode(key *rsa.PrivateKey, hf crypto.Hash, r *request, params map[string]string) (string, error) { + if key == nil { + return "", ErrPrivateKey + } + var h hash.Hash + switch hf { + case crypto.SHA1: + h = sha1.New() + case crypto.SHA256: + h = sha256.New() + default: + return "", ErrSignature + } + writeBaseString(h, r.method, r.u, r.form, params) + d, err := rsa.SignPKCS1v15(rand.Reader, key, hf, h.Sum(nil)) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(d), nil +} + // SignForm adds an OAuth signature to form. The urlStr argument must not // include a query string. // @@ -407,15 +512,15 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { // information about transmitting OAuth parameters in a request body and // http://tools.ietf.org/html/rfc5849#section-3.5.2 for information about // transmitting OAuth parameters in a query string. -func (c *Client) SignForm(credentials *Credentials, method, urlStr string, form url.Values) error { +func (c *Client) SignForm(cred *Credentials, method, urlStr string, form url.Values) error { u, err := url.Parse(urlStr) switch { case err != nil: return err case u.RawQuery != "": - return errors.New("oauth: urlStr argument to SignForm must not include a query string") + return ErrURL } - p, err := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: form}) + p, err := c.oauthParams(&request{credentials: cred, method: method, u: u, form: form}) if err != nil { return err } @@ -426,26 +531,27 @@ func (c *Client) SignForm(credentials *Credentials, method, urlStr string, form } // SignParam is deprecated. Use SignForm instead. -func (c *Client) SignParam(credentials *Credentials, method, urlStr string, params url.Values) { +func (c *Client) SignParam(cred *Credentials, method, urlStr string, params url.Values) { u, _ := url.Parse(urlStr) u.RawQuery = "" - p, _ := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: params}) + p, _ := c.oauthParams(&request{credentials: cred, method: method, u: u, form: params}) for k, v := range p { params.Set(k, v) } } var oauthKeys = []string{ - "oauth_consumer_key", - "oauth_nonce", - "oauth_signature", - "oauth_signature_method", - "oauth_timestamp", - "oauth_token", - "oauth_version", - "oauth_callback", - "oauth_verifier", - "oauth_session_handle", + ConsumerKeyParam, + NonceParam, + SignatureParam, + SignatureMethodParam, + TSParam, + TokenParam, + VersionParam, + CallbackParam, + VerifierParam, + SessionHandleParam, + BodyHashParam, } func (c *Client) authorizationHeader(r *request) (string, error) { @@ -458,7 +564,7 @@ func (c *Client) authorizationHeader(r *request) (string, error) { for _, k := range oauthKeys { if v, ok := p[k]; ok { if h == nil { - h = []byte(`OAuth `) + h = []byte(Header) } else { h = append(h, ", "...) } @@ -475,10 +581,10 @@ func (c *Client) authorizationHeader(r *request) (string, error) { // method, URL and parameters. // // AuthorizationHeader is deprecated. Use SetAuthorizationHeader instead. -func (c *Client) AuthorizationHeader(credentials *Credentials, method string, u *url.URL, params url.Values) string { +func (c *Client) AuthorizationHeader(cred *Credentials, method string, u *url.URL, params url.Values) string { // Signing a request can return an error. This method is deprecated because // this method does not return an error. - v, _ := c.authorizationHeader(&request{credentials: credentials, method: method, u: u, form: params}) + v, _ := c.authorizationHeader(&request{credentials: cred, method: method, u: u, form: params}) return v } @@ -486,13 +592,22 @@ func (c *Client) AuthorizationHeader(credentials *Credentials, method string, u // // See http://tools.ietf.org/html/rfc5849#section-3.5.1 for information about // transmitting OAuth parameters in an HTTP request header. -func (c *Client) SetAuthorizationHeader(header http.Header, credentials *Credentials, method string, u *url.URL, form url.Values) error { - v, err := c.authorizationHeader(&request{credentials: credentials, method: method, u: u, form: form}) +func (c *Client) SetAuthorizationHeader(h http.Header, cred *Credentials, method string, u *url.URL, q url.Values) error { + return c.setHTTPHeader(h, &request{credentials: cred, method: method, u: u, form: q}) +} + +// SetAuthorizationHeaderWithBody does the same job than SetAuthorizationHeader and also manages the body hash. +func (c *Client) SetAuthorizationHeaderWithBody(h http.Header, cred *Credentials, method string, u *url.URL, q url.Values, body io.Reader) error { + return c.setHTTPHeader(h, &request{credentials: cred, method: method, u: u, form: q, body: body}) +} + +func (c *Client) setHTTPHeader(h http.Header, req *request) (err error) { + v, err := c.authorizationHeader(req) if err != nil { - return err + return } - header.Set("Authorization", v) - return nil + h.Set(HTTPHeader, v) + return } func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Response, error) { @@ -505,7 +620,7 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo return nil, err } if req.URL.RawQuery != "" { - return nil, errors.New("oauth: url must not contain a query string") + return nil, ErrURL } for k, v := range c.Header { req.Header[k] = v @@ -515,7 +630,7 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo if err != nil { return nil, err } - req.Header.Set("Authorization", auth) + req.Header.Set(HTTPHeader, auth) if r.method == http.MethodGet { req.URL.RawQuery = r.form.Encode() } else { @@ -527,47 +642,47 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo } // Get issues a GET to the specified URL with form added as a query string. -func (c *Client) Get(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Get(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.GetContext(ctx, credentials, urlStr, form) + return c.GetContext(ctx, cred, urlStr, form) } // GetContext uses Context to perform Get. -func (c *Client) GetContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodGet, credentials: credentials, form: form}) +func (c *Client) GetContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodGet, credentials: cred, form: form}) } // Post issues a POST with the specified form. -func (c *Client) Post(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Post(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.PostContext(ctx, credentials, urlStr, form) + return c.PostContext(ctx, cred, urlStr, form) } // PostContext uses Context to perform Post. -func (c *Client) PostContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodPost, credentials: credentials, form: form}) +func (c *Client) PostContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodPost, credentials: cred, form: form}) } // Delete issues a DELETE with the specified form. -func (c *Client) Delete(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Delete(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.DeleteContext(ctx, credentials, urlStr, form) + return c.DeleteContext(ctx, cred, urlStr, form) } // DeleteContext uses Context to perform Delete. -func (c *Client) DeleteContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodDelete, credentials: credentials, form: form}) +func (c *Client) DeleteContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodDelete, credentials: cred, form: form}) } // Put issues a PUT with the specified form. -func (c *Client) Put(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Put(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.PutContext(ctx, credentials, urlStr, form) + return c.PutContext(ctx, cred, urlStr, form) } // PutContext uses Context to perform Put. -func (c *Client) PutContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodPut, credentials: credentials, form: form}) +func (c *Client) PutContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodPut, credentials: cred, form: form}) } func (c *Client) requestCredentials(ctx context.Context, u string, r *request) (*Credentials, url.Values, error) { @@ -579,29 +694,27 @@ func (c *Client) requestCredentials(ctx context.Context, u string, r *request) ( return nil, nil, err } p, err := ioutil.ReadAll(resp.Body) - resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() if err != nil { - return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, - Body: p, msg: err.Error()} + return nil, nil, newRequestCredentialsError(err.Error(), resp, p) } if resp.StatusCode != 200 && resp.StatusCode != 201 { - return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, - Body: p, msg: fmt.Sprintf("OAuth server status %d, %s", resp.StatusCode, string(p))} + msg := fmt.Sprintf("OAuth server status %d, %s", resp.StatusCode, p) + return nil, nil, newRequestCredentialsError(msg, resp, p) } m, err := url.ParseQuery(string(p)) if err != nil { - return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, - Body: p, msg: err.Error()} + return nil, nil, newRequestCredentialsError(err.Error(), resp, p) } - tokens := m["oauth_token"] + tokens := m[TokenParam] if len(tokens) == 0 || tokens[0] == "" { - return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, - Body: p, msg: "oauth: token missing from server result"} + return nil, nil, newRequestCredentialsError("oauth: token missing from server result", resp, p) } - secrets := m["oauth_token_secret"] + secrets := m[TokenSecretParam] if len(secrets) == 0 { // allow "" as a valid secret. - return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, - Body: p, msg: "oauth: secret missing from server result"} + return nil, nil, newRequestCredentialsError("oauth: secret missing from server result", resp, p) } return &Credentials{Token: tokens[0], Secret: secrets[0]}, m, nil } @@ -621,59 +734,59 @@ func (c *Client) RequestTemporaryCredentialsContext(ctx context.Context, callbac return credentials, err } -// RequestToken requests token credentials from the server. See -// http://tools.ietf.org/html/rfc5849#section-2.3 for information about token +// RequestToken requests token credentials from the server. +// See http://tools.ietf.org/html/rfc5849#section-2.3 for information about token // credentials. -func (c *Client) RequestToken(client *http.Client, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) { +func (c *Client) RequestToken(client *http.Client, tmpCred *Credentials, verifier string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RequestTokenContext(ctx, temporaryCredentials, verifier) + return c.RequestTokenContext(ctx, tmpCred, verifier) } // RequestTokenContext uses Context to perform RequestToken. -func (c *Client) RequestTokenContext(ctx context.Context, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenContext(ctx context.Context, tmpCred *Credentials, verifier string) (*Credentials, url.Values, error) { return c.requestCredentials(ctx, c.TokenRequestURI, - &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, verifier: verifier}) + &request{credentials: tmpCred, method: c.TokenCredentialsMethod, verifier: verifier}) } // RenewRequestCredentials requests new token credentials from the server. // See http://wiki.oauth.net/w/page/12238549/ScalableOAuth#AccessTokenRenewal // for information about access token renewal. -func (c *Client) RenewRequestCredentials(client *http.Client, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) { +func (c *Client) RenewRequestCredentials(client *http.Client, cred *Credentials, sessionHandle string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RenewRequestCredentialsContext(ctx, credentials, sessionHandle) + return c.RenewRequestCredentialsContext(ctx, cred, sessionHandle) } // RenewRequestCredentialsContext uses Context to perform RenewRequestCredentials. -func (c *Client) RenewRequestCredentialsContext(ctx context.Context, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) { - return c.requestCredentials(ctx, c.RenewCredentialRequestURI, &request{credentials: credentials, sessionHandle: sessionHandle}) +func (c *Client) RenewRequestCredentialsContext(ctx context.Context, cred *Credentials, sessionHandle string) (*Credentials, url.Values, error) { + return c.requestCredentials(ctx, c.RenewCredentialRequestURI, &request{credentials: cred, sessionHandle: sessionHandle}) } // RequestTokenXAuth requests token credentials from the server using the xAuth protocol. // See https://dev.twitter.com/oauth/xauth for information on xAuth. -func (c *Client) RequestTokenXAuth(client *http.Client, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenXAuth(client *http.Client, tmpCred *Credentials, user, password string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RequestTokenXAuthContext(ctx, temporaryCredentials, user, password) + return c.RequestTokenXAuthContext(ctx, tmpCred, user, password) } // RequestTokenXAuthContext uses Context to perform RequestTokenXAuth. -func (c *Client) RequestTokenXAuthContext(ctx context.Context, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenXAuthContext(ctx context.Context, tmpCred *Credentials, user, password string) (*Credentials, url.Values, error) { form := make(url.Values) form.Set("x_auth_mode", "client_auth") form.Set("x_auth_username", user) form.Set("x_auth_password", password) return c.requestCredentials(ctx, c.TokenRequestURI, - &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, form: form}) + &request{credentials: tmpCred, method: c.TokenCredentialsMethod, form: form}) } -// AuthorizationURL returns the URL for resource owner authorization. See -// http://tools.ietf.org/html/rfc5849#section-2.2 for information about +// AuthorizationURL returns the URL for resource owner authorization. +// See http://tools.ietf.org/html/rfc5849#section-2.2 for information about // resource owner authorization. -func (c *Client) AuthorizationURL(temporaryCredentials *Credentials, additionalParams url.Values) string { +func (c *Client) AuthorizationURL(tmpCred *Credentials, additionalParams url.Values) string { params := make(url.Values) for k, vs := range additionalParams { params[k] = vs } - params.Set("oauth_token", temporaryCredentials.Token) + params.Set(TokenParam, tmpCred.Token) return c.ResourceOwnerAuthorizationURI + "?" + params.Encode() } @@ -691,16 +804,3 @@ func contextClient(ctx context.Context) *http.Client { } return http.DefaultClient } - -// RequestCredentialsError is an error containing -// response information when requesting credentials. -type RequestCredentialsError struct { - StatusCode int - Header http.Header - Body []byte - msg string -} - -func (e RequestCredentialsError) Error() string { - return e.msg -} diff --git a/oauth/oauth_test.go b/oauth/oauth_test.go index ac4a592..b4de140 100644 --- a/oauth/oauth_test.go +++ b/oauth/oauth_test.go @@ -294,7 +294,7 @@ func TestRequestToken(t *testing.T) { defer ts.Close() for _, method = range []string{"", "GET", "POST"} { - c := Client{TokenRequestURI: ts.URL, TokenCredentailsMethod: method} + c := Client{TokenRequestURI: ts.URL, TokenCredentialsMethod: method} cred, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier") if err != nil { t.Errorf("returned error %v", err) From c7207a9844caf5613f04efea653fbd759d0ad7bf Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 12:17:45 +0200 Subject: [PATCH 2/9] rsa-sha256 + oauth_body_hash --- README.markdown | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index e830f50..e7df769 100644 --- a/README.markdown +++ b/README.markdown @@ -3,17 +3,23 @@ [![GoDoc](https://godoc.org/github.com/gomodule/oauth1/oauth?status.svg)](https://godoc.org/github.com/gomodule/oauth1/oauth) [![Build Status](https://travis-ci.org/gomodule/oauth1.svg?branch=master)](https://travis-ci.org/gomodule/oauth1) -OAuth1 is a [Go](https://golang.org/) client for the OAuth 1.0, OAuth 1.0a and -[RFC 5849](https://tools.ietf.org/html/rfc5849) Protocols. The package supports -HMAC-SHA1, RSA-SHA1 and PLAINTEXT signatures. +OAuth1 is a [Go](https://golang.org/) client for the OAuth 1.0, OAuth 1.0a and [RFC 5849](https://tools.ietf.org/html/rfc5849) Protocols. +The package supports the following signatures: +* HMAC-SHA1 +* HMAC-SHA256 +* RSA-SHA1 +* PLAINTEXT + ## Installation - go get github.com/gomodule/oauth1/oauth +```bash +$ go get -u github.com/gomodule/oauth1/oauth +``` ## License -oauth1 is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). +`oauth1` is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). ## Documentation From fbeff043c5490ad7ff05fceaf326550b5e94cd6d Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 13:43:46 +0200 Subject: [PATCH 3/9] golang.org/x/net/context > context go1.7+ --- oauth/oauth_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oauth/oauth_test.go b/oauth/oauth_test.go index b4de140..ba50001 100644 --- a/oauth/oauth_test.go +++ b/oauth/oauth_test.go @@ -16,6 +16,7 @@ package oauth import ( "bytes" + "context" "crypto/x509" "encoding/pem" "io" @@ -26,8 +27,6 @@ import ( "net/url" "strings" "testing" - - "golang.org/x/net/context" ) func parseURL(urlStr string) *url.URL { From b43e4ec7ec2cd94d830290531ce1969027f0cd9a Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 13:44:07 +0200 Subject: [PATCH 4/9] golint --- oauth/examples_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/oauth/examples_test.go b/oauth/examples_test.go index 4f5ec05..89b6e68 100644 --- a/oauth/examples_test.go +++ b/oauth/examples_test.go @@ -51,7 +51,9 @@ func ExampleClient_SetAuthorizationHeader(client *oauth.Client, credentials *oau if err != nil { return err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // process the response return nil } From a3e7377de98785ed530bc28f797f8077add089de Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 13:45:03 +0200 Subject: [PATCH 5/9] golang.org/x/net/context > context go1.7+ --- go.mod | 5 +---- go.sum | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 80fd254..cb4970e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,3 @@ module github.com/gomodule/oauth1 -require ( - golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 - google.golang.org/appengine v1.5.0 -) +require google.golang.org/appengine v1.5.0 diff --git a/go.sum b/go.sum index 0fd2110..26529e0 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,7 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= -golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= From eb1fe7bb95d3ef8f269b72898c21ba2e63490f7a Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 13:45:16 +0200 Subject: [PATCH 6/9] golang.org/x/net/context > context go1.7+ --- examples/appengine/app.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/appengine/app.go b/examples/appengine/app.go index 20be8cc..ec8539a 100644 --- a/examples/appengine/app.go +++ b/examples/appengine/app.go @@ -15,14 +15,13 @@ package app import ( + "context" "encoding/json" "fmt" "io/ioutil" "net/http" "text/template" - "golang.org/x/net/context" - "github.com/gomodule/oauth1/oauth" "google.golang.org/appengine" From f6134d80109bc548ecd2e1db734a61408e2b41c9 Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 14 May 2019 13:45:37 +0200 Subject: [PATCH 7/9] rsa-sha256 + oauth_body_hash --- README.markdown | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.markdown b/README.markdown index e7df769..e5a5e21 100644 --- a/README.markdown +++ b/README.markdown @@ -15,8 +15,18 @@ The package supports the following signatures: ```bash $ go get -u github.com/gomodule/oauth1/oauth +``` + +Import it in your code: + +```go +import "github.com/gomodule/oauth1/oauth" ``` +### Prerequisite + +`oauth1` uses the context package requiring Go 1.7 or later. + ## License `oauth1` is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). From b04134e6dd5a20824a53824d524f7be1678a56cb Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 28 May 2019 11:44:42 +0200 Subject: [PATCH 8/9] review: puts back all lint errors and misspell --- oauth/examples_test.go | 5 +- oauth/oauth.go | 214 +++++++++++++++++------------------------ oauth/oauth_test.go | 2 +- 3 files changed, 92 insertions(+), 129 deletions(-) diff --git a/oauth/examples_test.go b/oauth/examples_test.go index 89b6e68..7e44a93 100644 --- a/oauth/examples_test.go +++ b/oauth/examples_test.go @@ -51,9 +51,8 @@ func ExampleClient_SetAuthorizationHeader(client *oauth.Client, credentials *oau if err != nil { return err } - defer func() { - _ = resp.Body.Close() - }() + defer resp.Body.Close() + // process the response return nil } diff --git a/oauth/oauth.go b/oauth/oauth.go index f6bd251..b3c1bd0 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -49,7 +49,7 @@ // an authenticated request by encoding the modified form to the query string // or request body. // -// The SetAuthorizationHeader method adds an OAuth siganture to a request +// The SetAuthorizationHeader method adds an OAuth signature to a request // header. The SetAuthorizationHeader method is the only way to correctly sign // a request if the application sets the URL Opaque field when making a // request. @@ -94,31 +94,6 @@ import ( "time" ) -// Lists of supported HTTP header and OAuth parameters. -const ( - HTTPHeader = "Authorization" - Header = "OAuth " - BodyHashParam = "oauth_body_hash" - CallbackParam = "oauth_callback" - ConsumerKeyParam = "oauth_consumer_key" - NonceParam = "oauth_nonce" - SessionHandleParam = "oauth_session_handle" - SignatureParam = "oauth_signature" - SignatureMethodParam = "oauth_signature_method" - TSParam = "oauth_timestamp" - TokenParam = "oauth_token" - TokenSecretParam = "oauth_token_secret" - VerifierParam = "oauth_verifier" - VersionParam = "oauth_version" -) - -// List of common errors -var ( - ErrPrivateKey = errors.New("oauth: private key not set") - ErrSignature = errors.New("oauth: unknown signature method") - ErrURL = errors.New("oauth: url must not contain a query string") -) - func newRequestCredentialsError(msg string, resp *http.Response, body []byte) RequestCredentialsError { return RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, Body: body, msg: msg} } @@ -218,8 +193,8 @@ func (p byKeyValue) appendValues(values url.Values) byKeyValue { // base string computation described in section 3.4.1 of the RFC. func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oauthParams map[string]string) { // Method - write(w, encode(strings.ToUpper(method), false)) - write(w, []byte{'&'}) + w.Write(encode(strings.ToUpper(method), false)) + w.Write([]byte{'&'}) // URL scheme := strings.ToLower(u.Scheme) @@ -236,11 +211,11 @@ func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oa host = host[:len(host)-len(":443")] } - write(w, encode(scheme, false)) - write(w, encode("://", false)) - write(w, encode(host, false)) - write(w, encode(path, false)) - write(w, []byte{'&'}) + w.Write(encode(scheme, false)) + w.Write(encode("://", false)) + w.Write(encode(host, false)) + w.Write(encode(path, false)) + w.Write([]byte{'&'}) // Create sorted slice of encoded parameters. Parameter keys and values are // double encoded in a single step. This is safe because double encoding @@ -260,20 +235,13 @@ func writeBaseString(w io.Writer, method string, u *url.URL, form url.Values, oa sep := false for _, kv := range p { if sep { - write(w, encodedAmp) + w.Write(encodedAmp) } else { sep = true } - write(w, kv.key) - write(w, encodedEqual) - write(w, kv.value) - } -} - -func write(to io.Writer, data []byte) { - _, err := to.Write(data) - if err != nil { - panic(err) + w.Write(kv.key) + w.Write(encodedEqual) + w.Write(kv.value) } } @@ -294,11 +262,7 @@ func nonce() string { // SignatureMethod identifies a signature method. type SignatureMethod int -func (sm SignatureMethod) hash(r io.Reader) ([]byte, error) { - b, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } +func (sm SignatureMethod) hash(b []byte) ([]byte, error) { var h hash.Hash switch sm { case HMACSHA1, RSASHA1: @@ -308,7 +272,7 @@ func (sm SignatureMethod) hash(r io.Reader) ([]byte, error) { default: return nil, nil } - _, err = h.Write(b) + _, err := h.Write(b) if err != nil { return nil, err } @@ -326,7 +290,7 @@ func (sm SignatureMethod) String() string { case PLAINTEXT: return "PLAINTEXT" default: - return "UNKNOWN" + return "unknown" } } @@ -378,10 +342,10 @@ type Client struct { // string, then POST is used. TemporaryCredentialsMethod string - // TokenCredentialsMethod is the HTTP method used by the client to request + // TokenCredentailsMethod is the HTTP method used by the client to request // a set of token credentials. If this field is the empty string, then POST // is used. - TokenCredentialsMethod string + TokenCredentailsMethod string // Header specifies optional extra headers for requests. Header http.Header @@ -400,7 +364,7 @@ type request struct { method string u *url.URL form url.Values - body io.Reader + body []byte sessionHandle string verifier string callbackURL string @@ -413,25 +377,25 @@ var testHook = func(map[string]string) {} // See http://tools.ietf.org/html/rfc5849#section-3.4 for more information about signatures. func (c *Client) oauthParams(r *request) (map[string]string, error) { oauthParams := map[string]string{ - ConsumerKeyParam: c.Credentials.Token, - SignatureMethodParam: c.SignatureMethod.String(), - VersionParam: "1.0", + "oauth_consumer_key": c.Credentials.Token, + "oauth_signature_method": c.SignatureMethod.String(), + "oauth_version": "1.0", } if c.SignatureMethod != PLAINTEXT { - oauthParams[TSParam] = strconv.FormatInt(time.Now().Unix(), 10) - oauthParams[NonceParam] = nonce() + oauthParams["oauth_timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) + oauthParams["oauth_nonce"] = nonce() } if r.credentials != nil { - oauthParams[TokenParam] = r.credentials.Token + oauthParams["oauth_token"] = r.credentials.Token } if r.verifier != "" { - oauthParams[VerifierParam] = r.verifier + oauthParams["oauth_verifier"] = r.verifier } if r.sessionHandle != "" { - oauthParams[SessionHandleParam] = r.sessionHandle + oauthParams["oauth_session_handle"] = r.sessionHandle } if r.callbackURL != "" { - oauthParams[CallbackParam] = r.callbackURL + oauthParams["oauth_callback"] = r.callbackURL } if r.body != nil { src, err := c.SignatureMethod.hash(r.body) @@ -439,7 +403,7 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { return nil, err } if src != nil { - oauthParams[BodyHashParam] = base64.StdEncoding.EncodeToString(src) + oauthParams["oauth_body_hash"] = base64.StdEncoding.EncodeToString(src) } } @@ -477,7 +441,7 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { } signature = string(rawSignature) default: - return nil, ErrSignature + return nil, errors.New("oauth: unknown signature method") } oauthParams["oauth_signature"] = signature @@ -486,7 +450,7 @@ func (c *Client) oauthParams(r *request) (map[string]string, error) { func rsaSignAndEncode(key *rsa.PrivateKey, hf crypto.Hash, r *request, params map[string]string) (string, error) { if key == nil { - return "", ErrPrivateKey + return "", errors.New("oauth: private key not set") } var h hash.Hash switch hf { @@ -495,7 +459,7 @@ func rsaSignAndEncode(key *rsa.PrivateKey, hf crypto.Hash, r *request, params ma case crypto.SHA256: h = sha256.New() default: - return "", ErrSignature + return "", errors.New("oauth: unknown signature method") } writeBaseString(h, r.method, r.u, r.form, params) d, err := rsa.SignPKCS1v15(rand.Reader, key, hf, h.Sum(nil)) @@ -512,15 +476,15 @@ func rsaSignAndEncode(key *rsa.PrivateKey, hf crypto.Hash, r *request, params ma // information about transmitting OAuth parameters in a request body and // http://tools.ietf.org/html/rfc5849#section-3.5.2 for information about // transmitting OAuth parameters in a query string. -func (c *Client) SignForm(cred *Credentials, method, urlStr string, form url.Values) error { +func (c *Client) SignForm(credentials *Credentials, method, urlStr string, form url.Values) error { u, err := url.Parse(urlStr) switch { case err != nil: return err case u.RawQuery != "": - return ErrURL + return errors.New("oauth: urlStr argument to SignForm must not include a query string") } - p, err := c.oauthParams(&request{credentials: cred, method: method, u: u, form: form}) + p, err := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: form}) if err != nil { return err } @@ -531,27 +495,27 @@ func (c *Client) SignForm(cred *Credentials, method, urlStr string, form url.Val } // SignParam is deprecated. Use SignForm instead. -func (c *Client) SignParam(cred *Credentials, method, urlStr string, params url.Values) { +func (c *Client) SignParam(credentials *Credentials, method, urlStr string, params url.Values) { u, _ := url.Parse(urlStr) u.RawQuery = "" - p, _ := c.oauthParams(&request{credentials: cred, method: method, u: u, form: params}) + p, _ := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: params}) for k, v := range p { params.Set(k, v) } } var oauthKeys = []string{ - ConsumerKeyParam, - NonceParam, - SignatureParam, - SignatureMethodParam, - TSParam, - TokenParam, - VersionParam, - CallbackParam, - VerifierParam, - SessionHandleParam, - BodyHashParam, + "oauth_consumer_key", + "oauth_nonce", + "oauth_signature", + "oauth_signature_method", + "oauth_timestamp", + "oauth_token", + "oauth_version", + "oauth_callback", + "oauth_verifier", + "oauth_session_handle", + "oauth_body_hash", } func (c *Client) authorizationHeader(r *request) (string, error) { @@ -564,7 +528,7 @@ func (c *Client) authorizationHeader(r *request) (string, error) { for _, k := range oauthKeys { if v, ok := p[k]; ok { if h == nil { - h = []byte(Header) + h = []byte("OAuth ") } else { h = append(h, ", "...) } @@ -581,10 +545,10 @@ func (c *Client) authorizationHeader(r *request) (string, error) { // method, URL and parameters. // // AuthorizationHeader is deprecated. Use SetAuthorizationHeader instead. -func (c *Client) AuthorizationHeader(cred *Credentials, method string, u *url.URL, params url.Values) string { +func (c *Client) AuthorizationHeader(credentials *Credentials, method string, u *url.URL, params url.Values) string { // Signing a request can return an error. This method is deprecated because // this method does not return an error. - v, _ := c.authorizationHeader(&request{credentials: cred, method: method, u: u, form: params}) + v, _ := c.authorizationHeader(&request{credentials: credentials, method: method, u: u, form: params}) return v } @@ -592,13 +556,13 @@ func (c *Client) AuthorizationHeader(cred *Credentials, method string, u *url.UR // // See http://tools.ietf.org/html/rfc5849#section-3.5.1 for information about // transmitting OAuth parameters in an HTTP request header. -func (c *Client) SetAuthorizationHeader(h http.Header, cred *Credentials, method string, u *url.URL, q url.Values) error { - return c.setHTTPHeader(h, &request{credentials: cred, method: method, u: u, form: q}) +func (c *Client) SetAuthorizationHeader(h http.Header, credentials *Credentials, method string, u *url.URL, q url.Values) error { + return c.setHTTPHeader(h, &request{credentials: credentials, method: method, u: u, form: q}) } // SetAuthorizationHeaderWithBody does the same job than SetAuthorizationHeader and also manages the body hash. -func (c *Client) SetAuthorizationHeaderWithBody(h http.Header, cred *Credentials, method string, u *url.URL, q url.Values, body io.Reader) error { - return c.setHTTPHeader(h, &request{credentials: cred, method: method, u: u, form: q, body: body}) +func (c *Client) SetAuthorizationHeaderWithBody(h http.Header, credentials *Credentials, method string, u *url.URL, q url.Values, body []byte) error { + return c.setHTTPHeader(h, &request{credentials: credentials, method: method, u: u, form: q, body: body}) } func (c *Client) setHTTPHeader(h http.Header, req *request) (err error) { @@ -606,7 +570,7 @@ func (c *Client) setHTTPHeader(h http.Header, req *request) (err error) { if err != nil { return } - h.Set(HTTPHeader, v) + h.Set("Authorization", v) return } @@ -620,7 +584,7 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo return nil, err } if req.URL.RawQuery != "" { - return nil, ErrURL + return nil, errors.New("oauth: url must not contain a query string") } for k, v := range c.Header { req.Header[k] = v @@ -630,7 +594,7 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo if err != nil { return nil, err } - req.Header.Set(HTTPHeader, auth) + req.Header.Set("Authorization", auth) if r.method == http.MethodGet { req.URL.RawQuery = r.form.Encode() } else { @@ -642,47 +606,47 @@ func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Respo } // Get issues a GET to the specified URL with form added as a query string. -func (c *Client) Get(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Get(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.GetContext(ctx, cred, urlStr, form) + return c.GetContext(ctx, credentials, urlStr, form) } // GetContext uses Context to perform Get. -func (c *Client) GetContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodGet, credentials: cred, form: form}) +func (c *Client) GetContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodGet, credentials: credentials, form: form}) } // Post issues a POST with the specified form. -func (c *Client) Post(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Post(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.PostContext(ctx, cred, urlStr, form) + return c.PostContext(ctx, credentials, urlStr, form) } // PostContext uses Context to perform Post. -func (c *Client) PostContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodPost, credentials: cred, form: form}) +func (c *Client) PostContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodPost, credentials: credentials, form: form}) } // Delete issues a DELETE with the specified form. -func (c *Client) Delete(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Delete(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.DeleteContext(ctx, cred, urlStr, form) + return c.DeleteContext(ctx, credentials, urlStr, form) } // DeleteContext uses Context to perform Delete. -func (c *Client) DeleteContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodDelete, credentials: cred, form: form}) +func (c *Client) DeleteContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodDelete, credentials: credentials, form: form}) } // Put issues a PUT with the specified form. -func (c *Client) Put(client *http.Client, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { +func (c *Client) Put(client *http.Client, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.PutContext(ctx, cred, urlStr, form) + return c.PutContext(ctx, credentials, urlStr, form) } // PutContext uses Context to perform Put. -func (c *Client) PutContext(ctx context.Context, cred *Credentials, urlStr string, form url.Values) (*http.Response, error) { - return c.do(ctx, urlStr, &request{method: http.MethodPut, credentials: cred, form: form}) +func (c *Client) PutContext(ctx context.Context, credentials *Credentials, urlStr string, form url.Values) (*http.Response, error) { + return c.do(ctx, urlStr, &request{method: http.MethodPut, credentials: credentials, form: form}) } func (c *Client) requestCredentials(ctx context.Context, u string, r *request) (*Credentials, url.Values, error) { @@ -708,11 +672,11 @@ func (c *Client) requestCredentials(ctx context.Context, u string, r *request) ( if err != nil { return nil, nil, newRequestCredentialsError(err.Error(), resp, p) } - tokens := m[TokenParam] + tokens := m["oauth_token"] if len(tokens) == 0 || tokens[0] == "" { return nil, nil, newRequestCredentialsError("oauth: token missing from server result", resp, p) } - secrets := m[TokenSecretParam] + secrets := m["oauth_token_secret"] if len(secrets) == 0 { // allow "" as a valid secret. return nil, nil, newRequestCredentialsError("oauth: secret missing from server result", resp, p) } @@ -737,56 +701,56 @@ func (c *Client) RequestTemporaryCredentialsContext(ctx context.Context, callbac // RequestToken requests token credentials from the server. // See http://tools.ietf.org/html/rfc5849#section-2.3 for information about token // credentials. -func (c *Client) RequestToken(client *http.Client, tmpCred *Credentials, verifier string) (*Credentials, url.Values, error) { +func (c *Client) RequestToken(client *http.Client, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RequestTokenContext(ctx, tmpCred, verifier) + return c.RequestTokenContext(ctx, temporaryCredentials, verifier) } // RequestTokenContext uses Context to perform RequestToken. -func (c *Client) RequestTokenContext(ctx context.Context, tmpCred *Credentials, verifier string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenContext(ctx context.Context, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) { return c.requestCredentials(ctx, c.TokenRequestURI, - &request{credentials: tmpCred, method: c.TokenCredentialsMethod, verifier: verifier}) + &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, verifier: verifier}) } // RenewRequestCredentials requests new token credentials from the server. // See http://wiki.oauth.net/w/page/12238549/ScalableOAuth#AccessTokenRenewal // for information about access token renewal. -func (c *Client) RenewRequestCredentials(client *http.Client, cred *Credentials, sessionHandle string) (*Credentials, url.Values, error) { +func (c *Client) RenewRequestCredentials(client *http.Client, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RenewRequestCredentialsContext(ctx, cred, sessionHandle) + return c.RenewRequestCredentialsContext(ctx, credentials, sessionHandle) } // RenewRequestCredentialsContext uses Context to perform RenewRequestCredentials. -func (c *Client) RenewRequestCredentialsContext(ctx context.Context, cred *Credentials, sessionHandle string) (*Credentials, url.Values, error) { - return c.requestCredentials(ctx, c.RenewCredentialRequestURI, &request{credentials: cred, sessionHandle: sessionHandle}) +func (c *Client) RenewRequestCredentialsContext(ctx context.Context, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) { + return c.requestCredentials(ctx, c.RenewCredentialRequestURI, &request{credentials: credentials, sessionHandle: sessionHandle}) } // RequestTokenXAuth requests token credentials from the server using the xAuth protocol. // See https://dev.twitter.com/oauth/xauth for information on xAuth. -func (c *Client) RequestTokenXAuth(client *http.Client, tmpCred *Credentials, user, password string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenXAuth(client *http.Client, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) - return c.RequestTokenXAuthContext(ctx, tmpCred, user, password) + return c.RequestTokenXAuthContext(ctx, temporaryCredentials, user, password) } // RequestTokenXAuthContext uses Context to perform RequestTokenXAuth. -func (c *Client) RequestTokenXAuthContext(ctx context.Context, tmpCred *Credentials, user, password string) (*Credentials, url.Values, error) { +func (c *Client) RequestTokenXAuthContext(ctx context.Context, temporaryCredentials *Credentials, user, password string) (*Credentials, url.Values, error) { form := make(url.Values) form.Set("x_auth_mode", "client_auth") form.Set("x_auth_username", user) form.Set("x_auth_password", password) return c.requestCredentials(ctx, c.TokenRequestURI, - &request{credentials: tmpCred, method: c.TokenCredentialsMethod, form: form}) + &request{credentials: temporaryCredentials, method: c.TokenCredentailsMethod, form: form}) } // AuthorizationURL returns the URL for resource owner authorization. // See http://tools.ietf.org/html/rfc5849#section-2.2 for information about // resource owner authorization. -func (c *Client) AuthorizationURL(tmpCred *Credentials, additionalParams url.Values) string { +func (c *Client) AuthorizationURL(temporaryCredentials *Credentials, additionalParams url.Values) string { params := make(url.Values) for k, vs := range additionalParams { params[k] = vs } - params.Set(TokenParam, tmpCred.Token) + params.Set("oauth_token", temporaryCredentials.Token) return c.ResourceOwnerAuthorizationURI + "?" + params.Encode() } diff --git a/oauth/oauth_test.go b/oauth/oauth_test.go index ba50001..29b8c3a 100644 --- a/oauth/oauth_test.go +++ b/oauth/oauth_test.go @@ -293,7 +293,7 @@ func TestRequestToken(t *testing.T) { defer ts.Close() for _, method = range []string{"", "GET", "POST"} { - c := Client{TokenRequestURI: ts.URL, TokenCredentialsMethod: method} + c := Client{TokenRequestURI: ts.URL, TokenCredentailsMethod: method} cred, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier") if err != nil { t.Errorf("returned error %v", err) From 08d4fdf30bfbc56f9fdff75681b5ca098b063c1f Mon Sep 17 00:00:00 2001 From: "herve.gouchet" Date: Tue, 28 May 2019 12:18:53 +0200 Subject: [PATCH 9/9] x/context dependency: only for appengine example --- examples/appengine/app.go | 3 ++- go.mod | 6 +++++- go.sum | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/appengine/app.go b/examples/appengine/app.go index ec8539a..ba63cb3 100644 --- a/examples/appengine/app.go +++ b/examples/appengine/app.go @@ -15,7 +15,6 @@ package app import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -24,6 +23,8 @@ import ( "github.com/gomodule/oauth1/oauth" + "golang.org/x/net/context" + "google.golang.org/appengine" "google.golang.org/appengine/datastore" "google.golang.org/appengine/log" diff --git a/go.mod b/go.mod index cb4970e..eabd97c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module github.com/gomodule/oauth1 -require google.golang.org/appengine v1.5.0 +require ( + golang.org/x/net v0.0.0-20180724234803-3673e40ba225 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect + google.golang.org/appengine v1.5.0 +) diff --git a/go.sum b/go.sum index 26529e0..7f71a76 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=