This repository has been archived by the owner on May 1, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
login.go
123 lines (97 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
package login
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/url"
"strings"
http "github.com/valyala/fasthttp"
"golang.org/x/text/language"
)
type (
Config struct {
// ClientSecret is the bot token.
ClientSecret string
// RedirectURL is the URL to redirect users going through the login flow.
RedirectURL string
// RequestWriteAccess request the permission for bot to send messages to the user.
RequestWriteAccess bool
}
// User contains data about authenticated user.
User struct {
AuthDate int64 `json:"auth_date"`
FirstName string `json:"first_name"`
Hash string `json:"hash"`
ID int `json:"id"`
LastName string `json:"last_name,omitempty"`
PhotoURL string `json:"photo_url,omitempty"`
Username string `json:"username,omitempty"`
}
)
const Endpoint string = "https://oauth.telegram.org/auth"
// Key represents available and supported query arguments keys.
const (
KeyAuthDate string = "auth_date"
KeyFirstName string = "first_name"
KeyHash string = "hash"
KeyID string = "id"
KeyLastName string = "last_name"
KeyPhotoURL string = "photo_url"
KeyUsername string = "username"
)
// ClientID returns bot ID from it's ClientSecret token.
func (c Config) ClientID() string {
return strings.SplitN(c.ClientSecret, ":", 2)[0]
}
// AuthCodeURL returns a URL to Telegram login page that asks for permissions for the required scopes explicitly.
func (c *Config) AuthCodeURL(lang language.Tag) string {
origin, _ := url.Parse(c.RedirectURL)
u := http.AcquireURI()
defer http.ReleaseURI(u)
u.Update(Endpoint)
a := u.QueryArgs()
a.Set("bot_id", c.ClientID())
a.Set("origin", origin.Scheme+"://"+origin.Host)
a.Add("embed", "0")
if lang != language.Und {
a.Set("lang", lang.String())
}
if c.RequestWriteAccess {
a.Set("request_access", "write")
}
return u.String()
}
// Verify verify the authentication and the integrity of the data received by
// comparing the received hash parameter with the hexadecimal representation
// of the HMAC-SHA-256 signature of the data-check-string with the SHA256
// hash of the bot's token used as a secret key.
func (c *Config) Verify(u *User) bool {
if u == nil || u.Hash == "" {
return false
}
h, err := generateHash(c.ClientSecret, u)
return err == nil && u.Hash == h
}
func generateHash(token string, u *User) (string, error) {
a := http.AcquireArgs()
defer http.ReleaseArgs(a)
// WARN(toby3d): do not change order of this args, it must be alphabetical
a.SetUint(KeyAuthDate, int(u.AuthDate))
a.Set(KeyFirstName, u.FirstName)
a.SetUint(KeyID, u.ID)
if u.LastName != "" {
a.Set(KeyLastName, u.LastName)
}
if u.PhotoURL != "" {
a.Set(KeyPhotoURL, u.PhotoURL)
}
if u.Username != "" {
a.Set(KeyUsername, u.Username)
}
secretKey := sha256.Sum256([]byte(token))
h := hmac.New(sha256.New, secretKey[0:])
if _, err := h.Write(a.QueryString()); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}