forked from dghubble/oauth1
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.go
166 lines (155 loc) · 5.55 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package oauth1
import (
"errors"
"io/ioutil"
"net/http"
"net/url"
)
const (
OAUTH_TOKEN_SECRET = "oauth_token_secret"
OAUTH_CALLBACK_CONFIRMED = "oauth_callback_confirmed"
)
// Config represents an OAuth1 consumer's (client's) credentials, callback URL,
// and the provider to which the consumer corresponds.
type Config struct {
// Consumer Key (Client Identifier)
ConsumerKey string
// Consumer Secret (Client Shared-Secret)
ConsumerSecret string
// Callback URL
CallbackURL string
// Provider Endpoint specifying OAuth1 endpoint URLs
Endpoint Endpoint
}
func NewConfig(consumerKey, consumerSecret string) *Config {
return &Config{
ConsumerKey: consumerKey,
ConsumerSecret: consumerSecret,
}
}
// Client returns an HTTP client which uses the provided Token.
func (c *Config) Client(t *Token) *http.Client {
return NewClient(c, t)
}
// Returns a new http Client which signs requests via OAuth1.
func NewClient(config *Config, token *Token) *http.Client {
transport := &Transport{
source: &ReuseTokenSource{token, nil},
signer: &Signer{config},
}
return &http.Client{Transport: transport}
}
// RequestToken represents OAuth1 temporary credentials as defined in RFC 5849
// 1.1 Terminology.
type RequestToken struct {
Token string
TokenSecret string
}
// GetRequestToken obtains a RequestToken (temporary credential) by POSTing a
// signed request (with oauth_callback in the auth header) to the Endpoint
// RequestTokenURL. The request is signed by the consumer secret and an empty
// token secret. The response body form is validated to ensure
// oauth_callback_confirmed is true. Returns a new RequestToken with the
// oauth_token and oauth_token_secret in the body.
// See RFC 5849 2.1 Temporary Credentials.
func (c *Config) GetRequestToken() (*RequestToken, error) {
req, err := http.NewRequest("POST", c.Endpoint.RequestTokenURL, nil)
if err != nil {
return nil, err
}
signer := &Signer{c}
signer.SetRequestTokenAuthHeader(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// ParseQuery to decode URL-encoded application/x-www-form-urlencoded body
values, err := url.ParseQuery(string(body))
if err != nil {
return nil, err
}
if values.Get(OAUTH_CALLBACK_CONFIRMED) != "true" {
return nil, errors.New("oauth_callback_confirmed was not true")
}
token := values.Get(OAUTH_TOKEN)
tokenSecret := values.Get(OAUTH_TOKEN_SECRET)
if token == "" || tokenSecret == "" {
return nil, errors.New("GetRequestToken response missing oauth token or secret")
}
return &RequestToken{Token: token, TokenSecret: tokenSecret}, nil
}
// AuthorizationURL accepts a consumer (client) RequestToken and returns a
// *url.URL the consumer should (re)direct the client (resource owner) to
// in order to authorize the consumer to act on his/her/its behalf. The url
// includes the required RequestToken.Token oauth_token as a query parameter.
// See RFC 5849 2.2 Resource Owner Authorization.
func (c *Config) AuthorizationURL(rt *RequestToken) (*url.URL, error) {
authorizationURL, err := url.Parse(c.Endpoint.AuthorizeURL)
if err != nil {
return nil, err
}
values := authorizationURL.Query()
values.Add(OAUTH_TOKEN, rt.Token)
authorizationURL.RawQuery = values.Encode()
return authorizationURL, nil
}
// HandleAuthorizationCallback handles an OAuth1 authorization callback GET
// http.Request from a provider server. Request query parameters oauth_token
// and oauth_verifier are parsed and returned. The oauth_token (temporary
// credential) identifies the RequestToken pair obtained from GetRequestToken
// previously.
// See RFC 2.2 Resource Owner Authorization.
func (c *Config) HandleAuthorizationCallback(req *http.Request) (tokenKey, verifier string, err error) {
// parse the raw query from the URL into req.Form
err = req.ParseForm()
if err != nil {
return "", "", err
}
tokenKey = req.Form.Get(OAUTH_TOKEN)
verifier = req.Form.Get(OAUTH_VERIFIER)
if tokenKey == "" || verifier == "" {
return "", "", errors.New("callback did not receive an oauth_token or oauth_verifier")
}
return tokenKey, verifier, nil
}
// GetAccessToken obtains an AccessToken (token credential) by POSTing a signed
// request (with oauth_token and oauth_verifier in the auth header) to the
// Endpoint AccessTokenURL. The request is signed by the consumer secret and
// request token secret pair. The access oauth_token and oauth_secret are
// read from the response body form to return an AccessToken.
// See RFC 2.3 Token Credentials.
func (c *Config) GetAccessToken(requestToken *RequestToken, verifier string) (*Token, error) {
req, err := http.NewRequest("POST", c.Endpoint.AccessTokenURL, nil)
if err != nil {
return nil, err
}
signer := &Signer{c}
signer.SetAccessTokenAuthHeader(req, requestToken, verifier)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
// when err is nil, resp contains a non-nil resp.Body which must be closed
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// ParseQuery to decode URL-encoded application/x-www-form-urlencoded body
values, err := url.ParseQuery(string(body))
if err != nil {
return nil, err
}
token := values.Get(OAUTH_TOKEN)
tokenSecret := values.Get(OAUTH_TOKEN_SECRET)
if token == "" || tokenSecret == "" {
return nil, errors.New("GetAccessToken response missing oauth token or secret")
}
return &Token{Token: token, TokenSecret: tokenSecret}, nil
}