-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathoauth_token.go
129 lines (107 loc) · 3 KB
/
oauth_token.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
package ejabberd
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/url"
"time"
)
// OAuthToken defines how to store ejabberd OAuth token and
// attributes.
type OAuthToken struct {
// Actual token value retrieved from server
AccessToken string `json:"access_token"`
Endpoint string `json:"endpoint"`
// Parameters associated with the token, stored for reference
JID string `json:"jid"`
Scope string `json:"scope"`
Expiration time.Time `json:"expiration"`
}
// Save writes ejabberd OAuth structure to file.
func (t OAuthToken) Save(file string) error {
b, err := json.Marshal(t)
if err != nil {
return err
}
return ioutil.WriteFile(file, b, 0640)
}
// ReadOAuthToken reads the content of JSon OAuth token file and
// return proper OAuthToken structure.
func ReadOAuthToken(file string) (OAuthToken, error) {
var t OAuthToken
data, err := ioutil.ReadFile(file)
if err != nil {
return t, err
}
err = json.Unmarshal(data, &t)
return t, err
}
//==============================================================================
//==============================================================================
// HTTP
func httpGetToken(c *http.Client, apiURL string, params url.Values) (OAuthToken, error) {
// Performs HTTP request
resp, err := c.PostForm(apiURL, params)
if err != nil {
return OAuthToken{}, err
}
defer resp.Body.Close()
// Endpoint not found
if resp.StatusCode == 404 {
return OAuthToken{}, errors.New("oauth endpoint not found (404)")
}
// Cannot read HTTP response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return OAuthToken{}, errors.New("cannot read HTTP response from server")
}
// Bad request
if resp.StatusCode == 400 {
return OAuthToken{}, parseTokenError(body)
}
// Success
return parseTokenResponse(body)
}
// tokenParams prepares HTTP form to retrieve token
func tokenParams(j jid, password, scope, ttl string) url.Values {
return url.Values{
"grant_type": {"password"},
// TODO It would be nice to have ejabberd password grant_type support client_id:
// "client_id": {clientID},
"scope": {scope},
"username": {j.bare()},
"password": {password},
"ttl": {ttl},
}
}
// ====
// Process ejabberd HTTP token response
func parseTokenError(body []byte) error {
type jsonError struct {
Error string `json:"error"`
Description string `json:"error_description"`
}
var e jsonError
if err := json.Unmarshal(body, &e); err != nil {
return errors.New("bad request")
}
return errors.New(e.Description)
}
func parseTokenResponse(body []byte) (OAuthToken, error) {
type jsonResp struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
Scope string `json:"scope"`
ExpiresIn int `json:"expires_in"`
}
var r jsonResp
if err := json.Unmarshal(body, &r); err != nil {
return OAuthToken{}, err
}
var t OAuthToken
t.AccessToken = r.AccessToken
t.Scope = r.Scope
t.Expiration = time.Now().Add(time.Duration(r.ExpiresIn) * time.Second)
return t, nil
}