pax_global_header 0000666 0000000 0000000 00000000064 13772472505 0014526 g ustar 00root root 0000000 0000000 52 comment=864d9cc92d61cd6e6954432d51a8788d213a2b48
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/ 0000775 0000000 0000000 00000000000 13772472505 0021734 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/.gitignore 0000664 0000000 0000000 00000000043 13772472505 0023721 0 ustar 00root root 0000000 0000000 _testmain.go
*.6
*.a
6.out
example
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/.travis.yml 0000664 0000000 0000000 00000000133 13772472505 0024042 0 ustar 00root root 0000000 0000000 language: 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.markdown 0000664 0000000 0000000 00000002672 13772472505 0024444 0 ustar 00root root 0000000 0000000 # OAuth1
[](https://godoc.org/github.com/gomodule/oauth1/oauth)
[](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/ 0000775 0000000 0000000 00000000000 13772472505 0023552 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/ 0000775 0000000 0000000 00000000000 13772472505 0025520 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/appengine/README.markdown 0000664 0000000 0000000 00000000726 13772472505 0030226 0 ustar 00root root 0000000 0000000 This 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.go 0000664 0000000 0000000 00000014051 13772472505 0026630 0 ustar 00root root 0000000 0000000 // 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.yaml 0000664 0000000 0000000 00000000261 13772472505 0027163 0 ustar 00root root 0000000 0000000 application: 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.example 0000664 0000000 0000000 00000000131 13772472505 0031305 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/ 0000775 0000000 0000000 00000000000 13772472505 0025205 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/README.markdown 0000664 0000000 0000000 00000000745 13772472505 0027714 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000133 13772472505 0030774 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/discogs/main.go 0000664 0000000 0000000 00000012670 13772472505 0026466 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025227 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/README.markdown 0000664 0000000 0000000 00000000470 13772472505 0027731 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000121 13772472505 0031013 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/dropbox/main.go 0000664 0000000 0000000 00000013232 13772472505 0026503 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025724 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/README.markdown 0000664 0000000 0000000 00000000730 13772472505 0030425 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000127 13772472505 0031516 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/quickbooks/main.go 0000664 0000000 0000000 00000014620 13772472505 0027202 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025235 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/session/session.go 0000664 0000000 0000000 00000003374 13772472505 0027256 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025236 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/README.markdown 0000664 0000000 0000000 00000000673 13772472505 0027745 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000127 13772472505 0031030 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/smugmug/main.go 0000664 0000000 0000000 00000014617 13772472505 0026522 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025254 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/README.markdown 0000664 0000000 0000000 00000001002 13772472505 0027746 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000131 13772472505 0031041 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitter/main.go 0000664 0000000 0000000 00000022012 13772472505 0026524 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0025754 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/README.markdown 0000664 0000000 0000000 00000000752 13772472505 0030461 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000131 13772472505 0031541 0 ustar 00root root 0000000 0000000 {
"Token":"",
"Secret":""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/twitteroob/main.go 0000664 0000000 0000000 00000003752 13772472505 0027236 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 13772472505 0024523 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/README.markdown 0000664 0000000 0000000 00000000534 13772472505 0027226 0 ustar 00root root 0000000 0000000 This 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.example 0000664 0000000 0000000 00000000171 13772472505 0030314 0 ustar 00root root 0000000 0000000 {
"ConsumerKey": "",
"ConsumerSecret": "",
"Token": "",
"TokenSecret": ""
}
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/examples/yelp/main.go 0000664 0000000 0000000 00000004364 13772472505 0026005 0 ustar 00root root 0000000 0000000 // 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.mod 0000664 0000000 0000000 00000000053 13772472505 0023040 0 ustar 00root root 0000000 0000000 module github.com/gomodule/oauth1
go 1.13
golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/ 0000775 0000000 0000000 00000000000 13772472505 0023054 5 ustar 00root root 0000000 0000000 golang-github-gomodule-oauth1-0.0~git20181215.9a59ed3/oauth/examples_test.go 0000664 0000000 0000000 00000003277 13772472505 0026271 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000057336 13772472505 0024541 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000053166 13772472505 0025575 0 ustar 00root root 0000000 0000000 // 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")
}
}