mirror of
https://github.com/ThisTine/Snip.git
synced 2026-08-18 23:18:47 +07:00
feat: first commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
// Package security provides the password hasher and stateless session manager.
|
||||
package security
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// BcryptHasher implements port.PasswordHasher.
|
||||
type BcryptHasher struct{ cost int }
|
||||
|
||||
func NewBcryptHasher() *BcryptHasher { return &BcryptHasher{cost: bcrypt.DefaultCost} }
|
||||
|
||||
func (h *BcryptHasher) Hash(plain string) (string, error) {
|
||||
b, err := bcrypt.GenerateFromPassword([]byte(plain), h.cost)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (h *BcryptHasher) Compare(hash, plain string) bool {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)) == nil
|
||||
}
|
||||
|
||||
// HMACSessions implements port.SessionManager with signed, stateless tokens of
|
||||
// the form base64(userID|expiryUnix).base64(hmacSHA256). No server-side store.
|
||||
type HMACSessions struct{ key []byte }
|
||||
|
||||
func NewHMACSessions(secret string) *HMACSessions {
|
||||
return &HMACSessions{key: []byte(secret)}
|
||||
}
|
||||
|
||||
func (s *HMACSessions) Issue(userID string, ttl time.Duration) (string, error) {
|
||||
payload := userID + "|" + strconv.FormatInt(time.Now().Add(ttl).Unix(), 10)
|
||||
p := base64.RawURLEncoding.EncodeToString([]byte(payload))
|
||||
return p + "." + s.sign(p), nil
|
||||
}
|
||||
|
||||
func (s *HMACSessions) Verify(token string) (string, error) {
|
||||
p, sig, ok := strings.Cut(token, ".")
|
||||
if !ok {
|
||||
return "", errors.New("malformed token")
|
||||
}
|
||||
if !hmac.Equal([]byte(sig), []byte(s.sign(p))) {
|
||||
return "", errors.New("bad signature")
|
||||
}
|
||||
raw, err := base64.RawURLEncoding.DecodeString(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
userID, expStr, ok := strings.Cut(string(raw), "|")
|
||||
if !ok {
|
||||
return "", errors.New("malformed payload")
|
||||
}
|
||||
exp, err := strconv.ParseInt(expStr, 10, 64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if time.Now().Unix() > exp {
|
||||
return "", errors.New("session expired")
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (s *HMACSessions) sign(payload string) string {
|
||||
mac := hmac.New(sha256.New, s.key)
|
||||
mac.Write([]byte(payload))
|
||||
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
Reference in New Issue
Block a user