forked from McKael/madon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
login.go
129 lines (106 loc) · 2.93 KB
/
login.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
/*
Copyright 2017-2018 Mikael Berthe
Licensed under the MIT license. Please see the LICENSE file is this directory.
*/
package madon
import (
"encoding/json"
"strings"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"github.com/pkg/errors"
"github.com/sendgrid/rest"
)
const oAuthRelPath = "/oauth/"
// UserToken represents a user token as returned by the Mastodon API
type UserToken struct {
AccessToken string `json:"access_token"`
CreatedAt int64 `json:"created_at"`
Scope string `json:"scope"`
TokenType string `json:"token_type"`
}
// LoginBasic does basic user authentication
func (mc *Client) LoginBasic(username, password string, scopes []string) error {
if mc == nil {
return ErrUninitializedClient
}
if username == "" {
return errors.New("missing username")
}
if password == "" {
return errors.New("missing password")
}
hdrs := make(map[string]string)
opts := make(map[string]string)
hdrs["User-Agent"] = "madon/" + MadonVersion
opts["grant_type"] = "password"
opts["client_id"] = mc.ID
opts["client_secret"] = mc.Secret
opts["username"] = username
opts["password"] = password
if len(scopes) > 0 {
opts["scope"] = strings.Join(scopes, " ")
}
req := rest.Request{
BaseURL: mc.InstanceURL + oAuthRelPath + "token",
Headers: hdrs,
QueryParams: opts,
Method: rest.Post,
}
r, err := restAPI(req)
if err != nil {
return err
}
var resp UserToken
err = json.Unmarshal([]byte(r.Body), &resp)
if err != nil {
return errors.Wrap(err, "cannot unmarshal server response")
}
mc.UserToken = &resp
return nil
}
// SetUserToken sets an existing user credentials
// No verification of the arguments is made.
func (mc *Client) SetUserToken(token, username, password string, scopes []string) error {
if mc == nil {
return ErrUninitializedClient
}
mc.UserToken = &UserToken{
AccessToken: token,
Scope: strings.Join(scopes, " "),
TokenType: "bearer",
}
return nil
}
// LoginOAuth2 handles OAuth2 authentication
// If code is empty, the URL to the server consent page will be returned;
// if not, the user token is set.
func (mc *Client) LoginOAuth2(code string, scopes []string) (string, error) {
if mc == nil {
return "", ErrUninitializedClient
}
conf := &oauth2.Config{
ClientID: mc.ID,
ClientSecret: mc.Secret,
Scopes: scopes,
Endpoint: oauth2.Endpoint{
AuthURL: mc.InstanceURL + oAuthRelPath + "authorize",
TokenURL: mc.InstanceURL + oAuthRelPath + "token",
},
RedirectURL: NoRedirect,
}
if code == "" {
// URL to consent page to ask for permission
// for the scopes specified above.
return conf.AuthCodeURL("state", oauth2.AccessTypeOffline), nil
}
// Return token
t, err := conf.Exchange(context.TODO(), code)
if err != nil {
return "", errors.Wrap(err, "cannot convert code into a token")
}
if t == nil || t.AccessToken == "" {
return "", errors.New("empty token")
}
return "", mc.SetUserToken(t.AccessToken, "", "", scopes)
}