🏗️ Login package refactor
This commit is contained in:
parent
90a7dec4be
commit
29c6ef9fd6
|
@ -4,34 +4,40 @@ import (
|
|||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var ErrUserNotDefined = errors.New("user is not defined")
|
||||
|
||||
// CheckAuthorization 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 (user *User) CheckAuthorization(botToken string) (bool, error) {
|
||||
dataCheckString := fmt.Sprint(
|
||||
"auth_date=", user.AuthDate.Unix(),
|
||||
"\n", "first_name=", user.FirstName,
|
||||
// Eliminate 'hash' to avoid recursion and incorrect data validation.
|
||||
"\n", "id=", user.ID,
|
||||
)
|
||||
func (app *App) CheckAuthorization(user *User) (bool, error) {
|
||||
if user == nil {
|
||||
return false, ErrUserNotDefined
|
||||
}
|
||||
|
||||
dataCheck := make(url.Values)
|
||||
dataCheck.Add(KeyAuthDate, string(user.AuthDate))
|
||||
dataCheck.Add(KeyFirstName, user.FirstName)
|
||||
dataCheck.Add(KeyID, strconv.Itoa(user.ID))
|
||||
|
||||
// Add optional values if exist
|
||||
if user.LastName != "" {
|
||||
dataCheckString += fmt.Sprint("\n", "last_name=", user.LastName)
|
||||
dataCheck.Add(KeyLastName, user.LastName)
|
||||
}
|
||||
if user.PhotoURL != "" {
|
||||
dataCheckString += fmt.Sprint("\n", "photo_url=", user.PhotoURL)
|
||||
dataCheck.Add(KeyPhotoURL, user.PhotoURL)
|
||||
}
|
||||
if user.Username != "" {
|
||||
dataCheckString += fmt.Sprint("\n", "username=", user.Username)
|
||||
dataCheck.Add(KeyUsername, user.Username)
|
||||
}
|
||||
|
||||
secretKey := sha256.Sum256([]byte(botToken))
|
||||
secretKey := sha256.Sum256([]byte(app.SecretKey))
|
||||
hash := hmac.New(sha256.New, secretKey[0:])
|
||||
_, err := hash.Write([]byte(dataCheckString))
|
||||
_, err := hash.Write([]byte(dataCheck.Encode()))
|
||||
return hex.EncodeToString(hash.Sum(nil)) == user.Hash, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package login
|
||||
|
||||
const (
|
||||
KeyAuthDate = "auth_date"
|
||||
KeyFirstName = "first_name"
|
||||
KeyHash = "hash"
|
||||
KeyID = "id"
|
||||
KeyLastName = "last_name"
|
||||
KeyPhotoURL = "photo_url"
|
||||
KeyUsername = "username"
|
||||
)
|
42
login/new.go
42
login/new.go
|
@ -1,41 +1,11 @@
|
|||
package login
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// User contains data about authenticated user.
|
||||
type User struct {
|
||||
AuthDate time.Time `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"`
|
||||
// App represents a widget which get and validate users authorizations.
|
||||
type App struct {
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
// New create User structure from input url.Values.
|
||||
func New(src url.Values) (*User, error) {
|
||||
authDate, err := strconv.Atoi(src.Get("auth_date"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(src.Get("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &User{
|
||||
AuthDate: time.Unix(int64(authDate), 0),
|
||||
FirstName: src.Get("first_name"),
|
||||
Hash: src.Get("hash"),
|
||||
ID: id,
|
||||
LastName: src.Get("last_name"),
|
||||
PhotoURL: src.Get("photo_url"),
|
||||
Username: src.Get("username"),
|
||||
}, nil
|
||||
// New create new app widget for validate authorizations with bot token as secret key.
|
||||
func New(botToken string) *App {
|
||||
return &App{SecretKey: botToken}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package login
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// User contains data about authenticated user.
|
||||
type 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"`
|
||||
}
|
||||
|
||||
// ParseUser create User structure from input url.Values.
|
||||
func ParseUser(src url.Values) (*User, error) {
|
||||
authDate, err := strconv.Atoi(src.Get(KeyAuthDate))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(src.Get(KeyID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &User{
|
||||
AuthDate: int64(authDate),
|
||||
FirstName: src.Get(KeyFirstName),
|
||||
Hash: src.Get(KeyHash),
|
||||
ID: id,
|
||||
LastName: src.Get(KeyLastName),
|
||||
PhotoURL: src.Get(KeyPhotoURL),
|
||||
Username: src.Get(KeyUsername),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package login
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FullName return user first name only or full name if last name is present.
|
||||
func (user *User) FullName() string {
|
||||
if user == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if user.LastName != "" {
|
||||
return fmt.Sprintln(user.FirstName, user.LastName)
|
||||
}
|
||||
|
||||
return user.FirstName
|
||||
}
|
||||
|
||||
// AuthTime convert AuthDate field into time.Time.
|
||||
func (user *User) AuthTime() time.Time {
|
||||
if user == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
return time.Unix(user.AuthDate, 0)
|
||||
}
|
Loading…
Reference in New Issue