Initial project snapshot

This commit is contained in:
2026-04-28 22:29:50 +03:00
commit 8ba0561f4f
365 changed files with 91832 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
package auth
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
)
type TokenManager struct {
issuer string
accessSecret []byte
refreshHashSecret []byte
accessTTL time.Duration
refreshTTL time.Duration
}
type AccessClaims struct {
AuthSessionID string `json:"sid"`
DeviceID string `json:"did"`
jwt.RegisteredClaims
}
func NewTokenManager(cfg TokenConfig) *TokenManager {
return &TokenManager{
issuer: cfg.Issuer,
accessSecret: []byte(cfg.AccessTokenSecret),
refreshHashSecret: []byte(cfg.RefreshHashSecret),
accessTTL: cfg.AccessTokenTTL,
refreshTTL: cfg.RefreshTokenTTL,
}
}
type TokenConfig struct {
Issuer string
AccessTokenSecret string
RefreshHashSecret string
AccessTokenTTL time.Duration
RefreshTokenTTL time.Duration
}
func (m *TokenManager) IssueAccessToken(userID, authSessionID, deviceID string, now time.Time) (string, time.Time, error) {
expiresAt := now.Add(m.accessTTL)
claims := AccessClaims{
AuthSessionID: authSessionID,
DeviceID: deviceID,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: m.issuer,
Subject: userID,
ExpiresAt: jwt.NewNumericDate(expiresAt),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signed, err := token.SignedString(m.accessSecret)
if err != nil {
return "", time.Time{}, fmt.Errorf("sign access token: %w", err)
}
return signed, expiresAt, nil
}
func (m *TokenManager) IssueRefreshToken(authSessionID string, now time.Time) (raw string, hash string, expiresAt time.Time, err error) {
secret := make([]byte, 32)
if _, err = rand.Read(secret); err != nil {
return "", "", time.Time{}, fmt.Errorf("read random refresh secret: %w", err)
}
encodedSecret := base64.RawURLEncoding.EncodeToString(secret)
raw = authSessionID + "." + encodedSecret
hash = m.HashRefreshToken(raw)
expiresAt = now.Add(m.refreshTTL)
return raw, hash, expiresAt, nil
}
func (m *TokenManager) HashRefreshToken(token string) string {
mac := hmac.New(sha256.New, m.refreshHashSecret)
_, _ = mac.Write([]byte(token))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
func (m *TokenManager) ParseRefreshToken(token string) (string, error) {
sessionID, _, ok := strings.Cut(token, ".")
if !ok || sessionID == "" {
return "", ErrInvalidRefreshToken
}
return sessionID, nil
}