From 1df3bd683719c4bfeb4d1a25cc2066b4d2ef3f5f Mon Sep 17 00:00:00 2001 From: howardhuang Date: Tue, 29 Aug 2023 16:36:09 +0800 Subject: [PATCH 01/10] feat: export auther type --- auther.go | 25 +++++++++++++------------ auther_test.go | 22 +++++++++++----------- config.go | 6 +++--- reference_test.go | 12 ++++++------ transport.go | 4 ++-- transport_test.go | 6 +++--- verifier.go | 9 +++++---- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/auther.go b/auther.go index 7ad7325..490e2bf 100644 --- a/auther.go +++ b/auther.go @@ -36,27 +36,28 @@ type clock interface { Now() time.Time } -// auther adds an "OAuth" Authorization header field to requests. -type auther struct { +// Auther adds an "OAuth" Authorization header field to requests. +type Auther struct { config *Config clock clock } -func newAuther(config *Config) *auther { +// NewAuther returns a new Auther +func NewAuther(config *Config) *Auther { if config == nil { config = &Config{} } if config.Noncer == nil { config.Noncer = Base64Noncer{} } - return &auther{ + return &Auther{ config: config, } } // setRequestTokenAuthHeader adds the OAuth1 header for the request token // request (temporary credential) according to RFC 5849 2.1. -func (a *auther) setRequestTokenAuthHeader(req *http.Request) error { +func (a *Auther) setRequestTokenAuthHeader(req *http.Request) error { oauthParams := a.commonOAuthParams() oauthParams[oauthCallbackParam] = a.config.CallbackURL params, err := collectParameters(req, oauthParams) @@ -78,7 +79,7 @@ func (a *auther) setRequestTokenAuthHeader(req *http.Request) error { // setAccessTokenAuthHeader sets the OAuth1 header for the access token request // (token credential) according to RFC 5849 2.3. -func (a *auther) setAccessTokenAuthHeader(req *http.Request, requestToken, requestSecret, verifier string) error { +func (a *Auther) setAccessTokenAuthHeader(req *http.Request, requestToken, requestSecret, verifier string) error { oauthParams := a.commonOAuthParams() oauthParams[oauthTokenParam] = requestToken oauthParams[oauthVerifierParam] = verifier @@ -96,9 +97,9 @@ func (a *auther) setAccessTokenAuthHeader(req *http.Request, requestToken, reque return nil } -// setRequestAuthHeader sets the OAuth1 header for making authenticated +// SetRequestAuthHeader sets the OAuth1 header for making authenticated // requests with an AccessToken (token credential) according to RFC 5849 3.1. -func (a *auther) setRequestAuthHeader(req *http.Request, accessToken *Token) error { +func (a *Auther) SetRequestAuthHeader(req *http.Request, accessToken *Token) error { oauthParams := a.commonOAuthParams() oauthParams[oauthTokenParam] = accessToken.Token params, err := collectParameters(req, oauthParams) @@ -119,7 +120,7 @@ func (a *auther) setRequestAuthHeader(req *http.Request, accessToken *Token) err // excluding the oauth_signature parameter. This includes the realm parameter // if it was set in the config. The realm parameter will not be included in // the signature base string as specified in RFC 5849 3.4.1.3.1. -func (a *auther) commonOAuthParams() map[string]string { +func (a *Auther) commonOAuthParams() map[string]string { params := map[string]string{ oauthConsumerKeyParam: a.config.ConsumerKey, oauthSignatureMethodParam: a.signer().Name(), @@ -134,12 +135,12 @@ func (a *auther) commonOAuthParams() map[string]string { } // Returns a nonce using the configured Noncer. -func (a *auther) nonce() string { +func (a *Auther) nonce() string { return a.config.Noncer.Nonce() } // Returns the Unix epoch seconds. -func (a *auther) epoch() int64 { +func (a *Auther) epoch() int64 { if a.clock != nil { return a.clock.Now().Unix() } @@ -147,7 +148,7 @@ func (a *auther) epoch() int64 { } // Returns the Config's Signer or the default Signer. -func (a *auther) signer() Signer { +func (a *Auther) signer() Signer { if a.config.Signer != nil { return a.config.Signer } diff --git a/auther_test.go b/auther_test.go index 9088e8e..c81910b 100644 --- a/auther_test.go +++ b/auther_test.go @@ -12,11 +12,11 @@ import ( func TestCommonOAuthParams(t *testing.T) { cases := []struct { - auther *auther + Auther *Auther expectedParams map[string]string }{ { - &auther{ + &Auther{ &Config{ ConsumerKey: "some_consumer_key", Noncer: &fixedNoncer{"some_nonce"}, @@ -32,7 +32,7 @@ func TestCommonOAuthParams(t *testing.T) { }, }, { - &auther{ + &Auther{ &Config{ ConsumerKey: "some_consumer_key", Realm: "photos", @@ -52,13 +52,13 @@ func TestCommonOAuthParams(t *testing.T) { } for _, c := range cases { - assert.Equal(t, c.expectedParams, c.auther.commonOAuthParams()) + assert.Equal(t, c.expectedParams, c.Auther.commonOAuthParams()) } } func TestNonce(t *testing.T) { - auther := newAuther(nil) - nonce := auther.nonce() + Auther := NewAuther(nil) + nonce := Auther.nonce() // assert that 32 bytes (256 bites) become 44 bytes since a base64 byte // zeros the 2 high bits. 3 bytes convert to 4 base64 bytes, 40 base64 bytes // represent the first 30 of 32 bytes, = padding adds another 4 byte group. @@ -67,17 +67,17 @@ func TestNonce(t *testing.T) { } func TestEpoch(t *testing.T) { - a := newAuther(nil) + a := NewAuther(nil) // assert that a real time is used by default assert.InEpsilon(t, time.Now().Unix(), a.epoch(), 1) // assert that the fixed clock can be used for testing - a = &auther{clock: &fixedClock{time.Unix(50037133, 0)}} + a = &Auther{clock: &fixedClock{time.Unix(50037133, 0)}} assert.Equal(t, int64(50037133), a.epoch()) } func TestSigner_Default(t *testing.T) { config := &Config{ConsumerSecret: "consumer_secret"} - a := newAuther(config) + a := NewAuther(config) // echo -n "hello world" | openssl dgst -sha1 -hmac "consumer_secret&token_secret" -binary | base64 expectedSignature := "BE0uILOruKfSXd4UzYlLJDfOq08=" // assert that the default signer produces the expected HMAC-SHA1 digest @@ -92,7 +92,7 @@ func TestSigner_SHA256(t *testing.T) { config := &Config{ Signer: &HMAC256Signer{ConsumerSecret: "consumer_secret"}, } - a := newAuther(config) + a := NewAuther(config) // echo -n "hello world" | openssl dgst -sha256 -hmac "consumer_secret&token_secret" -binary | base64 expectedSignature := "pW9drXUyErU8DASWbsP2I3XZbju37AW+VzcGdYSeMo8=" // assert that the signer produces the expected HMAC-SHA256 digest @@ -118,7 +118,7 @@ func TestSigner_Custom(t *testing.T) { ConsumerSecret: "consumer_secret", Signer: &identitySigner{}, } - a := newAuther(config) + a := NewAuther(config) // assert that the custom signer is used method := a.signer().Name() digest, err := a.signer().Sign("secret", "hello world") diff --git a/config.go b/config.go index c539a00..8824c88 100644 --- a/config.go +++ b/config.go @@ -53,7 +53,7 @@ func NewClient(ctx context.Context, config *Config, token *Token) *http.Client { transport := &Transport{ Base: contextTransport(ctx), source: StaticTokenSource(token), - auther: newAuther(config), + auther: NewAuther(config), } return &http.Client{Transport: transport} } @@ -69,7 +69,7 @@ func (c *Config) RequestToken() (requestToken, requestSecret string, err error) if err != nil { return "", "", err } - err = newAuther(c).setRequestTokenAuthHeader(req) + err = NewAuther(c).setRequestTokenAuthHeader(req) if err != nil { return "", "", err } @@ -148,7 +148,7 @@ func (c *Config) AccessToken(requestToken, requestSecret, verifier string) (acce if err != nil { return "", "", err } - err = newAuther(c).setAccessTokenAuthHeader(req, requestToken, requestSecret, verifier) + err = NewAuther(c).setAccessTokenAuthHeader(req, requestToken, requestSecret, verifier) if err != nil { return "", "", err } diff --git a/reference_test.go b/reference_test.go index 95d28cc..25506af 100644 --- a/reference_test.go +++ b/reference_test.go @@ -36,7 +36,7 @@ func TestTwitterRequestTokenAuthHeader(t *testing.T) { Noncer: &fixedNoncer{expectedNonce}, } - auther := &auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} + auther := &Auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} req, err := http.NewRequest("POST", config.Endpoint.RequestTokenURL, nil) assert.Nil(t, err) err = auther.setRequestTokenAuthHeader(req) @@ -74,7 +74,7 @@ func TestTwitterAccessTokenAuthHeader(t *testing.T) { Noncer: &fixedNoncer{expectedNonce}, } - auther := &auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} + auther := &Auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} req, err := http.NewRequest("POST", config.Endpoint.AccessTokenURL, nil) assert.Nil(t, err) err = auther.setAccessTokenAuthHeader(req, expectedRequestToken, requestTokenSecret, expectedVerifier) @@ -111,7 +111,7 @@ var twitterConfig = &Config{ } func TestTwitterParameterString(t *testing.T) { - auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") // note: the reference example is old and uses api v1 in the URL @@ -128,7 +128,7 @@ func TestTwitterParameterString(t *testing.T) { } func TestTwitterSignatureBase(t *testing.T) { - auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") // note: the reference example is old and uses api v1 in the URL @@ -151,7 +151,7 @@ func TestTwitterRequestAuthHeader(t *testing.T) { expectedSignature := PercentEncode("tnnArxj06cWHq44gCs1OSKk/jLY=") expectedTimestamp := "1318622958" - auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") @@ -159,7 +159,7 @@ func TestTwitterRequestAuthHeader(t *testing.T) { req, err := http.NewRequest("POST", "https://api.twitter.com/1/statuses/update.json?include_entities=true", strings.NewReader(values.Encode())) assert.Nil(t, err) req.Header.Set(contentType, formContentType) - err = auther.setRequestAuthHeader(req, accessToken) + err = auther.SetRequestAuthHeader(req, accessToken) // assert that request is signed and has an access token token assert.Nil(t, err) params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam)) diff --git a/transport.go b/transport.go index c1af993..6cb257b 100644 --- a/transport.go +++ b/transport.go @@ -18,7 +18,7 @@ type Transport struct { // source supplies the token to use when signing a request source TokenSource // auther adds OAuth1 Authorization headers to requests - auther *auther + auther *Auther } // RoundTrip authorizes the request with a signed OAuth1 Authorization header @@ -36,7 +36,7 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { } // RoundTripper should not modify the given request, clone it req2 := cloneRequest(req) - err = t.auther.setRequestAuthHeader(req2, accessToken) + err = t.auther.SetRequestAuthHeader(req2, accessToken) if err != nil { return nil, err } diff --git a/transport_test.go b/transport_test.go index 116c11b..94c1e10 100644 --- a/transport_test.go +++ b/transport_test.go @@ -34,7 +34,7 @@ func TestTransport(t *testing.T) { ConsumerSecret: "consumer_secret", Noncer: &fixedNoncer{expectedNonce}, } - auther := &auther{ + auther := &Auther{ config: config, clock: &fixedClock{time.Unix(123456789, 0)}, } @@ -68,7 +68,7 @@ func TestTransport_customBaseTransport(t *testing.T) { func TestTransport_nilSource(t *testing.T) { tr := &Transport{ source: nil, - auther: &auther{ + auther: &Auther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, @@ -84,7 +84,7 @@ func TestTransport_nilSource(t *testing.T) { func TestTransport_emptySource(t *testing.T) { tr := &Transport{ source: StaticTokenSource(nil), - auther: &auther{ + auther: &Auther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, diff --git a/verifier.go b/verifier.go index 136a127..46d1a7b 100644 --- a/verifier.go +++ b/verifier.go @@ -156,7 +156,7 @@ type HMACVerifier struct { // Default signer is HMAC-SHA1. func NewHMACVerifier(c *Config, tokenSecret string) *HMACVerifier { return &HMACVerifier{ - newAuther(c).signer(), + NewAuther(c).signer(), tokenSecret, } } @@ -180,9 +180,10 @@ func (v *HMACVerifier) Verify(baseString, actualSignature string) error { } // collectRequestParameters collects request parameters from -// 1. the request query, -// 2. the request body provided the body is single part & form encoded & form content type header is set, -// 3. and authorization header. +// 1. the request query, +// 2. the request body provided the body is single part & form encoded & form content type header is set, +// 3. and authorization header. +// // The returned map of collected parameter keys and values follow RFC 5849 3.4.1.3, // except duplicate parameters are not supported. func collectRequestParameters(req *http.Request) (map[string]string, string, error) { From deeeafa43a397c756830a85878da1fdf24491b09 Mon Sep 17 00:00:00 2001 From: howardhuang Date: Tue, 29 Aug 2023 16:37:43 +0800 Subject: [PATCH 02/10] feat: transport epxort Auther field --- config.go | 2 +- transport.go | 8 ++++---- transport_test.go | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 8824c88..1a6e443 100644 --- a/config.go +++ b/config.go @@ -53,7 +53,7 @@ func NewClient(ctx context.Context, config *Config, token *Token) *http.Client { transport := &Transport{ Base: contextTransport(ctx), source: StaticTokenSource(token), - auther: NewAuther(config), + Auther: NewAuther(config), } return &http.Client{Transport: transport} } diff --git a/transport.go b/transport.go index 6cb257b..1ff5e0c 100644 --- a/transport.go +++ b/transport.go @@ -17,8 +17,8 @@ type Transport struct { Base http.RoundTripper // source supplies the token to use when signing a request source TokenSource - // auther adds OAuth1 Authorization headers to requests - auther *Auther + // Auther adds OAuth1 Authorization headers to requests + Auther *Auther } // RoundTrip authorizes the request with a signed OAuth1 Authorization header @@ -31,12 +31,12 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { if err != nil { return nil, err } - if t.auther == nil { + if t.Auther == nil { return nil, fmt.Errorf("oauth1: Transport's auther is nil") } // RoundTripper should not modify the given request, clone it req2 := cloneRequest(req) - err = t.auther.SetRequestAuthHeader(req2, accessToken) + err = t.Auther.SetRequestAuthHeader(req2, accessToken) if err != nil { return nil, err } diff --git a/transport_test.go b/transport_test.go index 94c1e10..108fe51 100644 --- a/transport_test.go +++ b/transport_test.go @@ -40,7 +40,7 @@ func TestTransport(t *testing.T) { } tr := &Transport{ source: StaticTokenSource(NewToken(expectedToken, "some_secret")), - auther: auther, + Auther: auther, } client := &http.Client{Transport: tr} @@ -68,7 +68,7 @@ func TestTransport_customBaseTransport(t *testing.T) { func TestTransport_nilSource(t *testing.T) { tr := &Transport{ source: nil, - auther: &Auther{ + Auther: &Auther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, @@ -84,7 +84,7 @@ func TestTransport_nilSource(t *testing.T) { func TestTransport_emptySource(t *testing.T) { tr := &Transport{ source: StaticTokenSource(nil), - auther: &Auther{ + Auther: &Auther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, @@ -100,7 +100,7 @@ func TestTransport_emptySource(t *testing.T) { func TestTransport_nilAuther(t *testing.T) { tr := &Transport{ source: StaticTokenSource(&Token{}), - auther: nil, + Auther: nil, } client := &http.Client{Transport: tr} resp, err := client.Get("http://example.com") From 1bd7d6770e07610cfc990862f6d9ff3b664a7eee Mon Sep 17 00:00:00 2001 From: howardhuang Date: Tue, 29 Aug 2023 17:04:19 +0800 Subject: [PATCH 03/10] feat: transport.Auther use interface --- auther.go | 26 +++++++++++++------------- auther_test.go | 22 +++++++++++----------- config.go | 6 +++--- reference_test.go | 10 +++++----- transport.go | 6 +++++- transport_test.go | 8 +++++--- verifier.go | 2 +- 7 files changed, 43 insertions(+), 37 deletions(-) diff --git a/auther.go b/auther.go index 490e2bf..ae30564 100644 --- a/auther.go +++ b/auther.go @@ -36,28 +36,28 @@ type clock interface { Now() time.Time } -// Auther adds an "OAuth" Authorization header field to requests. -type Auther struct { +// DefaultAuther adds an "OAuth" Authorization header field to requests. +type DefaultAuther struct { config *Config clock clock } -// NewAuther returns a new Auther -func NewAuther(config *Config) *Auther { +// NewDefaultAuther returns a new DefaultAuther +func NewDefaultAuther(config *Config) *DefaultAuther { if config == nil { config = &Config{} } if config.Noncer == nil { config.Noncer = Base64Noncer{} } - return &Auther{ + return &DefaultAuther{ config: config, } } // setRequestTokenAuthHeader adds the OAuth1 header for the request token // request (temporary credential) according to RFC 5849 2.1. -func (a *Auther) setRequestTokenAuthHeader(req *http.Request) error { +func (a *DefaultAuther) setRequestTokenAuthHeader(req *http.Request) error { oauthParams := a.commonOAuthParams() oauthParams[oauthCallbackParam] = a.config.CallbackURL params, err := collectParameters(req, oauthParams) @@ -79,7 +79,7 @@ func (a *Auther) setRequestTokenAuthHeader(req *http.Request) error { // setAccessTokenAuthHeader sets the OAuth1 header for the access token request // (token credential) according to RFC 5849 2.3. -func (a *Auther) setAccessTokenAuthHeader(req *http.Request, requestToken, requestSecret, verifier string) error { +func (a *DefaultAuther) setAccessTokenAuthHeader(req *http.Request, requestToken, requestSecret, verifier string) error { oauthParams := a.commonOAuthParams() oauthParams[oauthTokenParam] = requestToken oauthParams[oauthVerifierParam] = verifier @@ -99,7 +99,7 @@ func (a *Auther) setAccessTokenAuthHeader(req *http.Request, requestToken, reque // SetRequestAuthHeader sets the OAuth1 header for making authenticated // requests with an AccessToken (token credential) according to RFC 5849 3.1. -func (a *Auther) SetRequestAuthHeader(req *http.Request, accessToken *Token) error { +func (a *DefaultAuther) SetRequestAuthHeader(req *http.Request, accessToken *Token) error { oauthParams := a.commonOAuthParams() oauthParams[oauthTokenParam] = accessToken.Token params, err := collectParameters(req, oauthParams) @@ -120,7 +120,7 @@ func (a *Auther) SetRequestAuthHeader(req *http.Request, accessToken *Token) err // excluding the oauth_signature parameter. This includes the realm parameter // if it was set in the config. The realm parameter will not be included in // the signature base string as specified in RFC 5849 3.4.1.3.1. -func (a *Auther) commonOAuthParams() map[string]string { +func (a *DefaultAuther) commonOAuthParams() map[string]string { params := map[string]string{ oauthConsumerKeyParam: a.config.ConsumerKey, oauthSignatureMethodParam: a.signer().Name(), @@ -135,12 +135,12 @@ func (a *Auther) commonOAuthParams() map[string]string { } // Returns a nonce using the configured Noncer. -func (a *Auther) nonce() string { +func (a *DefaultAuther) nonce() string { return a.config.Noncer.Nonce() } // Returns the Unix epoch seconds. -func (a *Auther) epoch() int64 { +func (a *DefaultAuther) epoch() int64 { if a.clock != nil { return a.clock.Now().Unix() } @@ -148,7 +148,7 @@ func (a *Auther) epoch() int64 { } // Returns the Config's Signer or the default Signer. -func (a *Auther) signer() Signer { +func (a *DefaultAuther) signer() Signer { if a.config.Signer != nil { return a.config.Signer } @@ -194,7 +194,7 @@ func sortParameters(params map[string]string, format string) []string { return pairs } -// collectParameters collects request parameters from the request query, OAuth +// CollectParameters collects request parameters from the request query, OAuth // parameters (which should exclude oauth_signature), and the request body // provided the body is single part, form encoded, and the form content type // header is set. The returned map of collected parameter keys and values diff --git a/auther_test.go b/auther_test.go index c81910b..236fd46 100644 --- a/auther_test.go +++ b/auther_test.go @@ -12,11 +12,11 @@ import ( func TestCommonOAuthParams(t *testing.T) { cases := []struct { - Auther *Auther + DefaultAuther *DefaultAuther expectedParams map[string]string }{ { - &Auther{ + &DefaultAuther{ &Config{ ConsumerKey: "some_consumer_key", Noncer: &fixedNoncer{"some_nonce"}, @@ -32,7 +32,7 @@ func TestCommonOAuthParams(t *testing.T) { }, }, { - &Auther{ + &DefaultAuther{ &Config{ ConsumerKey: "some_consumer_key", Realm: "photos", @@ -52,13 +52,13 @@ func TestCommonOAuthParams(t *testing.T) { } for _, c := range cases { - assert.Equal(t, c.expectedParams, c.Auther.commonOAuthParams()) + assert.Equal(t, c.expectedParams, c.DefaultAuther.commonOAuthParams()) } } func TestNonce(t *testing.T) { - Auther := NewAuther(nil) - nonce := Auther.nonce() + DefaultAuther := NewDefaultAuther(nil) + nonce := DefaultAuther.nonce() // assert that 32 bytes (256 bites) become 44 bytes since a base64 byte // zeros the 2 high bits. 3 bytes convert to 4 base64 bytes, 40 base64 bytes // represent the first 30 of 32 bytes, = padding adds another 4 byte group. @@ -67,17 +67,17 @@ func TestNonce(t *testing.T) { } func TestEpoch(t *testing.T) { - a := NewAuther(nil) + a := NewDefaultAuther(nil) // assert that a real time is used by default assert.InEpsilon(t, time.Now().Unix(), a.epoch(), 1) // assert that the fixed clock can be used for testing - a = &Auther{clock: &fixedClock{time.Unix(50037133, 0)}} + a = &DefaultAuther{clock: &fixedClock{time.Unix(50037133, 0)}} assert.Equal(t, int64(50037133), a.epoch()) } func TestSigner_Default(t *testing.T) { config := &Config{ConsumerSecret: "consumer_secret"} - a := NewAuther(config) + a := NewDefaultAuther(config) // echo -n "hello world" | openssl dgst -sha1 -hmac "consumer_secret&token_secret" -binary | base64 expectedSignature := "BE0uILOruKfSXd4UzYlLJDfOq08=" // assert that the default signer produces the expected HMAC-SHA1 digest @@ -92,7 +92,7 @@ func TestSigner_SHA256(t *testing.T) { config := &Config{ Signer: &HMAC256Signer{ConsumerSecret: "consumer_secret"}, } - a := NewAuther(config) + a := NewDefaultAuther(config) // echo -n "hello world" | openssl dgst -sha256 -hmac "consumer_secret&token_secret" -binary | base64 expectedSignature := "pW9drXUyErU8DASWbsP2I3XZbju37AW+VzcGdYSeMo8=" // assert that the signer produces the expected HMAC-SHA256 digest @@ -118,7 +118,7 @@ func TestSigner_Custom(t *testing.T) { ConsumerSecret: "consumer_secret", Signer: &identitySigner{}, } - a := NewAuther(config) + a := NewDefaultAuther(config) // assert that the custom signer is used method := a.signer().Name() digest, err := a.signer().Sign("secret", "hello world") diff --git a/config.go b/config.go index 1a6e443..1cdf66b 100644 --- a/config.go +++ b/config.go @@ -53,7 +53,7 @@ func NewClient(ctx context.Context, config *Config, token *Token) *http.Client { transport := &Transport{ Base: contextTransport(ctx), source: StaticTokenSource(token), - Auther: NewAuther(config), + Auther: NewDefaultAuther(config), } return &http.Client{Transport: transport} } @@ -69,7 +69,7 @@ func (c *Config) RequestToken() (requestToken, requestSecret string, err error) if err != nil { return "", "", err } - err = NewAuther(c).setRequestTokenAuthHeader(req) + err = NewDefaultAuther(c).setRequestTokenAuthHeader(req) if err != nil { return "", "", err } @@ -148,7 +148,7 @@ func (c *Config) AccessToken(requestToken, requestSecret, verifier string) (acce if err != nil { return "", "", err } - err = NewAuther(c).setAccessTokenAuthHeader(req, requestToken, requestSecret, verifier) + err = NewDefaultAuther(c).setAccessTokenAuthHeader(req, requestToken, requestSecret, verifier) if err != nil { return "", "", err } diff --git a/reference_test.go b/reference_test.go index 25506af..881623d 100644 --- a/reference_test.go +++ b/reference_test.go @@ -36,7 +36,7 @@ func TestTwitterRequestTokenAuthHeader(t *testing.T) { Noncer: &fixedNoncer{expectedNonce}, } - auther := &Auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} + auther := &DefaultAuther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} req, err := http.NewRequest("POST", config.Endpoint.RequestTokenURL, nil) assert.Nil(t, err) err = auther.setRequestTokenAuthHeader(req) @@ -74,7 +74,7 @@ func TestTwitterAccessTokenAuthHeader(t *testing.T) { Noncer: &fixedNoncer{expectedNonce}, } - auther := &Auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} + auther := &DefaultAuther{config, &fixedClock{time.Unix(unixTimestamp, 0)}} req, err := http.NewRequest("POST", config.Endpoint.AccessTokenURL, nil) assert.Nil(t, err) err = auther.setAccessTokenAuthHeader(req, expectedRequestToken, requestTokenSecret, expectedVerifier) @@ -111,7 +111,7 @@ var twitterConfig = &Config{ } func TestTwitterParameterString(t *testing.T) { - auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &DefaultAuther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") // note: the reference example is old and uses api v1 in the URL @@ -128,7 +128,7 @@ func TestTwitterParameterString(t *testing.T) { } func TestTwitterSignatureBase(t *testing.T) { - auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &DefaultAuther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") // note: the reference example is old and uses api v1 in the URL @@ -151,7 +151,7 @@ func TestTwitterRequestAuthHeader(t *testing.T) { expectedSignature := PercentEncode("tnnArxj06cWHq44gCs1OSKk/jLY=") expectedTimestamp := "1318622958" - auther := &Auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} + auther := &DefaultAuther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}} values := url.Values{} values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!") diff --git a/transport.go b/transport.go index 1ff5e0c..72206c0 100644 --- a/transport.go +++ b/transport.go @@ -5,6 +5,10 @@ import ( "net/http" ) +type Auther interface { + SetRequestAuthHeader(req *http.Request, accessToken *Token) error +} + // Transport is an http.RoundTripper which makes OAuth1 HTTP requests. It // wraps a base RoundTripper and adds an Authorization header using the // token from a TokenSource. @@ -18,7 +22,7 @@ type Transport struct { // source supplies the token to use when signing a request source TokenSource // Auther adds OAuth1 Authorization headers to requests - Auther *Auther + Auther Auther } // RoundTrip authorizes the request with a signed OAuth1 Authorization header diff --git a/transport_test.go b/transport_test.go index 108fe51..edfc57d 100644 --- a/transport_test.go +++ b/transport_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/assert" ) +var _ Auther = &DefaultAuther{} + func TestTransport(t *testing.T) { const ( expectedToken = "access_token" @@ -34,7 +36,7 @@ func TestTransport(t *testing.T) { ConsumerSecret: "consumer_secret", Noncer: &fixedNoncer{expectedNonce}, } - auther := &Auther{ + auther := &DefaultAuther{ config: config, clock: &fixedClock{time.Unix(123456789, 0)}, } @@ -68,7 +70,7 @@ func TestTransport_customBaseTransport(t *testing.T) { func TestTransport_nilSource(t *testing.T) { tr := &Transport{ source: nil, - Auther: &Auther{ + Auther: &DefaultAuther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, @@ -84,7 +86,7 @@ func TestTransport_nilSource(t *testing.T) { func TestTransport_emptySource(t *testing.T) { tr := &Transport{ source: StaticTokenSource(nil), - Auther: &Auther{ + Auther: &DefaultAuther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, }, diff --git a/verifier.go b/verifier.go index 46d1a7b..d540a02 100644 --- a/verifier.go +++ b/verifier.go @@ -156,7 +156,7 @@ type HMACVerifier struct { // Default signer is HMAC-SHA1. func NewHMACVerifier(c *Config, tokenSecret string) *HMACVerifier { return &HMACVerifier{ - NewAuther(c).signer(), + NewDefaultAuther(c).signer(), tokenSecret, } } From 1c3b984f51b8fb36914efab4102e488344a6c74e Mon Sep 17 00:00:00 2001 From: howardhuang Date: Tue, 29 Aug 2023 17:48:50 +0800 Subject: [PATCH 04/10] feat: export Transport source field --- config.go | 2 +- transport.go | 8 ++++---- transport_test.go | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config.go b/config.go index 1cdf66b..69f6420 100644 --- a/config.go +++ b/config.go @@ -52,7 +52,7 @@ func (c *Config) Client(ctx context.Context, t *Token) *http.Client { func NewClient(ctx context.Context, config *Config, token *Token) *http.Client { transport := &Transport{ Base: contextTransport(ctx), - source: StaticTokenSource(token), + Source: StaticTokenSource(token), Auther: NewDefaultAuther(config), } return &http.Client{Transport: transport} diff --git a/transport.go b/transport.go index 72206c0..f15b660 100644 --- a/transport.go +++ b/transport.go @@ -19,8 +19,8 @@ type Transport struct { // Base is the base RoundTripper used to make HTTP requests. If nil, then // http.DefaultTransport is used Base http.RoundTripper - // source supplies the token to use when signing a request - source TokenSource + // Source supplies the token to use when signing a request + Source TokenSource // Auther adds OAuth1 Authorization headers to requests Auther Auther } @@ -28,10 +28,10 @@ type Transport struct { // RoundTrip authorizes the request with a signed OAuth1 Authorization header // using the auther and TokenSource. func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.source == nil { + if t.Source == nil { return nil, fmt.Errorf("oauth1: Transport's source is nil") } - accessToken, err := t.source.Token() + accessToken, err := t.Source.Token() if err != nil { return nil, err } diff --git a/transport_test.go b/transport_test.go index edfc57d..60710ec 100644 --- a/transport_test.go +++ b/transport_test.go @@ -41,7 +41,7 @@ func TestTransport(t *testing.T) { clock: &fixedClock{time.Unix(123456789, 0)}, } tr := &Transport{ - source: StaticTokenSource(NewToken(expectedToken, "some_secret")), + Source: StaticTokenSource(NewToken(expectedToken, "some_secret")), Auther: auther, } client := &http.Client{Transport: tr} @@ -69,7 +69,7 @@ func TestTransport_customBaseTransport(t *testing.T) { func TestTransport_nilSource(t *testing.T) { tr := &Transport{ - source: nil, + Source: nil, Auther: &DefaultAuther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, @@ -85,7 +85,7 @@ func TestTransport_nilSource(t *testing.T) { func TestTransport_emptySource(t *testing.T) { tr := &Transport{ - source: StaticTokenSource(nil), + Source: StaticTokenSource(nil), Auther: &DefaultAuther{ config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, clock: &fixedClock{time.Unix(123456789, 0)}, @@ -101,7 +101,7 @@ func TestTransport_emptySource(t *testing.T) { func TestTransport_nilAuther(t *testing.T) { tr := &Transport{ - source: StaticTokenSource(&Token{}), + Source: StaticTokenSource(&Token{}), Auther: nil, } client := &http.Client{Transport: tr} From 4746ab9212638bb7d5ce985df5896bef3054a103 Mon Sep 17 00:00:00 2001 From: howardhuang Date: Wed, 30 Aug 2023 13:26:04 +0800 Subject: [PATCH 05/10] fix: provide NewTransport instead of exported field --- config.go | 6 +-- transport.go | 51 ++++++++++++++++----- transport_test.go | 111 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 130 insertions(+), 38 deletions(-) diff --git a/config.go b/config.go index 69f6420..3162e0c 100644 --- a/config.go +++ b/config.go @@ -50,11 +50,7 @@ func (c *Config) Client(ctx context.Context, t *Token) *http.Client { // NewClient returns a new http Client which signs requests via OAuth1. func NewClient(ctx context.Context, config *Config, token *Token) *http.Client { - transport := &Transport{ - Base: contextTransport(ctx), - Source: StaticTokenSource(token), - Auther: NewDefaultAuther(config), - } + transport := newTransport(contextTransport(ctx), StaticTokenSource(token), NewDefaultAuther(config)) return &http.Client{Transport: transport} } diff --git a/transport.go b/transport.go index f15b660..134c6ef 100644 --- a/transport.go +++ b/transport.go @@ -19,28 +19,45 @@ type Transport struct { // Base is the base RoundTripper used to make HTTP requests. If nil, then // http.DefaultTransport is used Base http.RoundTripper - // Source supplies the token to use when signing a request - Source TokenSource + // source supplies the token to use when signing a request + source TokenSource // Auther adds OAuth1 Authorization headers to requests - Auther Auther + auther Auther +} + +// NewTransport returns a new Transport and an error +func NewTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther Auther) (*Transport, error) { + t := newTransport(baseRoundTripper, source, auther) + err := t.checkValid() + if err != nil { + return nil, err + } + return t, nil +} + +// NewTransport returns a new Transport without checking whether there is an error +func newTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther Auther) *Transport { + return &Transport{ + Base: baseRoundTripper, + source: source, + auther: auther, + } } // RoundTrip authorizes the request with a signed OAuth1 Authorization header // using the auther and TokenSource. func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.Source == nil { - return nil, fmt.Errorf("oauth1: Transport's source is nil") - } - accessToken, err := t.Source.Token() + err := t.checkValid() if err != nil { return nil, err } - if t.Auther == nil { - return nil, fmt.Errorf("oauth1: Transport's auther is nil") + accessToken, err := t.source.Token() + if err != nil { + return nil, err } // RoundTripper should not modify the given request, clone it req2 := cloneRequest(req) - err = t.Auther.SetRequestAuthHeader(req2, accessToken) + err = t.auther.SetRequestAuthHeader(req2, accessToken) if err != nil { return nil, err } @@ -67,3 +84,17 @@ func cloneRequest(req *http.Request) *http.Request { } return r2 } + +func (t *Transport) checkValid() error { + if t.source == nil { + return fmt.Errorf("oauth1: Transport's source is nil") + } + if t.auther == nil { + return fmt.Errorf("oauth1: Transport's auther is nil") + } + _, err := t.source.Token() + if err != nil { + return err + } + return nil +} diff --git a/transport_test.go b/transport_test.go index 60710ec..ad34b31 100644 --- a/transport_test.go +++ b/transport_test.go @@ -40,10 +40,7 @@ func TestTransport(t *testing.T) { config: config, clock: &fixedClock{time.Unix(123456789, 0)}, } - tr := &Transport{ - Source: StaticTokenSource(NewToken(expectedToken, "some_secret")), - Auther: auther, - } + tr := newTransport(nil, StaticTokenSource(NewToken(expectedToken, "some_secret")), auther) client := &http.Client{Transport: tr} req, err := http.NewRequest("GET", server.URL, nil) @@ -52,13 +49,90 @@ func TestTransport(t *testing.T) { assert.Nil(t, err) } +func TestNewTransport(t *testing.T) { + const ( + expectedToken = "access_token" + expectedConsumerKey = "consumer_key" + expectedNonce = "some_nonce" + expectedSignatureMethod = "HMAC-SHA1" + expectedTimestamp = "123456789" + ) + config := &Config{ + ConsumerKey: expectedConsumerKey, + ConsumerSecret: "consumer_secret", + Noncer: &fixedNoncer{expectedNonce}, + } + auther := &DefaultAuther{ + config: config, + clock: &fixedClock{time.Unix(123456789, 0)}, + } + source := StaticTokenSource(NewToken(expectedToken, "some_secret")) + + type testCase struct { + description string + base http.RoundTripper + source TokenSource + auther Auther + errMsg string + } + for _, tc := range []testCase{ + { + description: "default base transport", + base: nil, + source: source, + auther: auther, + errMsg: "", + }, + { + description: "custom base transport", + base: nil, + source: source, + auther: auther, + errMsg: "", + }, + { + description: "nil source", + base: nil, + source: nil, + auther: auther, + errMsg: "oauth1: Transport's source is nil", + }, + { + description: "empty source", + base: nil, + source: StaticTokenSource(nil), + auther: auther, + errMsg: "oauth1: Token is nil", + }, + { + description: "nil auther", + base: nil, + source: source, + auther: nil, + errMsg: "oauth1: Transport's auther is nil", + }, + } { + tr, err := NewTransport(tc.base, tc.source, tc.auther) + if tc.errMsg == "" { + assert.Nil(t, err) + if tc.base == nil { + assert.Equal(t, http.DefaultTransport, tr.base()) + } else { + assert.Equal(t, tc.base, tr.base()) + } + } else { + assert.Contains(t, err.Error(), tc.errMsg) + assert.Nil(t, tr) + } + } +} + func TestTransport_defaultBaseTransport(t *testing.T) { tr := &Transport{ Base: nil, } assert.Equal(t, http.DefaultTransport, tr.base()) } - func TestTransport_customBaseTransport(t *testing.T) { expected := &http.Transport{} tr := &Transport{ @@ -68,13 +142,10 @@ func TestTransport_customBaseTransport(t *testing.T) { } func TestTransport_nilSource(t *testing.T) { - tr := &Transport{ - Source: nil, - Auther: &DefaultAuther{ - config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, - clock: &fixedClock{time.Unix(123456789, 0)}, - }, - } + tr := newTransport(nil, nil, &DefaultAuther{ + config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, + clock: &fixedClock{time.Unix(123456789, 0)}, + }) client := &http.Client{Transport: tr} resp, err := client.Get("http://example.com") assert.Nil(t, resp) @@ -84,13 +155,10 @@ func TestTransport_nilSource(t *testing.T) { } func TestTransport_emptySource(t *testing.T) { - tr := &Transport{ - Source: StaticTokenSource(nil), - Auther: &DefaultAuther{ - config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, - clock: &fixedClock{time.Unix(123456789, 0)}, - }, - } + tr := newTransport(nil, StaticTokenSource(nil), &DefaultAuther{ + config: &Config{Noncer: &fixedNoncer{"any_nonce"}}, + clock: &fixedClock{time.Unix(123456789, 0)}, + }) client := &http.Client{Transport: tr} resp, err := client.Get("http://example.com") assert.Nil(t, resp) @@ -100,10 +168,7 @@ func TestTransport_emptySource(t *testing.T) { } func TestTransport_nilAuther(t *testing.T) { - tr := &Transport{ - Source: StaticTokenSource(&Token{}), - Auther: nil, - } + tr := newTransport(nil, StaticTokenSource(&Token{}), nil) client := &http.Client{Transport: tr} resp, err := client.Get("http://example.com") assert.Nil(t, resp) From e4f1aef23d5775c16a883d839623119baa0611a2 Mon Sep 17 00:00:00 2001 From: howardhuang Date: Wed, 30 Aug 2023 13:29:02 +0800 Subject: [PATCH 06/10] fix: comment typo --- auther.go | 2 +- transport.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auther.go b/auther.go index ae30564..be517af 100644 --- a/auther.go +++ b/auther.go @@ -194,7 +194,7 @@ func sortParameters(params map[string]string, format string) []string { return pairs } -// CollectParameters collects request parameters from the request query, OAuth +// collectParameters collects request parameters from the request query, OAuth // parameters (which should exclude oauth_signature), and the request body // provided the body is single part, form encoded, and the form content type // header is set. The returned map of collected parameter keys and values diff --git a/transport.go b/transport.go index 134c6ef..d742ab2 100644 --- a/transport.go +++ b/transport.go @@ -35,7 +35,7 @@ func NewTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther return t, nil } -// NewTransport returns a new Transport without checking whether there is an error +// newTransport returns a new Transport without checking whether there is an error func newTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther Auther) *Transport { return &Transport{ Base: baseRoundTripper, From bdbb285a7172c40e45137f4c29c84a3ff6bf367b Mon Sep 17 00:00:00 2001 From: howardhuang Date: Wed, 30 Aug 2023 13:39:51 +0800 Subject: [PATCH 07/10] refactor: remove unused const --- transport_test.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/transport_test.go b/transport_test.go index ad34b31..159db09 100644 --- a/transport_test.go +++ b/transport_test.go @@ -50,23 +50,16 @@ func TestTransport(t *testing.T) { } func TestNewTransport(t *testing.T) { - const ( - expectedToken = "access_token" - expectedConsumerKey = "consumer_key" - expectedNonce = "some_nonce" - expectedSignatureMethod = "HMAC-SHA1" - expectedTimestamp = "123456789" - ) config := &Config{ - ConsumerKey: expectedConsumerKey, + ConsumerKey: "consumer_key", ConsumerSecret: "consumer_secret", - Noncer: &fixedNoncer{expectedNonce}, + Noncer: &fixedNoncer{"some_nonce"}, } auther := &DefaultAuther{ config: config, clock: &fixedClock{time.Unix(123456789, 0)}, } - source := StaticTokenSource(NewToken(expectedToken, "some_secret")) + source := StaticTokenSource(NewToken("access_token", "some_secret")) type testCase struct { description string From 2f274d4d9d6babe249f4e77a76ac5a38c1cae991 Mon Sep 17 00:00:00 2001 From: hhuang-rayark <143489676+hhuang-rayark@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:01:13 +0800 Subject: [PATCH 08/10] fix: doc typo Co-authored-by: raymond-chia <39833336+raymond-chia@users.noreply.github.com> --- transport.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport.go b/transport.go index d742ab2..cdb5a33 100644 --- a/transport.go +++ b/transport.go @@ -21,7 +21,7 @@ type Transport struct { Base http.RoundTripper // source supplies the token to use when signing a request source TokenSource - // Auther adds OAuth1 Authorization headers to requests + // auther adds OAuth1 Authorization headers to requests auther Auther } From 30c7438e6a01be134ceb4dad553cdbb0d2e81acd Mon Sep 17 00:00:00 2001 From: howardhuang Date: Wed, 30 Aug 2023 16:33:45 +0800 Subject: [PATCH 09/10] fix: test case --- transport_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport_test.go b/transport_test.go index 159db09..a3c8889 100644 --- a/transport_test.go +++ b/transport_test.go @@ -78,7 +78,7 @@ func TestNewTransport(t *testing.T) { }, { description: "custom base transport", - base: nil, + base: &http.Transport{}, source: source, auther: auther, errMsg: "", From 0f1df77259de5323282abb51f6162a3bd3fb7905 Mon Sep 17 00:00:00 2001 From: howardhuang Date: Wed, 30 Aug 2023 16:34:29 +0800 Subject: [PATCH 10/10] doc: add comment --- transport.go | 1 + 1 file changed, 1 insertion(+) diff --git a/transport.go b/transport.go index cdb5a33..118f1f5 100644 --- a/transport.go +++ b/transport.go @@ -36,6 +36,7 @@ func NewTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther } // newTransport returns a new Transport without checking whether there is an error +// newTransport is only for NewTransport & NewClient func newTransport(baseRoundTripper http.RoundTripper, source TokenSource, auther Auther) *Transport { return &Transport{ Base: baseRoundTripper,