forked from gpavlidi/go-hangups
-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
156 lines (131 loc) · 3.89 KB
/
session.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
package hangups
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
"golang.org/x/oauth2"
)
var deviceName = oauth2.SetAuthURLParam("device_name", "hangups")
// Session represent a hangouts session
type Session struct {
RefreshToken string
Cookies string
Sapisid string
}
var (
scopes = []string{
"https://www.google.com/accounts/OAuthLogin",
"https://www.googleapis.com/auth/userinfo.email",
} // only need scope for logging in
oauthEndpoint = oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/programmatic_auth", // interactive user login
TokenURL: "https://accounts.google.com/o/oauth2/token", // API endpoint to get access token from refresh token or auth code
}
)
// Init a session
func (s *Session) Init() error {
oauthConf := &oauth2.Config{
ClientID: "936475272427.apps.googleusercontent.com", // iOS id
ClientSecret: "KWsJlkaMn1jGLxQpWxMnOox-", // iOS secret
Scopes: scopes,
Endpoint: oauthEndpoint,
}
client, err := s.getOauthClient(oauthConf)
if err != nil {
return err
}
err = s.setCookies(client)
if err != nil {
return err
}
return nil
}
func (s *Session) setCookies(client *http.Client) error {
cookieJar, _ := cookiejar.New(nil)
client.Jar = cookieJar
resp, err := client.Get("https://accounts.google.com/accounts/OAuthLogin?source=hangups&issueuberauth=1")
if err != nil {
return err
}
defer resp.Body.Close()
uberauth, _ := ioutil.ReadAll(resp.Body)
mergeSessionURL := fmt.Sprintf("https://accounts.google.com/MergeSession?service=mail&continue=http://www.google.com&uberauth=%s", uberauth)
// url encode it
url, _ := url.Parse(mergeSessionURL)
q := url.Query()
url.RawQuery = q.Encode()
resp, err = client.Get(url.String())
if err != nil {
return err
}
defer resp.Body.Close()
u, _ := url.Parse("google.com")
requiredCookies := map[string]string{
"APISID": "",
"HSID": "",
"NID": "",
"SAPISID": "",
"SID": "",
"SIDCC": "",
"SSID": "",
}
cookies := make([]string, 0)
for _, cookie := range client.Jar.Cookies(u) {
_, found := requiredCookies[cookie.Name]
if found {
cookies = append(cookies, cookie.String())
}
if "SAPISID" == cookie.Name {
s.Sapisid = cookie.Value
}
}
s.Cookies = strings.Join(cookies, "; ")
return nil
}
func (s *Session) getOauthClient(oauthConf *oauth2.Config) (*http.Client, error) {
var oauthClient *http.Client
var token *oauth2.Token
var err error
if s.RefreshToken == "" {
token, err = tokenFromAuthCode(oauthConf)
} else {
token, err = tokenFromRefreshToken(oauthConf, s.RefreshToken)
}
if err != nil {
return nil, err
}
s.RefreshToken = token.RefreshToken
oauthClient = oauthConf.Client(context.TODO(), token)
return oauthClient, nil
}
func tokenFromRefreshToken(oauthConf *oauth2.Config, refreshToken string) (*oauth2.Token, error) {
// generate an expired token with the refreshToken and let TokenSource refresh it
expiredToken := &oauth2.Token{RefreshToken: refreshToken, Expiry: time.Now().Add(-1 * time.Hour)}
tokenSource := oauthConf.TokenSource(context.TODO(), expiredToken)
return tokenSource.Token()
}
func tokenFromAuthCode(oauthConf *oauth2.Config) (*oauth2.Token, error) {
// construct url and encode queries properly
authURL := removeResponseTypeFromAuthURL(oauthConf.AuthCodeURL("", deviceName))
// ask the user for the auth token
fmt.Println("Can't find Refresh Token. Please navigate to the below address and paste the code")
fmt.Println(authURL)
fmt.Print("Auth Code: ")
authCode := ""
fmt.Scanln(&authCode)
// got the auth_code. Exchange it with an access token
token, err := oauthConf.Exchange(context.TODO(), authCode)
return token, err
}
func removeResponseTypeFromAuthURL(uri string) string {
auth, _ := url.Parse(uri)
query := auth.Query()
query.Del("response_type")
auth.RawQuery = query.Encode()
return auth.String()
}