pax_global_header00006660000000000000000000000064137724725050014526gustar00rootroot0000000000000052 comment=864d9cc92d61cd6e6954432d51a8788d213a2b48 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/000077500000000000000000000000001377247250500217345ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/.gitignore000066400000000000000000000000431377247250500237210ustar00rootroot00000000000000_testmain.go *.6 *.a 6.out example golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/.travis.yml000066400000000000000000000001331377247250500240420ustar00rootroot00000000000000language: go sudo: false go: - 1.7.x - 1.8.x - 1.9.x - 1.10.x - 1.11.x - tip golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/README.markdown000066400000000000000000000026721377247250500244440ustar00rootroot00000000000000# OAuth1 [![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. ## Installation go get 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). ## Documentation - [Reference](http://godoc.org/github.com/gomodule/oauth1/oauth) - Examples - [Discogs](https://github.com/gomodule/oauth1/tree/master/examples/discogs) - [Dropbox](https://github.com/gomodule/oauth1/tree/master/examples/dropbox) - [Quickbooks](https://github.com/gomodule/oauth1/tree/master/examples/quickbooks) - [SmugMug](https://github.com/gomodule/oauth1/tree/master/examples/smugmug) - [Twitter on App Engine](https://github.com/gomodule/oauth1/tree/master/examples/appengine) - [Twitter](https://github.com/gomodule/oauth1/tree/master/examples/twitter) - [Twitter OOB](https://github.com/gomodule/oauth1/tree/master/examples/twitteroob) (a command line application using OOB authorization) - [Yelp](https://github.com/gomodule/oauth1/tree/master/examples/yelp) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/000077500000000000000000000000001377247250500235525ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/000077500000000000000000000000001377247250500255205ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/README.markdown000066400000000000000000000007261377247250500302260ustar00rootroot00000000000000This example shows how to use the oauth package on App Engine. The examples require a configuration file containing a consumer key and secret from Twitter: 1. Register an application at https://dev.twitter.com/apps/new 2. $ cp config.json.example config.json. 3. Edit config.json to include your Twitter consumer key and consumer secret from step 1. To run the web example: 1. devapp\_server.py . 2. Go to http://127.0.0.1:8080/ in a browser to try the application. golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/app.go000066400000000000000000000140511377247250500266300ustar00rootroot00000000000000// Copyright 2014 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package app import ( "encoding/json" "fmt" "io/ioutil" "net/http" "text/template" "golang.org/x/net/context" "github.com/gomodule/oauth1/oauth" "google.golang.org/appengine" "google.golang.org/appengine/datastore" "google.golang.org/appengine/log" "google.golang.org/appengine/memcache" "google.golang.org/appengine/urlfetch" "google.golang.org/appengine/user" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token", ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize", TokenRequestURI: "https://api.twitter.com/oauth/access_token", } // context stores context associated with an HTTP request. type Context struct { c context.Context r *http.Request w http.ResponseWriter u *user.User } // handler adapts a function to an http.Handler type handler func(c *Context) error func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) c := Context{ c: ctx, r: r, w: w, u: user.Current(ctx), } if c.u == nil { url, _ := user.LoginURL(c.c, c.r.URL.Path) http.Redirect(w, r, url, 301) return } err := h(&c) if err != nil { http.Error(w, "server error", 500) log.Errorf(c.c, "error %v", err) } } // userInfo is stored in the App Engine datastore with key email. type userInfo struct { TwitterCred oauth.Credentials } // getUserInfo returns information about the currently logged in user. func (c *Context) getUserInfo() (*userInfo, error) { key := datastore.NewKey(c.c, "user", c.u.Email, 0, nil) var u userInfo err := datastore.Get(c.c, key, &u) if err == datastore.ErrNoSuchEntity { err = nil } return &u, err } // updateUserInfo updates information about the currently logged in user. func (c *Context) updateUserInfo(f func(u *userInfo)) error { key := datastore.NewKey(c.c, "user", c.u.Email, 0, nil) return datastore.RunInTransaction(c.c, func(ctx context.Context) error { var u userInfo err := datastore.Get(ctx, key, &u) if err != nil && err != datastore.ErrNoSuchEntity { return err } f(&u) _, err = datastore.Put(ctx, key, &u) return err }, nil) } type connectInfo struct { Secret string Redirect string } // serveTwitterConnect gets the OAuth temp credentials and redirects the user to the // Twitter's authorization page. func serveTwitterConnect(c *Context) error { httpClient := urlfetch.Client(c.c) callback := "http://" + c.r.Host + "/twitter/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(httpClient, callback, nil) if err != nil { return err } ci := connectInfo{Secret: tempCred.Secret, Redirect: c.r.FormValue("redirect")} err = memcache.Gob.Set(c.c, &memcache.Item{Key: tempCred.Token, Object: &ci}) if err != nil { return err } http.Redirect(c.w, c.r, oauthClient.AuthorizationURL(tempCred, nil), 302) return nil } // serveTwitterCallback handles callbacks from the Twitter OAuth server. func serveTwitterCallback(c *Context) error { token := c.r.FormValue("oauth_token") var ci connectInfo _, err := memcache.Gob.Get(c.c, token, &ci) if err != nil { return err } memcache.Delete(c.c, token) tempCred := &oauth.Credentials{ Token: token, Secret: ci.Secret, } httpClient := urlfetch.Client(c.c) tokenCred, _, err := oauthClient.RequestToken(httpClient, tempCred, c.r.FormValue("oauth_verifier")) if err != nil { return err } if err := c.updateUserInfo(func(u *userInfo) { u.TwitterCred = *tokenCred }); err != nil { return err } http.Redirect(c.w, c.r, ci.Redirect, 302) return nil } // serveTwitterDisconnect clears the user's Twitter credentials. func serveTwitterDisconnect(c *Context) error { if err := c.updateUserInfo(func(u *userInfo) { u.TwitterCred = oauth.Credentials{} }); err != nil { return err } http.Redirect(c.w, c.r, c.r.FormValue("redirect"), 302) return nil } func serveHome(c *Context) error { if c.r.URL.Path != "/" { http.NotFound(c.w, c.r) return nil } u, err := c.getUserInfo() if err != nil { return err } var data = struct { Connected bool Timeline []map[string]interface{} }{ Connected: u.TwitterCred.Token != "" && u.TwitterCred.Secret != "", } if data.Connected { httpClient := urlfetch.Client(c.c) resp, err := oauthClient.Get(httpClient, &u.TwitterCred, "https://api.twitter.com/1.1/statuses/home_timeline.json", nil) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { p, _ := ioutil.ReadAll(resp.Body) return fmt.Errorf("get %s returned status %d, %s", resp.Request.URL, resp.StatusCode, p) } if err := json.NewDecoder(resp.Body).Decode(&data.Timeline); err != nil { return err } } c.w.Header().Set("Content-Type", "text/html; charset=utf-8") return homeTmpl.Execute(c.w, &data) } func init() { b, err := ioutil.ReadFile("config.json") if err != nil { panic(err) } if err := json.Unmarshal(b, &oauthClient.Credentials); err != nil { panic(err) } http.Handle("/", handler(serveHome)) http.Handle("/twitter/connect", handler(serveTwitterConnect)) http.Handle("/twitter/disconnect", handler(serveTwitterDisconnect)) http.Handle("/twitter/callback", handler(serveTwitterCallback)) } var homeTmpl = template.Must(template.New("home").Parse( ` {{if .Connected}} Disconnect Twitter account {{range .Timeline}}

{{html .user.name}} {{html .text}} {{end}} {{else}} Connect Twitter account {{end}} `)) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/app.yaml000066400000000000000000000002611377247250500271630ustar00rootroot00000000000000application: helloworld version: 1 runtime: go api_version: go1 handlers: - url: /favicon\.ico static_files: favicon.ico upload: favicon\.ico - url: /.* script: _go_app golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/config.json.example000066400000000000000000000001311377247250500313050ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/000077500000000000000000000000001377247250500252055ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/README.markdown000066400000000000000000000007451377247250500277140ustar00rootroot00000000000000This example shows how to use the oauth package with [Discogs](http://www.discogs.com/developers/). The examples require a configuration file containing a consumer key and secret: 1. [Create an application](https://www.discogs.com/settings/developers). 2. $ cp config.json.example config.json. 3. Edit config.json to include your consumer key and secret from step 1. To run the web example: 1. $ go run main.go 2. Go to http://127.0.0.1:8080/ in a browser to try the application. golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/config.json.example000066400000000000000000000001331377247250500307740ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/main.go000066400000000000000000000126701377247250500264660ustar00rootroot00000000000000// Copyright 2014 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "text/template" "github.com/gomodule/oauth1/examples/session" "github.com/gomodule/oauth1/oauth" ) // Session state keys. const ( tempCredKey = "tempCred" tokenCredKey = "tokenCred" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://api.discogs.com/oauth/request_token", ResourceOwnerAuthorizationURI: "https://www.discogs.com/oauth/authorize", TokenRequestURI: "https://api.discogs.com/oauth/access_token", Header: http.Header{"User-Agent": {"ExampleDiscogsClient/1.0"}}, } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } // serveLogin gets the OAuth temp credentials and redirects the user to the // Discogs' authorization page. func serveLogin(w http.ResponseWriter, r *http.Request) { callback := "http://" + r.Host + "/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(nil, callback, nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, nil), 302) } // serveOAuthCallback handles callbacks from the OAuth server. func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { s := session.Get(r) tempCred, _ := s[tempCredKey].(*oauth.Credentials) if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { http.Error(w, "Unknown oauth_token.", 500) return } tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) if err != nil { http.Error(w, "Error getting request token, "+err.Error(), 500) return } delete(s, tempCredKey) s[tokenCredKey] = tokenCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // serveLogout clears the token credentials func serveLogout(w http.ResponseWriter, r *http.Request) { s := session.Get(r) delete(s, tokenCredKey) if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // authHandler reads the auth cookie and invokes a handler with the result. type authHandler struct { handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials) optional bool } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { cred, _ := session.Get(r)[tokenCredKey].(*oauth.Credentials) if cred == nil && !h.optional { http.Error(w, "Not logged in.", 403) return } h.handler(w, r, cred) } // getJSON gets a resource from the Discogs API server and decodes the result as JSON. func getJSON(cred *oauth.Credentials, endpoint string, form url.Values, v interface{}) error { resp, err := oauthClient.Get(nil, cred, "https://api.discogs.com"+endpoint, form) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("server returned status %d", resp.StatusCode) } return json.NewDecoder(resp.Body).Decode(v) } // respond responds to a request by executing the html template t with data. func respond(w http.ResponseWriter, t *template.Template, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.Execute(w, data); err != nil { log.Print(err) } } func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { if r.URL.Path != "/" { http.NotFound(w, r) return } if cred == nil { data := struct{ Host string }{r.Host} respond(w, homeLoggedOutTmpl, &data) } else { var data struct { Username string } if err := getJSON(cred, "/oauth/identity", nil, &data); err != nil { http.Error(w, err.Error(), 500) return } respond(w, homeTmpl, &data) } } var httpAddr = flag.String("addr", ":8080", "HTTP server address") func main() { flag.Parse() if err := readCredentials(); err != nil { log.Fatalf("Error reading configuration, %v", err) } http.Handle("/", &authHandler{handler: serveHome, optional: true}) http.HandleFunc("/login", serveLogin) http.HandleFunc("/logout", serveLogout) http.HandleFunc("/callback", serveOAuthCallback) if err := http.ListenAndServe(*httpAddr, nil); err != nil { log.Fatalf("Error listening, %v", err) } } var ( homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( ` login `)) homeTmpl = template.Must(template.New("home").Parse( `

Welcome {{.Username}}! `)) ) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/000077500000000000000000000000001377247250500252275ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/README.markdown000066400000000000000000000004701377247250500277310ustar00rootroot00000000000000This example shows how to use the oauth package with Dropbox. To run this example: 1. Register an application at https://www.dropbox.com/developers/apps 2. $ cp config.json.example config.json. 3. Edit config.json to include your Dropbox application key and application secret from step 1. 4. $ go run main.go golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/config.json.example000066400000000000000000000001211377247250500310130ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/main.go000066400000000000000000000132321377247250500265030ustar00rootroot00000000000000// Copyright 2011 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "text/template" "github.com/gomodule/oauth1/examples/session" "github.com/gomodule/oauth1/oauth" ) // Session state keys. var ( tempCredKey = "tempCred" tokenCredKey = "tokenCred" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://api.dropbox.com/1/oauth/request_token", ResourceOwnerAuthorizationURI: "https://www.dropbox.com/1/oauth/authorize", TokenRequestURI: "https://api.dropbox.com/1/oauth/access_token", SignatureMethod: oauth.PLAINTEXT, // Dropbox also works with HMACSHA1 } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } // serveLogin gets the OAuth temp credentials and redirects the user to the // OAuth server's authorization page. func serveLogin(w http.ResponseWriter, r *http.Request) { // Dropbox supports the older OAuth 1.0 specification where the callback URL // is passed to the authorization endpoint. callback := "http://" + r.Host + "/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(nil, "", nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, url.Values{"oauth_callback": {callback}}), 302) } // serveOAuthCallback handles callbacks from the OAuth server. func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { s := session.Get(r) tempCred, _ := s[tempCredKey].(*oauth.Credentials) if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { http.Error(w, "Unknown oauth_token.", 500) return } tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) if err != nil { http.Error(w, "Error getting request token, "+err.Error(), 500) return } delete(s, tempCredKey) s[tokenCredKey] = tokenCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // serveLogout clears the authentication cookie. func serveLogout(w http.ResponseWriter, r *http.Request) { s := session.Get(r) delete(s, tokenCredKey) if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // authHandler reads the auth cookie and invokes a handler with the result. type authHandler struct { handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials) optional bool } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { cred, _ := session.Get(r)[tokenCredKey].(*oauth.Credentials) if cred == nil && !h.optional { http.Error(w, "Not logged in.", 403) return } h.handler(w, r, cred) } // respond responds to a request by executing the html template t with data. func respond(w http.ResponseWriter, t *template.Template, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.Execute(w, data); err != nil { log.Print(err) } } func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { if r.URL.Path != "/" { http.NotFound(w, r) return } if cred == nil { respond(w, homeLoggedOutTmpl, nil) } else { respond(w, homeTmpl, nil) } } func serveInfo(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { resp, err := oauthClient.Get(nil, cred, "https://api.dropbox.com/1/account/info", nil) if err != nil { http.Error(w, "Error getting info: "+err.Error(), 500) return } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { http.Error(w, "Error reading body:"+err.Error(), 500) return } if resp.StatusCode != 200 { http.Error(w, fmt.Sprintf("Get account/info returned status %d, %s", resp.StatusCode, b), 500) return } w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Write(b) } var httpAddr = flag.String("addr", ":8080", "HTTP server address") func main() { flag.Parse() if err := readCredentials(); err != nil { log.Fatalf("Error reading configuration, %v", err) } http.Handle("/", &authHandler{handler: serveHome, optional: true}) http.Handle("/info", &authHandler{handler: serveInfo}) http.HandleFunc("/login", serveLogin) http.HandleFunc("/logout", serveLogout) http.HandleFunc("/callback", serveOAuthCallback) if err := http.ListenAndServe(*httpAddr, nil); err != nil { log.Fatalf("Error listening, %v", err) } } var ( homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( ` login `)) homeTmpl = template.Must(template.New("home").Parse( `

info

logout `)) ) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/000077500000000000000000000000001377247250500257245ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/README.markdown000066400000000000000000000007301377247250500304250ustar00rootroot00000000000000This example shows how to use the oauth package with QuickBooks . The examples require a configuration file containing a consumer key and secret: 1. Apply to the developer program at https://developer.intuit.com/ and create an application. 2. $ cp config.json.example config.json. 3. Edit config.json to include your consumer key and secret from step 1. To run the web example: 1. $ go run main.go 2. Go to http://127.0.0.1:8080/ in a browser to try the application. golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/config.json.example000066400000000000000000000001271377247250500315160ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/main.go000066400000000000000000000146201377247250500272020ustar00rootroot00000000000000// Copyright 2014 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "text/template" "github.com/gomodule/oauth1/examples/session" "github.com/gomodule/oauth1/oauth" ) // Session state keys. const ( tempCredKey = "tempCred" tokenCredKey = "tokenCred" companyKey = "company" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://oauth.intuit.com/oauth/v1/get_request_token", ResourceOwnerAuthorizationURI: "https://appcenter.intuit.com/Connect/Begin", TokenRequestURI: "https://oauth.intuit.com/oauth/v1/get_access_token", Header: http.Header{"Accept": {"application/json"}}, } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } // serveLogin gets the OAuth temp credentials and redirects the user to the // Quickbooks's authorization page. func serveLogin(w http.ResponseWriter, r *http.Request) { callback := "http://" + r.Host + "/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(nil, callback, nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, nil), 302) } // serveOAuthCallback handles callbacks from the OAuth server. func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { s := session.Get(r) tempCred, _ := s[tempCredKey].(*oauth.Credentials) if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { http.Error(w, "Unknown oauth_token.", 500) return } tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) if err != nil { http.Error(w, "Error getting request token, "+err.Error(), 500) return } delete(s, tempCredKey) s[tokenCredKey] = tokenCred s[companyKey] = r.FormValue("realmId") if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // serveLogout clears the authentication cookie. func serveLogout(w http.ResponseWriter, r *http.Request) { s := session.Get(r) delete(s, tokenCredKey) delete(s, companyKey) if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // authHandler reads the auth cookie and invokes a handler with the result. type authHandler struct { handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials, company string) optional bool } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { s := session.Get(r) cred, _ := s[tokenCredKey].(*oauth.Credentials) if cred == nil && !h.optional { http.Error(w, "Not logged in.", 403) return } company, _ := s[companyKey].(string) h.handler(w, r, cred, company) } func callAPI(cred *oauth.Credentials, company string, endpoint string, form url.Values, data interface{}) error { resp, err := oauthClient.Get(nil, cred, fmt.Sprintf("https://qb.sbfinance.intuit.com/v3/company/%s/%s", company, endpoint), form) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { p, _ := ioutil.ReadAll(resp.Body) return fmt.Errorf("get %s returned status %d, %s", resp.Request.URL, resp.StatusCode, p) } return json.NewDecoder(resp.Body).Decode(data) } // respond responds to a request by executing the html template t with data. func respond(w http.ResponseWriter, t *template.Template, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.Execute(w, data); err != nil { log.Print(err) } } func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials, company string) { if r.URL.Path != "/" { http.NotFound(w, r) return } if cred == nil { data := struct{ Host string }{r.Host} respond(w, homeLoggedOutTmpl, &data) } else { respond(w, homeTmpl, nil) } } func serveAccounts(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials, company string) { var accounts map[string]interface{} if err := callAPI( cred, company, "query", url.Values{"query": {"select * from Account"}}, &accounts); err != nil { http.Error(w, "Error getting accounts, "+err.Error(), 500) return } respond(w, accountsTmpl, accounts) } var httpAddr = flag.String("addr", ":8080", "HTTP server address") func main() { flag.Parse() if err := readCredentials(); err != nil { log.Fatalf("Error reading configuration, %v", err) } http.Handle("/", &authHandler{handler: serveHome, optional: true}) http.Handle("/accounts", &authHandler{handler: serveAccounts}) http.HandleFunc("/login", serveLogin) http.HandleFunc("/logout", serveLogout) http.HandleFunc("/callback", serveOAuthCallback) if err := http.ListenAndServe(*httpAddr, nil); err != nil { log.Fatalf("Error listening, %v", err) } } var ( homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( ` `)) homeTmpl = template.Must(template.New("home").Parse( `

accounts `)) accountsTmpl = template.Must(template.New("accounts").Parse( `

home {{range .QueryResponse.Account}}

{{.Name}}{{end}} <{{.}} `)) ) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/session/000077500000000000000000000000001377247250500252355ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/session/session.go000066400000000000000000000033741377247250500272560ustar00rootroot00000000000000// Copyright 2015 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // Package session implements a session store for the Oauth1 examples. A real // application should not use this package. package session import ( "crypto/rand" "encoding/hex" "net/http" "sync" ) var ( mu sync.Mutex sessions = make(map[string]map[string]interface{}) ) // Get returns the session data for the request client. func Get(r *http.Request) (s map[string]interface{}) { if c, _ := r.Cookie("session"); c != nil && c.Value != "" { mu.Lock() s = sessions[c.Value] mu.Unlock() } if s == nil { s = make(map[string]interface{}) } return s } // Save saves session for the request client. func Save(w http.ResponseWriter, r *http.Request, s map[string]interface{}) error { key := "" if c, _ := r.Cookie("session"); c != nil { key = c.Value } if len(s) == 0 { if key != "" { mu.Lock() delete(sessions, key) mu.Unlock() } return nil } if key == "" { var buf [16]byte _, err := rand.Read(buf[:]) if err != nil { return err } key = hex.EncodeToString(buf[:]) http.SetCookie(w, &http.Cookie{ Name: "session", Path: "/", HttpOnly: true, Value: key, }) } mu.Lock() sessions[key] = s mu.Unlock() return nil } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/000077500000000000000000000000001377247250500252365ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/README.markdown000066400000000000000000000006731377247250500277450ustar00rootroot00000000000000This example shows how to use the oauth package with SmugMug. The examples require a configuration file containing a consumer key and secret: 1. Apply for an API key at http://www.smugmug.com/hack/apikeys 2. $ cp config.json.example config.json. 3. Edit config.json to include your Smugmug client key and secret from step 1. To run the web example: 1. $ go run main.go 2. Go to http://127.0.0.1:8080/ in a browser to try the application. golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/config.json.example000066400000000000000000000001271377247250500310300ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/main.go000066400000000000000000000146171377247250500265220ustar00rootroot00000000000000// Copyright 2013 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "text/template" "github.com/gomodule/oauth1/examples/session" "github.com/gomodule/oauth1/oauth" ) // Session state keys. const ( tempCredKey = "tempCred" tokenCredKey = "tokenCred" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "http://api.smugmug.com/services/oauth/getRequestToken.mg", ResourceOwnerAuthorizationURI: "http://api.smugmug.com/services/oauth/authorize.mg", TokenRequestURI: "http://api.smugmug.com/services/oauth/getAccessToken.mg", } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } // serveLogin gets the OAuth temp credentials and redirects the user to the // SmugMug's authorization page. func serveLogin(w http.ResponseWriter, r *http.Request) { callback := "http://" + r.Host + "/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(nil, callback, nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, nil), 302) } // serveOAuthCallback handles callbacks from the OAuth server. func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { s := session.Get(r) tempCred, _ := s[tempCredKey].(*oauth.Credentials) if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { http.Error(w, "Unknown oauth_token.", 500) return } tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) if err != nil { http.Error(w, "Error getting request token, "+err.Error(), 500) return } delete(s, tempCredKey) s[tokenCredKey] = tokenCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // serveLogout clears the authentication cookie. func serveLogout(w http.ResponseWriter, r *http.Request) { s := session.Get(r) delete(s, tokenCredKey) if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // authHandler reads the auth cookie and invokes a handler with the result. type authHandler struct { handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials) optional bool } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { cred, _ := session.Get(r)[tokenCredKey].(*oauth.Credentials) if cred == nil && !h.optional { http.Error(w, "Not logged in.", 403) return } h.handler(w, r, cred) } const apiURL = "https://api.smugmug.com/services/api/json/1.3.0/" // apiGet issues a GET request to the SmugMug API and decodes the response JSON to data. func apiGet(cred *oauth.Credentials, form url.Values, data interface{}) error { resp, err := oauthClient.Get(nil, cred, apiURL, form) if err != nil { return err } defer resp.Body.Close() return decodeResponse(resp, data) } // apiPost issues a POST request to the SmugMug API and decodes the response JSON to data. func apiPost(cred *oauth.Credentials, urlStr string, form url.Values, data interface{}) error { resp, err := oauthClient.Post(nil, cred, apiURL, form) if err != nil { return err } defer resp.Body.Close() return decodeResponse(resp, data) } // decodeResponse decodes the JSON response from the Twitter API. func decodeResponse(resp *http.Response, data interface{}) error { if resp.StatusCode != 200 { p, _ := ioutil.ReadAll(resp.Body) return fmt.Errorf("get %s returned status %d, %s", resp.Request.URL, resp.StatusCode, p) } return json.NewDecoder(resp.Body).Decode(data) } // respond responds to a request by executing the html template t with data. func respond(w http.ResponseWriter, t *template.Template, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.Execute(w, data); err != nil { log.Print(err) } } func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { if r.URL.Path != "/" { http.NotFound(w, r) return } if cred == nil { respond(w, homeLoggedOutTmpl, nil) } else { respond(w, homeTmpl, nil) } } func serveAlbums(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { var albums map[string]interface{} if err := apiGet( cred, url.Values{"method": {"smugmug.albums.get"}}, &albums); err != nil { http.Error(w, "Error getting albums, "+err.Error(), 500) return } respond(w, albumsTmpl, albums) } var httpAddr = flag.String("addr", ":8080", "HTTP server address") func main() { flag.Parse() if err := readCredentials(); err != nil { log.Fatalf("Error reading configuration, %v", err) } http.Handle("/", &authHandler{handler: serveHome, optional: true}) http.Handle("/albums", &authHandler{handler: serveAlbums}) http.HandleFunc("/login", serveLogin) http.HandleFunc("/logout", serveLogout) http.HandleFunc("/callback", serveOAuthCallback) if err := http.ListenAndServe(*httpAddr, nil); err != nil { log.Fatalf("Error listening, %v", err) } } var ( homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( ` login `)) homeTmpl = template.Must(template.New("home").Parse( `

albums

logout `)) albumsTmpl = template.Must(template.New("albums").Parse( `

home {{range .Albums}}

{{html .Title}}{{end}} <{{.}} `)) ) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/000077500000000000000000000000001377247250500252545ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/README.markdown000066400000000000000000000010021377247250500277460ustar00rootroot00000000000000This example shows how to use the oauth package with Twitter from a web application. The examples require a configuration file containing a consumer key and secret: 1. Register an application at https://dev.twitter.com/apps/new (Note: create a callback url for the app) 2. $ cp config.json.example config.json. 3. Edit config.json to include your Twitter consumer key and consumer secret from step 1. To run the example: 1. $ go run main.go 2. Go to http://127.0.0.1:8080/ in a browser to try the application. golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/config.json.example000066400000000000000000000001311377247250500310410ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/main.go000066400000000000000000000220121377247250500265240ustar00rootroot00000000000000// Copyright 2011 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "html/template" "io/ioutil" "log" "net/http" "net/url" "github.com/gomodule/oauth1/examples/session" "github.com/gomodule/oauth1/oauth" ) // Session state keys. const ( tempCredKey = "tempCred" tokenCredKey = "tokenCred" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token", ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize", TokenRequestURI: "https://api.twitter.com/oauth/access_token", } var signinOAuthClient oauth.Client var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } // serveSignin gets the OAuth temp credentials and redirects the user to the // Twitter's authentication page. func serveSignin(w http.ResponseWriter, r *http.Request) { callback := "http://" + r.Host + "/callback" tempCred, err := signinOAuthClient.RequestTemporaryCredentials(nil, callback, nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, signinOAuthClient.AuthorizationURL(tempCred, nil), 302) } // serveAuthorize gets the OAuth temp credentials and redirects the user to the // Twitter's authorization page. func serveAuthorize(w http.ResponseWriter, r *http.Request) { callback := "http://" + r.Host + "/callback" tempCred, err := oauthClient.RequestTemporaryCredentials(nil, callback, nil) if err != nil { http.Error(w, "Error getting temp cred, "+err.Error(), 500) return } s := session.Get(r) s[tempCredKey] = tempCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, oauthClient.AuthorizationURL(tempCred, nil), 302) } // serveOAuthCallback handles callbacks from the OAuth server. func serveOAuthCallback(w http.ResponseWriter, r *http.Request) { s := session.Get(r) tempCred, _ := s[tempCredKey].(*oauth.Credentials) if tempCred == nil || tempCred.Token != r.FormValue("oauth_token") { http.Error(w, "Unknown oauth_token.", 500) return } tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, r.FormValue("oauth_verifier")) if err != nil { http.Error(w, "Error getting request token, "+err.Error(), 500) return } delete(s, tempCredKey) s[tokenCredKey] = tokenCred if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // serveLogout clears the authentication cookie. func serveLogout(w http.ResponseWriter, r *http.Request) { s := session.Get(r) delete(s, tokenCredKey) if err := session.Save(w, r, s); err != nil { http.Error(w, "Error saving session , "+err.Error(), 500) return } http.Redirect(w, r, "/", 302) } // authHandler reads the auth cookie and invokes a handler with the result. type authHandler struct { handler func(w http.ResponseWriter, r *http.Request, c *oauth.Credentials) optional bool } func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { cred, _ := session.Get(r)[tokenCredKey].(*oauth.Credentials) if cred == nil && !h.optional { http.Error(w, "Not logged in.", 403) return } h.handler(w, r, cred) } // apiGet issues a GET request to the Twitter API and decodes the response JSON to data. func apiGet(cred *oauth.Credentials, urlStr string, form url.Values, data interface{}) error { resp, err := oauthClient.Get(nil, cred, urlStr, form) if err != nil { return err } defer resp.Body.Close() return decodeResponse(resp, data) } // apiPost issues a POST request to the Twitter API and decodes the response JSON to data. func apiPost(cred *oauth.Credentials, urlStr string, form url.Values, data interface{}) error { resp, err := oauthClient.Post(nil, cred, urlStr, form) if err != nil { return err } defer resp.Body.Close() return decodeResponse(resp, data) } // decodeResponse decodes the JSON response from the Twitter API. func decodeResponse(resp *http.Response, data interface{}) error { if resp.StatusCode != 200 { p, _ := ioutil.ReadAll(resp.Body) return fmt.Errorf("get %s returned status %d, %s", resp.Request.URL, resp.StatusCode, p) } return json.NewDecoder(resp.Body).Decode(data) } // respond responds to a request by executing the html template t with data. func respond(w http.ResponseWriter, t *template.Template, data interface{}) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.Execute(w, data); err != nil { log.Print(err) } } func serveHome(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { if r.URL.Path != "/" { http.NotFound(w, r) return } if cred == nil { respond(w, homeLoggedOutTmpl, nil) } else { respond(w, homeTmpl, nil) } } func serveTimeline(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { var timeline []map[string]interface{} if err := apiGet( cred, "https://api.twitter.com/1.1/statuses/home_timeline.json", url.Values{"include_entities": {"true"}}, &timeline); err != nil { http.Error(w, "Error getting timeline, "+err.Error(), 500) return } respond(w, timelineTmpl, timeline) } func serveMessages(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { var dms []map[string]interface{} if err := apiGet( cred, "https://api.twitter.com/1.1/direct_messages.json", nil, &dms); err != nil { http.Error(w, "Error getting timeline, "+err.Error(), 500) return } respond(w, messagesTmpl, dms) } func serveFollow(w http.ResponseWriter, r *http.Request, cred *oauth.Credentials) { var profile map[string]interface{} if err := apiPost( cred, "https://api.twitter.com/1.1/friendships/create.json", url.Values{"screen_name": {"gburd"}, "follow": {"true"}}, &profile); err != nil { http.Error(w, "Error following, "+err.Error(), 500) return } respond(w, followTmpl, profile) } var httpAddr = flag.String("addr", ":8080", "HTTP server address") func main() { flag.Parse() if err := readCredentials(); err != nil { log.Fatalf("Error reading configuration, %v", err) } // Use a different auth URL for "Sign in with Twitter." signinOAuthClient = oauthClient signinOAuthClient.ResourceOwnerAuthorizationURI = "https://api.twitter.com/oauth/authenticate" http.Handle("/", &authHandler{handler: serveHome, optional: true}) http.Handle("/timeline", &authHandler{handler: serveTimeline}) http.Handle("/messages", &authHandler{handler: serveMessages}) http.Handle("/follow", &authHandler{handler: serveFollow}) http.HandleFunc("/signin", serveSignin) http.HandleFunc("/authorize", serveAuthorize) http.HandleFunc("/logout", serveLogout) http.HandleFunc("/callback", serveOAuthCallback) if err := http.ListenAndServe(*httpAddr, nil); err != nil { log.Fatalf("Error listening, %v", err) } } var ( homeLoggedOutTmpl = template.Must(template.New("loggedout").Parse( ` Authorize or `)) homeTmpl = template.Must(template.New("home").Parse( `

timeline

direct messages

follow @gburd

logout `)) messagesTmpl = template.Must(template.New("messages").Parse( `

home {{range .}}

{{.sender.name}} {{.text}} {{end}} `)) timelineTmpl = template.Must(template.New("timeline").Parse( `

home {{range .}}

{{.user.name}} {{.text}} {{with .entities}} {{with .urls}}
urls: {{range .}}{{.expanded_url}}{{end}}{{end}} {{with .hashtags}}
hashtags: {{range .}}{{.text}}{{end}}{{end}} {{with .user_mentions}}
user_mentions: {{range .}}{{.screen_name}}{{end}}{{end}} {{end}} {{end}} `)) followTmpl = template.Must(template.New("follow").Parse( `

home

You are now following {{.name}} `)) ) golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/000077500000000000000000000000001377247250500257545ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/README.markdown000066400000000000000000000007521377247250500304610ustar00rootroot00000000000000This example shows how to use the oauth package with Twitter from a command line application. The examples require a configuration file containing a consumer key and secret: 1. Register an application at https://dev.twitter.com/apps/new (Note: create a callback url for the app) 2. $ cp config.json.example config.json. 3. Edit config.json to include your Twitter consumer key and consumer secret from step 1. To run the command line example with OOB authorization: 1. $ go run main.go golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/config.json.example000066400000000000000000000001311377247250500315410ustar00rootroot00000000000000{ "Token":"", "Secret":"" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/main.go000066400000000000000000000037521377247250500272360ustar00rootroot00000000000000// Copyright 2013 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io" "io/ioutil" "log" "os" "github.com/gomodule/oauth1/oauth" ) var oauthClient = oauth.Client{ TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token", ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize", TokenRequestURI: "https://api.twitter.com/oauth/access_token", } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials() error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } return json.Unmarshal(b, &oauthClient.Credentials) } func main() { if err := readCredentials(); err != nil { log.Fatal(err) } tempCred, err := oauthClient.RequestTemporaryCredentials(nil, "oob", nil) if err != nil { log.Fatal("RequestTemporaryCredentials:", err) } u := oauthClient.AuthorizationURL(tempCred, nil) fmt.Printf("1. Go to %s\n2. Authorize the application\n3. Enter verification code:\n", u) var code string fmt.Scanln(&code) tokenCred, _, err := oauthClient.RequestToken(nil, tempCred, code) if err != nil { log.Fatal(err) } resp, err := oauthClient.Get(nil, tokenCred, "https://api.twitter.com/1.1/statuses/home_timeline.json", nil) if err != nil { log.Fatal(err) } defer resp.Body.Close() if _, err := io.Copy(os.Stdout, resp.Body); err != nil { log.Fatal(err) } } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/000077500000000000000000000000001377247250500245235ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/README.markdown000066400000000000000000000005341377247250500272260ustar00rootroot00000000000000This example shows how to use the oauth package with Yelp. How to run the example: 1. Sign up and for a developer account and get your credentials at [the Yelp developer site](http://www.yelp.com/developers/manage_api_keys). 2. $ cp config.json.example config.json. 3. Edit config.json to include the credentials from step 1. 4. $ go run main.go golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/config.json.example000066400000000000000000000001711377247250500303140ustar00rootroot00000000000000{ "ConsumerKey": "", "ConsumerSecret": "", "Token": "", "TokenSecret": "" } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/main.go000066400000000000000000000043641377247250500260050ustar00rootroot00000000000000// Copyright 2015 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/url" "github.com/gomodule/oauth1/oauth" ) type client struct { client oauth.Client token oauth.Credentials } func (c *client) get(urlStr string, params url.Values, v interface{}) error { resp, err := c.client.Get(nil, &c.token, urlStr, params) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("yelp status %d", resp.StatusCode) } return json.NewDecoder(resp.Body).Decode(v) } var credPath = flag.String("config", "config.json", "Path to configuration file containing the application's credentials.") func readCredentials(c *client) error { b, err := ioutil.ReadFile(*credPath) if err != nil { return err } var creds struct { ConsumerKey string ConsumerSecret string Token string TokenSecret string } if err := json.Unmarshal(b, &creds); err != nil { return err } c.client.Credentials.Token = creds.ConsumerKey c.client.Credentials.Secret = creds.ConsumerSecret c.token.Token = creds.Token c.token.Secret = creds.TokenSecret return nil } func main() { var c client if err := readCredentials(&c); err != nil { log.Fatal(err) } var data struct { Businesses []struct { Name string Location struct { DisplayAddress []string `json:"display_address"` } } } form := url.Values{"term": {"food"}, "location": {"San Francisco"}} if err := c.get("http://api.yelp.com/v2/search", form, &data); err != nil { log.Fatal(err) } for _, b := range data.Businesses { addr := "" if len(b.Location.DisplayAddress) > 0 { addr = b.Location.DisplayAddress[0] } log.Println(b.Name, addr) } } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/go.mod000066400000000000000000000000531377247250500230400ustar00rootroot00000000000000module github.com/gomodule/oauth1 go 1.13 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/000077500000000000000000000000001377247250500230545ustar00rootroot00000000000000golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/examples_test.go000066400000000000000000000032771377247250500262710ustar00rootroot00000000000000// Copyright 2013 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package oauth_test import ( "net/http" "net/url" "strings" "github.com/gomodule/oauth1/oauth" ) // This example shows how to sign a request when the URL Opaque field is used. // See the note at http://golang.org/pkg/net/url/#URL for information on the // use of the URL Opaque field. func ExampleClient_SetAuthorizationHeader(client *oauth.Client, credentials *oauth.Credentials) error { form := url.Values{"maxResults": {"100"}} // The last element of path contains a "/". path := "/document/encoding%2gizp" // Create the request with the temporary path "/". req, err := http.NewRequest("GET", "http://api.example.com/", strings.NewReader(form.Encode())) if err != nil { return err } // Overwrite the temporary path with the actual request path. req.URL.Opaque = path // Sign the request. if err := client.SetAuthorizationHeader(req.Header, credentials, "GET", req.URL, form); err != nil { return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // process the response return nil } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/oauth.go000066400000000000000000000573361377247250500245410ustar00rootroot00000000000000// Copyright 2010 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // Package oauth is consumer interface for OAuth 1.0, OAuth 1.0a and RFC 5849. // // Redirection-based Authorization // // This section outlines how to use the oauth package in redirection-based // authorization (http://tools.ietf.org/html/rfc5849#section-2). // // Step 1: Create a Client using credentials and URIs provided by the server. // The Client can be initialized once at application startup and stored in a // package-level variable. // // Step 2: Request temporary credentials using the Client // RequestTemporaryCredentials method. The callbackURL parameter is the URL of // the callback handler in step 4. Save the returned credential secret so that // it can be later found using credential token as a key. The secret can be // stored in a database keyed by the token. Another option is to store the // token and secret in session storage or a cookie. // // Step 3: Redirect the user to URL returned from AuthorizationURL method. The // AuthorizationURL method uses the temporary credentials from step 2 and other // parameters as specified by the server. // // Step 4: The server redirects back to the callback URL specified in step 2 // with the temporary token and a verifier. Use the temporary token to find the // temporary secret saved in step 2. Using the temporary token, temporary // secret and verifier, request token credentials using the client RequestToken // method. Save the returned credentials for later use in the application. // // Signing Requests // // The Client type has two low-level methods for signing requests, SignForm and // SetAuthorizationHeader. // // The SignForm method adds an OAuth signature to a form. The application makes // 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 // 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. // // The Get, Put, Post and Delete methods sign and invoke a request using the // supplied net/http Client. These methods are easy to use, but not as flexible // as constructing a request using one of the low-level methods. // // Context With HTTP Client // // A context-enabled method can include a custom HTTP client in the // context and execute an HTTP request using the included HTTP client. // // hc := &http.Client{Timeout: 2 * time.Second} // ctx := context.WithValue(context.Background(), oauth.HTTPClient, hc) // c := oauth.Client{ /* Any settings */ } // resp, err := c.GetContext(ctx, &oauth.Credentials{}, rawurl, nil) package oauth // import "github.com/gomodule/oauth1/oauth" import ( "bytes" "context" "crypto" "crypto/hmac" "crypto/rand" "crypto/rsa" "crypto/sha1" "encoding/base64" "encoding/binary" "errors" "fmt" "io" "io/ioutil" "net/http" "net/url" "sort" "strconv" "strings" "sync/atomic" "time" ) // noscape[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, '0': true, true, true, true, true, true, true, true, true, true, '-': true, '.': true, '_': true, '~': true, } // encode encodes string per section 3.6 of the RFC. If double is true, then // the encoding is applied twice. func encode(s string, double bool) []byte { // Compute size of result. m := 3 if double { m = 5 } n := 0 for i := 0; i < len(s); i++ { if noEscape[s[i]] { n++ } else { n += m } } p := make([]byte, n) // Encode it. j := 0 for i := 0; i < len(s); i++ { b := s[i] if noEscape[b] { p[j] = b j++ } else if double { p[j] = '%' p[j+1] = '2' p[j+2] = '5' p[j+3] = "0123456789ABCDEF"[b>>4] p[j+4] = "0123456789ABCDEF"[b&15] j += 5 } else { p[j] = '%' p[j+1] = "0123456789ABCDEF"[b>>4] p[j+2] = "0123456789ABCDEF"[b&15] j += 3 } } return p } type keyValue struct{ key, value []byte } type byKeyValue []keyValue func (p byKeyValue) Len() int { return len(p) } func (p byKeyValue) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p byKeyValue) Less(i, j int) bool { sgn := bytes.Compare(p[i].key, p[j].key) if sgn == 0 { sgn = bytes.Compare(p[i].value, p[j].value) } return sgn < 0 } func (p byKeyValue) appendValues(values url.Values) byKeyValue { for k, vs := range values { k := encode(k, true) for _, v := range vs { v := encode(v, true) p = append(p, keyValue{k, v}) } } return p } // writeBaseString writes method, url, and params to w using the OAuth signature // 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{'&'}) // URL scheme := strings.ToLower(u.Scheme) host := strings.ToLower(u.Host) uNoQuery := *u uNoQuery.RawQuery = "" path := uNoQuery.RequestURI() switch { case scheme == "http" && strings.HasSuffix(host, ":80"): host = host[:len(host)-len(":80")] case scheme == "https" && strings.HasSuffix(host, ":443"): 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{'&'}) // Create sorted slice of encoded parameters. Parameter keys and values are // double encoded in a single step. This is safe because double encoding // does not change the sort order. queryParams := u.Query() p := make(byKeyValue, 0, len(form)+len(queryParams)+len(oauthParams)) p = p.appendValues(form) p = p.appendValues(queryParams) for k, v := range oauthParams { p = append(p, keyValue{encode(k, true), encode(v, true)}) } sort.Sort(p) // Write the parameters. encodedAmp := encode("&", false) encodedEqual := encode("=", false) sep := false for _, kv := range p { if sep { w.Write(encodedAmp) } else { sep = true } w.Write(kv.key) w.Write(encodedEqual) w.Write(kv.value) } } var nonceCounter uint64 func init() { if err := binary.Read(rand.Reader, binary.BigEndian, &nonceCounter); err != nil { // fallback to time if rand reader is broken nonceCounter = uint64(time.Now().UnixNano()) } } // nonce returns a unique string. func nonce() string { return strconv.FormatUint(atomic.AddUint64(&nonceCounter, 1), 16) } // SignatureMethod identifies a signature method. type SignatureMethod int func (sm SignatureMethod) String() string { switch sm { case RSASHA1: return "RSA-SHA1" case HMACSHA1: return "HMAC-SHA1" case PLAINTEXT: return "PLAINTEXT" default: return "unknown" } } const ( HMACSHA1 SignatureMethod = iota // HMAC-SHA1 RSASHA1 // RSA-SHA1 PLAINTEXT // Plain text ) // Credentials represents client, temporary and token credentials. type Credentials struct { Token string // Also known as consumer key or access token. Secret string // Also known as consumer secret or access token secret. } // Client represents an OAuth client. type Client struct { // Credentials specifies the client key and secret. // Also known as the consumer key and secret Credentials Credentials // TemporaryCredentialRequestURI is the endpoint used by the client to // obtain a set of temporary credentials. Also known as the request token // URL. TemporaryCredentialRequestURI string // ResourceOwnerAuthorizationURI is the endpoint to which the resource // owner is redirected to grant authorization. Also known as authorization // URL. ResourceOwnerAuthorizationURI string // TokenRequestURI is the endpoint used by the client to request a set of // token credentials using a set of temporary credentials. Also known as // access token URL. TokenRequestURI string // RenewCredentialRequestURI is the endpoint the client uses to // request a new set of token credentials using the old set of credentials. RenewCredentialRequestURI string // TemporaryCredentialsMethod is the HTTP method used by the client to // obtain a set of temporary credentials. If this field is the empty // string, then POST is used. TemporaryCredentialsMethod string // 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. TokenCredentailsMethod string // Header specifies optional extra headers for requests. Header http.Header // SignatureMethod specifies the method for signing a request. SignatureMethod SignatureMethod // PrivateKey is the private key to use for RSA-SHA1 signatures. This field // must be set for RSA-SHA1 signatures and ignored for other signature // methods. PrivateKey *rsa.PrivateKey } type request struct { credentials *Credentials method string u *url.URL form url.Values verifier string sessionHandle 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. 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", } if c.SignatureMethod != PLAINTEXT { oauthParams["oauth_timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) oauthParams["oauth_nonce"] = nonce() } if r.credentials != nil { oauthParams["oauth_token"] = r.credentials.Token } if r.verifier != "" { oauthParams["oauth_verifier"] = r.verifier } if r.sessionHandle != "" { oauthParams["oauth_session_handle"] = r.sessionHandle } if r.callbackURL != "" { oauthParams["oauth_callback"] = r.callbackURL } testHook(oauthParams) var signature string switch c.SignatureMethod { case HMACSHA1: key := encode(c.Credentials.Secret, false) key = append(key, '&') if r.credentials != nil { key = append(key, encode(r.credentials.Secret, false)...) } h := hmac.New(sha1.New, key) 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") } 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)) if err != nil { return nil, err } signature = base64.StdEncoding.EncodeToString(rawSignature) case PLAINTEXT: rawSignature := encode(c.Credentials.Secret, false) rawSignature = append(rawSignature, '&') if r.credentials != nil { rawSignature = append(rawSignature, encode(r.credentials.Secret, false)...) } signature = string(rawSignature) default: return nil, errors.New("oauth: unknown signature method") } oauthParams["oauth_signature"] = signature return oauthParams, nil } // SignForm adds an OAuth signature to form. The urlStr argument must not // include a query string. // // See http://tools.ietf.org/html/rfc5849#section-3.5.2 for // 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 { 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") } p, err := c.oauthParams(&request{credentials: credentials, method: method, u: u, form: form}) if err != nil { return err } for k, v := range p { form.Set(k, v) } return nil } // SignParam is deprecated. Use SignForm instead. func (c *Client) SignParam(credentials *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}) 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", } func (c *Client) authorizationHeader(r *request) (string, error) { p, err := c.oauthParams(r) if err != nil { return "", err } var h []byte // Append parameters in a fixed order to support testing. for _, k := range oauthKeys { if v, ok := p[k]; ok { if h == nil { h = []byte(`OAuth `) } else { h = append(h, ", "...) } h = append(h, k...) h = append(h, `="`...) h = append(h, encode(v, false)...) h = append(h, '"') } } return string(h), nil } // AuthorizationHeader returns the HTTP authorization header value for given // 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 { // 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}) return v } // SetAuthorizationHeader adds an OAuth signature to a request header. // // 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}) if err != nil { return err } header.Set("Authorization", v) return nil } func (c *Client) do(ctx context.Context, urlStr string, r *request) (*http.Response, error) { var body io.Reader if r.method != http.MethodGet { body = strings.NewReader(r.form.Encode()) } req, err := http.NewRequest(r.method, urlStr, body) if err != nil { return nil, err } if req.URL.RawQuery != "" { return nil, errors.New("oauth: url must not contain a query string") } for k, v := range c.Header { req.Header[k] = v } r.u = req.URL auth, err := c.authorizationHeader(r) if err != nil { return nil, err } req.Header.Set("Authorization", auth) if r.method == http.MethodGet { req.URL.RawQuery = r.form.Encode() } else { req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } req = req.WithContext(ctx) client := contextClient(ctx) return client.Do(req) } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.GetContext(ctx, credentials, 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}) } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.PostContext(ctx, credentials, 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}) } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.DeleteContext(ctx, credentials, 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}) } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.PutContext(ctx, credentials, 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) requestCredentials(ctx context.Context, u string, r *request) (*Credentials, url.Values, error) { if r.method == "" { r.method = http.MethodPost } resp, err := c.do(ctx, u, r) if err != nil { return nil, nil, err } p, err := ioutil.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, Body: p, msg: err.Error()} } 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))} } m, err := url.ParseQuery(string(p)) if err != nil { return nil, nil, RequestCredentialsError{StatusCode: resp.StatusCode, Header: resp.Header, Body: p, msg: err.Error()} } tokens := m["oauth_token"] 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"} } secrets := m["oauth_token_secret"] 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 &Credentials{Token: tokens[0], Secret: secrets[0]}, m, nil } // RequestTemporaryCredentials requests temporary credentials from the server. // See http://tools.ietf.org/html/rfc5849#section-2.1 for information about // temporary credentials. func (c *Client) RequestTemporaryCredentials(client *http.Client, callbackURL string, additionalParams url.Values) (*Credentials, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.RequestTemporaryCredentialsContext(ctx, callbackURL, additionalParams) } // RequestTemporaryCredentialsContext uses Context to perform RequestTemporaryCredentials. func (c *Client) RequestTemporaryCredentialsContext(ctx context.Context, callbackURL string, additionalParams url.Values) (*Credentials, error) { credentials, _, err := c.requestCredentials(ctx, c.TemporaryCredentialRequestURI, &request{method: c.TemporaryCredentialsMethod, form: additionalParams, callbackURL: callbackURL}) return credentials, err } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.RequestTokenContext(ctx, temporaryCredentials, verifier) } // RequestTokenContext uses Context to perform RequestToken. func (c *Client) RequestTokenContext(ctx context.Context, temporaryCredentials *Credentials, verifier string) (*Credentials, url.Values, error) { return c.requestCredentials(ctx, c.TokenRequestURI, &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, credentials *Credentials, sessionHandle string) (*Credentials, url.Values, error) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.RenewRequestCredentialsContext(ctx, credentials, 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}) } // 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) { ctx := context.WithValue(context.Background(), HTTPClient, client) return c.RequestTokenXAuthContext(ctx, temporaryCredentials, user, password) } // RequestTokenXAuthContext uses Context to perform RequestTokenXAuth. 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: 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(temporaryCredentials *Credentials, additionalParams url.Values) string { params := make(url.Values) for k, vs := range additionalParams { params[k] = vs } params.Set("oauth_token", temporaryCredentials.Token) return c.ResourceOwnerAuthorizationURI + "?" + params.Encode() } // HTTPClient is the context key to use with context's // WithValue function to associate an *http.Client value with a context. var HTTPClient contextKey type contextKey struct{} func contextClient(ctx context.Context) *http.Client { if ctx != nil { if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok && hc != nil { return hc } } 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 } golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/oauth_test.go000066400000000000000000000531661377247250500255750ustar00rootroot00000000000000// Copyright 2010 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package oauth import ( "bytes" "crypto/x509" "encoding/pem" "io" "io/ioutil" "net/http" "net/http/cookiejar" "net/http/httptest" "net/url" "strings" "testing" "golang.org/x/net/context" ) func parseURL(urlStr string) *url.URL { u, err := url.Parse(urlStr) if err != nil { panic(err) } return u } var oauthTests = []struct { method string url *url.URL form url.Values nonce string timestamp string clientCredentials Credentials credentials Credentials signatureMethod SignatureMethod base string header string }{ { // Simple example from Twitter OAuth tool method: "GET", url: parseURL("https://api.twitter.com/1/"), form: url.Values{"page": {"10"}}, nonce: "8067e8abc6bdca2006818132445c8f4c", timestamp: "1355795903", clientCredentials: Credentials{"kMViZR2MHk2mM7hUNVw9A", "56Fgl58yOfqXOhHXX0ybvOmSnPQFvR2miYmm30A"}, credentials: Credentials{"10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", "yF75mvq4LZMHj9O0DXwoC3ZxUnN1ptvieThYuOAYM"}, base: `GET&https%3A%2F%2Fapi.twitter.com%2F1%2F&oauth_consumer_key%3DkMViZR2MHk2mM7hUNVw9A%26oauth_nonce%3D8067e8abc6bdca2006818132445c8f4c%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355795903%26oauth_token%3D10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU%26oauth_version%3D1.0%26page%3D10`, header: `OAuth oauth_consumer_key="kMViZR2MHk2mM7hUNVw9A", oauth_nonce="8067e8abc6bdca2006818132445c8f4c", oauth_signature="o5cx1ggJrY9ognZuVVeUwglKV8U%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355795903", oauth_token="10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", oauth_version="1.0"`, }, { // Test case and port insensitivity. method: "GeT", url: parseURL("https://apI.twItter.com:443/1/"), form: url.Values{"page": {"10"}}, nonce: "8067e8abc6bdca2006818132445c8f4c", timestamp: "1355795903", clientCredentials: Credentials{"kMViZR2MHk2mM7hUNVw9A", "56Fgl58yOfqXOhHXX0ybvOmSnPQFvR2miYmm30A"}, credentials: Credentials{"10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", "yF75mvq4LZMHj9O0DXwoC3ZxUnN1ptvieThYuOAYM"}, base: `GET&https%3A%2F%2Fapi.twitter.com%2F1%2F&oauth_consumer_key%3DkMViZR2MHk2mM7hUNVw9A%26oauth_nonce%3D8067e8abc6bdca2006818132445c8f4c%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355795903%26oauth_token%3D10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU%26oauth_version%3D1.0%26page%3D10`, header: `OAuth oauth_consumer_key="kMViZR2MHk2mM7hUNVw9A", oauth_nonce="8067e8abc6bdca2006818132445c8f4c", oauth_signature="o5cx1ggJrY9ognZuVVeUwglKV8U%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355795903", oauth_token="10212-JJ3Zc1A49qSMgdcAO2GMOpW9l7A348ESmhjmOBOU", oauth_version="1.0"`, }, { // Example generated using the Netflix OAuth tool. method: "GET", url: parseURL("http://api-public.netflix.com/catalog/titles"), form: url.Values{"term": {"Dark Knight"}, "count": {"2"}}, nonce: "1234", timestamp: "1355850443", clientCredentials: Credentials{"apiKey001", "sharedSecret002"}, credentials: Credentials{"accessToken003", "accessSecret004"}, base: `GET&http%3A%2F%2Fapi-public.netflix.com%2Fcatalog%2Ftitles&count%3D2%26oauth_consumer_key%3DapiKey001%26oauth_nonce%3D1234%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1355850443%26oauth_token%3DaccessToken003%26oauth_version%3D1.0%26term%3DDark%2520Knight`, header: `OAuth oauth_consumer_key="apiKey001", oauth_nonce="1234", oauth_signature="0JAoaqt6oz6TJx8N%2B06XmhPjcOs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1355850443", oauth_token="accessToken003", oauth_version="1.0"`, }, { // Special characters in form values. method: "GET", url: parseURL("http://PHOTOS.example.net:8001/Photos"), form: url.Values{"photo size": {"300%"}, "title": {"Back of $100 Dollars Bill"}}, nonce: "kllo~9940~pd9333jh", timestamp: "1191242096", clientCredentials: Credentials{"dpf43f3++p+#2l4k3l03", "secret01"}, credentials: Credentials{"nnch734d(0)0sl2jdk", "secret02"}, base: "GET&http%3A%2F%2Fphotos.example.net%3A8001%2FPhotos&oauth_consumer_key%3Ddpf43f3%252B%252Bp%252B%25232l4k3l03%26oauth_nonce%3Dkllo~9940~pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d%25280%25290sl2jdk%26oauth_version%3D1.0%26photo%2520size%3D300%2525%26title%3DBack%2520of%2520%2524100%2520Dollars%2520Bill", header: `OAuth oauth_consumer_key="dpf43f3%2B%2Bp%2B%232l4k3l03", oauth_nonce="kllo~9940~pd9333jh", oauth_signature="n1UAoQy2PoIYizZUiWvkdCxM3P0%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d%280%290sl2jdk", oauth_version="1.0"`, }, { // Special characters in path, multiple values for same key in form. method: "GET", url: parseURL("http://EXAMPLE.COM:80/Space%20Craft"), form: url.Values{"name": {"value", "value"}}, nonce: "Ix4U1Ei3RFL", timestamp: "1327384901", clientCredentials: Credentials{"abcd", "efgh"}, credentials: Credentials{"ijkl", "mnop"}, base: "GET&http%3A%2F%2Fexample.com%2FSpace%2520Craft&name%3Dvalue%26name%3Dvalue%26oauth_consumer_key%3Dabcd%26oauth_nonce%3DIx4U1Ei3RFL%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1327384901%26oauth_token%3Dijkl%26oauth_version%3D1.0", header: `OAuth oauth_consumer_key="abcd", oauth_nonce="Ix4U1Ei3RFL", oauth_signature="TZZ5u7qQorLnmKs%2Biqunb8gqkh4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1327384901", oauth_token="ijkl", oauth_version="1.0"`, }, { // Query string in URL. method: "GET", url: parseURL("http://EXAMPLE.COM:80/Space%20Craft?name=value"), form: url.Values{"name": {"value"}}, nonce: "Ix4U1Ei3RFL", timestamp: "1327384901", clientCredentials: Credentials{"abcd", "efgh"}, credentials: Credentials{"ijkl", "mnop"}, base: "GET&http%3A%2F%2Fexample.com%2FSpace%2520Craft&name%3Dvalue%26name%3Dvalue%26oauth_consumer_key%3Dabcd%26oauth_nonce%3DIx4U1Ei3RFL%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1327384901%26oauth_token%3Dijkl%26oauth_version%3D1.0", header: `OAuth oauth_consumer_key="abcd", oauth_nonce="Ix4U1Ei3RFL", oauth_signature="TZZ5u7qQorLnmKs%2Biqunb8gqkh4%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1327384901", oauth_token="ijkl", oauth_version="1.0"`, }, { // "/" in form value. method: "POST", url: parseURL("https://stream.twitter.com/1.1/statuses/filter.json"), form: url.Values{"track": {"example.com/abcd"}}, nonce: "bf2cb6d611e59f99103238fc9a3bb8d8", timestamp: "1362434376", clientCredentials: Credentials{"consumer_key", "consumer_secret"}, credentials: Credentials{"token", "secret"}, base: "POST&https%3A%2F%2Fstream.twitter.com%2F1.1%2Fstatuses%2Ffilter.json&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3Dbf2cb6d611e59f99103238fc9a3bb8d8%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1362434376%26oauth_token%3Dtoken%26oauth_version%3D1.0%26track%3Dexample.com%252Fabcd", header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="bf2cb6d611e59f99103238fc9a3bb8d8", oauth_signature="LcxylEOnNdgoKSJi7jX07mxcvfM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1362434376", oauth_token="token", oauth_version="1.0"`, }, { // "/" in query string method: "POST", url: parseURL("https://stream.twitter.com/1.1/statuses/filter.json?track=example.com/query"), form: url.Values{}, nonce: "884275759fbab914654b50ae643c563a", timestamp: "1362435218", clientCredentials: Credentials{"consumer_key", "consumer_secret"}, credentials: Credentials{"token", "secret"}, base: "POST&https%3A%2F%2Fstream.twitter.com%2F1.1%2Fstatuses%2Ffilter.json&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3D884275759fbab914654b50ae643c563a%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1362435218%26oauth_token%3Dtoken%26oauth_version%3D1.0%26track%3Dexample.com%252Fquery", header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="884275759fbab914654b50ae643c563a", oauth_signature="OAldqvRrKDXRGZ9BqSi2CqeVH0g%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1362435218", oauth_token="token", oauth_version="1.0"`, }, { // QuickBooks query string method: "GET", url: parseURL("https://qb.sbfinance.intuit.com/v3/company/1273852765/query"), form: url.Values{"query": {"select * from account"}}, nonce: "12345678", timestamp: "1409876517", clientCredentials: Credentials{"consumer_key", "consumer_secret"}, credentials: Credentials{"token", "secret"}, base: "GET&https%3A%2F%2Fqb.sbfinance.intuit.com%2Fv3%2Fcompany%2F1273852765%2Fquery&oauth_consumer_key%3Dconsumer_key%26oauth_nonce%3D12345678%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1409876517%26oauth_token%3Dtoken%26oauth_version%3D1.0%26query%3Dselect%2520%252A%2520from%2520account", header: `OAuth oauth_consumer_key="consumer_key", oauth_nonce="12345678", oauth_signature="7crYee%2BJLvg7dksQiHbarUHN3rY%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1409876517", oauth_token="token", oauth_version="1.0"`, }, { // Plain text signature method signatureMethod: PLAINTEXT, method: "GET", url: parseURL("http://example.com/"), clientCredentials: Credentials{"key", "secret"}, credentials: Credentials{"accesskey", "accesssecret"}, header: `OAuth oauth_consumer_key="key", oauth_signature="secret%26accesssecret", oauth_signature_method="PLAINTEXT", oauth_token="accesskey", oauth_version="1.0"`, }, { // RSA-SHA1 signature method signatureMethod: RSASHA1, method: "GET", url: parseURL("http://term.ie/oauth/example/echo_api.php"), form: url.Values{"method": {"foo%20bar"}, "bar": {"baz"}}, nonce: "a7da4d14579d61886be9d596d1a6a720", timestamp: "1420240290", clientCredentials: Credentials{Token: "key"}, credentials: Credentials{Token: "accesskey"}, base: `GET&http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Fecho_api.php&bar%3Dbaz%26method%3Dfoo%252520bar%26oauth_consumer_key%3Dkey%26oauth_nonce%3Da7da4d14579d61886be9d596d1a6a720%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1420240290%26oauth_token%3Daccesskey%26oauth_version%3D1.0`, header: `OAuth oauth_consumer_key="key", oauth_nonce="a7da4d14579d61886be9d596d1a6a720", oauth_signature="jPun728OkfFo7BjZiaQ5UBVChwk6tf0uKNFDmNKVb%2Bd6aWYEzsDVkqqjcgTrCRNabK8ubAnhyprafk0mk3zEJe%2BxGb9GKauqwUJ6ZZoGJNYYZg3BZUQvdxSKFs1M4MUMv3fxntmD%2BoyE8jPbrVM2zD1G1AAPm79sX%2B8XE25tBE8%3D", oauth_signature_method="RSA-SHA1", oauth_timestamp="1420240290", oauth_token="accesskey", oauth_version="1.0"`, }, } func TestBaseString(t *testing.T) { for _, ot := range oauthTests { if ot.signatureMethod == PLAINTEXT { // PLAINTEXT signature does not use the base string. continue } oauthParams := map[string]string{ "oauth_consumer_key": ot.clientCredentials.Token, "oauth_nonce": ot.nonce, "oauth_signature_method": ot.signatureMethod.String(), "oauth_timestamp": ot.timestamp, "oauth_token": ot.credentials.Token, "oauth_version": "1.0", } var buf bytes.Buffer writeBaseString(&buf, ot.method, ot.url, ot.form, oauthParams) base := buf.String() if base != ot.base { t.Errorf("base string for %s %s\n = %q,\n want %q", ot.method, ot.url, base, ot.base) } } } var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3 gYytUvtC2JlYzypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp /IpH7kH41EtbmUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQAB AoGAWFlbZXlM2r5G6z48tE+RTKLvB1/btgAtq8vLw/5e3KnnbcDD6fZO07m4DRaP jRryrJdsp8qazmUdcY0O1oK4FQfpprknDjP+R1XHhbhkQ4WEwjmxPstZMUZaDWF5 8d3otc23mCzwh3YcUWFu09KnMpzZsK59OfyjtkS44EDWpbECQQDXgN0ODboKsuEA VAhAtPUqspU9ivRa6yLai9kCnPb9GcztrsJZQm4NHcKVbmD2F2L4pDRx4Pmglhfl V7G/a6T7AkEA1kfU0+DkXc6I/jXHJ6pDLA5s7dBHzWgDsBzplSdkVQbKT3MbeYje ByOxzXhulOWLBQW/vxmW4HwU95KTRlj06QJASPoBYY3yb0cN/J94P/lHgJMDCNky UEuJ/PoYndLrrN/8zow8kh91xwlJ6HJ9cTiQMmTgwaOOxPuu0eI1df4M2wJBAJJS WrKUT1z/O+zbLDOZwGTFNPzvzRgmft4z4A1J6OlmyZ+XKpvDKloVtcRpCJoEZPn5 AwaroquID4k/PfI7rIECQHeWa6+kPADv9IrK/92mujujS0MSEiynDw5NjTnHAH0v 8TrXzs+LCWDN/gbOCKPfnWRkgwgOeC8NN3h0zUIIUtA= -----END RSA PRIVATE KEY----- ` func TestAuthorizationHeader(t *testing.T) { originalTestHook := testHook defer func() { testHook = originalTestHook }() block, _ := pem.Decode([]byte(pemPrivateKey)) privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { t.Fatal(err) } for _, ot := range oauthTests { testHook = func(p map[string]string) { if _, ok := p["oauth_nonce"]; ok { p["oauth_nonce"] = ot.nonce } if _, ok := p["oauth_timestamp"]; ok { p["oauth_timestamp"] = ot.timestamp } } c := Client{Credentials: ot.clientCredentials, SignatureMethod: ot.signatureMethod, PrivateKey: privateKey} header, err := c.authorizationHeader(&request{credentials: &ot.credentials, method: ot.method, u: ot.url, form: ot.form}) if err != nil { t.Errorf("authorizationHeader(&cred, %q, %q, %v) returned error %v", ot.method, ot.url.String(), ot.form, err) continue } if header != ot.header { t.Errorf("authorizationHeader(&cred, %q, %q, %v) =\n %s\nwant: %s", ot.method, ot.url.String(), ot.form, header, ot.header) } } } func TestNonce(t *testing.T) { // This test is flaky, but failures should be very rare. n := nonce() if len(n) < 8 { t.Fatalf("nonce is %s, exected something longer", n) } } func TestRequestToken(t *testing.T) { var method string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { expectedMethod := method if method == "" { expectedMethod = http.MethodPost } if r.Method != expectedMethod { t.Errorf("got method %s, want %s", r.Method, expectedMethod) } expectedContenType := "" if r.Method != http.MethodGet { expectedContenType = "application/x-www-form-urlencoded" } if contentType := r.Header.Get("Content-Type"); contentType != expectedContenType { t.Errorf("got content type %q, want %q", contentType, expectedContenType) } if auth := r.Header.Get("Authorization"); !strings.Contains(auth, `oauth_verifier="verifier"`) { t.Errorf("verifier missing from auth header %q", auth) } v := url.Values{} v.Set("oauth_token", "token") v.Set("oauth_token_secret", "secret") io.WriteString(w, v.Encode()) })) defer ts.Close() for _, method = range []string{"", "GET", "POST"} { c := Client{TokenRequestURI: ts.URL, TokenCredentailsMethod: method} cred, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier") if err != nil { t.Errorf("returned error %v", err) } if cred.Token != "token" { t.Errorf("token for %s want %s", cred.Token, "token") } if cred.Secret != "secret" { t.Errorf("secret for %s want %s", cred.Secret, "secret") } } } func TestRenewRequestCredentials(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { a := r.Header.Get("Authorization") if !strings.Contains(a, `oauth_token="token"`) { t.Errorf("Authorization header %q should contains %q", a, `oauth_token="token"`) } if !strings.Contains(a, `oauth_session_handle="session-handle"`) { t.Errorf("Authorization header %q should contains %q", a, `oauth_session_handle="session-handle"`) } v := url.Values{} v.Set("oauth_token", "response-token") v.Set("oauth_token_secret", "response-token-secret") v.Set("oauth_session_handle", "response-session-handle") io.WriteString(w, v.Encode()) })) defer ts.Close() c := Client{RenewCredentialRequestURI: ts.URL} cred, rv, err := c.RenewRequestCredentials(http.DefaultClient, &Credentials{Token: "token"}, "session-handle") if err != nil { t.Errorf("returned error %v", err) } if cred.Token != "response-token" { t.Errorf("token for %s want %s", cred.Token, "response-token") } if cred.Secret != "response-token-secret" { t.Errorf("secret for %s want %s", cred.Secret, "response-token-secret") } if rv.Get("oauth_session_handle") != "response-session-handle" { t.Errorf("session handle for %s want %s", rv.Get("oauth_session_handle"), "response-session-handle") } } func TestGet(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { t.Errorf("got method %s, want %s", r.Method, http.MethodGet) } if err := r.ParseForm(); err != nil { t.Errorf("returned error %v", err) } if form := r.Form.Get("form"); form != "foo" { t.Errorf("form %s, want %s", form, "foo") } cookie, err := r.Cookie("client-cookie") if err != nil { t.Errorf("returned error %v", err) } if cookie.Value != "foobar" { t.Errorf("client-cookie %s, want %s", cookie.Value, "foobar") } io.WriteString(w, "bar") })) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Errorf("returned error %v", err) } jar, err := cookiejar.New(nil) if err != nil { t.Errorf("returned error %v", err) } jar.SetCookies(u, []*http.Cookie{&http.Cookie{Name: "client-cookie", Value: "foobar"}}) v := url.Values{} v.Set("form", "foo") c := Client{} resp, err := c.Get(&http.Client{Jar: jar}, &Credentials{}, u.String(), v) if err != nil { t.Errorf("returned error %v", err) } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("returned error %v", err) } if string(b) != "bar" { t.Errorf("body %s, want %s", string(b), "bar") } } func TestGet_ClientNil(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { t.Errorf("got method %s, want %s", r.Method, http.MethodGet) } io.WriteString(w, "bar") })) defer ts.Close() c := Client{} resp, err := c.Get(nil, &Credentials{}, ts.URL, nil) if err != nil { t.Errorf("returned error %v", err) } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("returned error %v", err) } if string(b) != "bar" { t.Errorf("body %s, want %s", string(b), "bar") } } func TestGetContext(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { t.Errorf("got method %s, want %s", r.Method, http.MethodGet) } cookie, err := r.Cookie("client-cookie") if err != nil { t.Errorf("returned error %v", err) } if cookie.Value != "foobar" { t.Errorf("client-cookie %s, want %s", cookie.Value, "foobar") } io.WriteString(w, "bar") })) defer ts.Close() u, err := url.Parse(ts.URL) if err != nil { t.Errorf("returned error %v", err) } jar, err := cookiejar.New(nil) if err != nil { t.Errorf("returned error %v", err) } jar.SetCookies(u, []*http.Cookie{&http.Cookie{Name: "client-cookie", Value: "foobar"}}) ctx := context.WithValue(context.Background(), HTTPClient, &http.Client{Jar: jar}) c := Client{} resp, err := c.GetContext(ctx, &Credentials{}, u.String(), nil) if err != nil { t.Errorf("returned error %v", err) } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("returned error %v", err) } if string(b) != "bar" { t.Errorf("body %s, want %s", string(b), "bar") } } func TestRequestCredentialsError(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("WWW-Authenticate", "oauth_problem=token_rejected") w.WriteHeader(http.StatusUnauthorized) io.WriteString(w, "oauth_problem=token_rejected") })) defer ts.Close() c := Client{TokenRequestURI: ts.URL} _, _, err := c.RequestToken(http.DefaultClient, &Credentials{}, "verifier") if err == nil { t.Error("error should not be nil") } if rce, ok := err.(RequestCredentialsError); ok { if rce.StatusCode != http.StatusUnauthorized { t.Errorf("status code %d, want %d", rce.StatusCode, http.StatusUnauthorized) } wa := rce.Header.Get("WWW-Authenticate") if wa != "oauth_problem=token_rejected" { t.Errorf("WWW-Authenticate header %s, want %s", wa, "oauth_problem=token_rejected") } if string(rce.Body) != "oauth_problem=token_rejected" { t.Errorf("body %s,want %s", rce.Body, "oauth_problem=token_rejected") } } else { t.Error("error should be assertable RequestCredentialsError") } } func TestGetContext_Cancel(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { })) defer ts.Close() ctx, cancel := context.WithCancel(context.Background()) cancel() c := Client{} _, err := c.GetContext(ctx, &Credentials{}, ts.URL, nil) if err == nil { t.Error("error should not be nil") } }