-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoauth.go
159 lines (146 loc) · 3.93 KB
/
oauth.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
package trakt
import (
"bytes"
"context"
"encoding/json"
"net/http"
"path"
"github.com/pkg/errors"
)
const (
// nolint: gosec
oauthTokenPath = "/oauth/token"
deviceCodePath = "/oauth/device/code"
// nolint: gosec
deviceTokenPath = "/oauth/device/token"
)
var _ Authentication = (*Client)(nil)
type Authentication interface {
DeviceCode(context.Context) (*DeviceCodeResult, error)
DeviceToken(context.Context, string) (*AuthResult, error)
RefreshToken(context.Context, string) (*AuthResult, error)
}
type DeviceCodeBody struct {
ClientID string `json:"client_id"`
}
type DeviceCodeResult struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURL string `json:"verification_url"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
}
func (c *Client) DeviceCode(ctx context.Context) (*DeviceCodeResult, error) {
postBody, err := json.Marshal(DeviceCodeBody{ClientID: c.ClientID})
if err != nil {
return nil, err
}
uri := *c.BaseURL
uri.Path = path.Join(uri.Path, deviceCodePath)
req, err := http.NewRequest(http.MethodPost, uri.String(), bytes.NewReader(postBody))
if err != nil {
return nil, err
}
c.SetHeaders(req)
req = req.WithContext(ctx)
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.Errorf("error getting device code: %d", resp.StatusCode)
}
result := &DeviceCodeResult{}
err = json.NewDecoder(resp.Body).Decode(result)
if err != nil {
return nil, err
}
return result, nil
}
type AuthBody struct {
Code string `json:"code"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}
type AuthResult struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
Scope string `json:"scope"`
CreatedAt int `json:"created_at"`
}
func (c *Client) DeviceToken(ctx context.Context, code string) (*AuthResult, error) {
postBody, err := json.Marshal(AuthBody{
Code: code,
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
})
if err != nil {
return nil, err
}
uri := *c.BaseURL
uri.Path = path.Join(uri.Path, deviceTokenPath)
req, err := http.NewRequest(http.MethodPost, uri.String(), bytes.NewReader(postBody))
if err != nil {
return nil, err
}
c.SetHeaders(req)
req = req.WithContext(ctx)
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.Errorf("error getting device code: %d", resp.StatusCode)
}
result := &AuthResult{}
err = json.NewDecoder(resp.Body).Decode(result)
if err != nil {
return nil, err
}
return result, nil
}
type RefreshBody struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RefreshToken string `json:"refresh_token"`
RedirectURI string `json:"redirect_uri"`
GrantType string `json:"grant_type"`
}
func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*AuthResult, error) {
postBody, err := json.Marshal(RefreshBody{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
RefreshToken: refreshToken,
RedirectURI: "urn:ietf:wg:oauth:2.0:oob",
GrantType: "refresh_token",
})
if err != nil {
return nil, err
}
uri := *c.BaseURL
uri.Path = path.Join(uri.Path, oauthTokenPath)
req, err := http.NewRequest(http.MethodPost, uri.String(), bytes.NewReader(postBody))
if err != nil {
return nil, err
}
c.SetHeaders(req)
req = req.WithContext(ctx)
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, errors.Errorf("error refreshing token: %d", resp.StatusCode)
}
result := &AuthResult{}
err = json.NewDecoder(resp.Body).Decode(result)
if err != nil {
return nil, err
}
return result, nil
}