Files
backend/auth.go
T
NanamiAdmin af5af2a04f fix(auth): skip timestamp validation in debug mode
Modify ValidateTimeStamp to bypass validation when in debug mode. Also update request validation to make timestamp optional in debug mode while maintaining security checks in production.
2026-02-27 23:13:15 +08:00

182 lines
3.5 KiB
Go

package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strings"
"sync"
"time"
)
type TokenInfo struct {
Token string
CreatedAt time.Time
UserID int
}
var (
tokenMap = make(map[int]*TokenInfo)
tokenMux sync.RWMutex
tokenTTL = time.Hour
)
func GenerateToken(userID int) (string, error) {
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
token := base64.URLEncoding.EncodeToString(hash[:])
tokenMux.Lock()
defer tokenMux.Unlock()
tokenMap[userID] = &TokenInfo{
Token: token,
CreatedAt: time.Now(),
UserID: userID,
}
return token, nil
}
func ValidateToken(userID int, token string) error {
tokenMux.RLock()
defer tokenMux.RUnlock()
tokenInfo, exists := tokenMap[userID]
if !exists {
return errors.New("token not found")
}
if tokenInfo.Token != token {
return errors.New("invalid token")
}
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
return errors.New("token expired")
}
return nil
}
func RefreshToken(userID int) (string, error) {
tokenMux.Lock()
defer tokenMux.Unlock()
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
token := base64.URLEncoding.EncodeToString(hash[:])
tokenMap[userID] = &TokenInfo{
Token: token,
CreatedAt: time.Now(),
UserID: userID,
}
return token, nil
}
func RemoveToken(userID int) {
tokenMux.Lock()
defer tokenMux.Unlock()
delete(tokenMap, userID)
}
func GetTokenInfo(userID int) (*TokenInfo, error) {
tokenMux.RLock()
defer tokenMux.RUnlock()
tokenInfo, exists := tokenMap[userID]
if !exists {
return nil, errors.New("token not found")
}
return tokenInfo, nil
}
func extractUserIDFromToken(token string) (int, error) {
tokenMux.RLock()
defer tokenMux.RUnlock()
for userID, tokenInfo := range tokenMap {
if tokenInfo.Token == token {
return userID, nil
}
}
return 0, errors.New("invalid token")
}
func CleanupExpiredTokens() {
tokenMux.Lock()
defer tokenMux.Unlock()
for userID, tokenInfo := range tokenMap {
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
delete(tokenMap, userID)
}
}
}
func hashPassword(password string) (string, error) {
hash := sha256.Sum256([]byte(password))
return hex.EncodeToString(hash[:]), nil
}
func verifyPassword(password, hashedPassword string) bool {
hash, err := hashPassword(password)
if err != nil {
return false
}
return hash == hashedPassword
}
func isValidPassword(password string) bool {
if len(password) < 8 {
return false
}
hasUpper := false
hasLower := false
hasDigit := false
hasSpecial := false
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
for _, char := range password {
switch {
case char >= 'A' && char <= 'Z':
hasUpper = true
case char >= 'a' && char <= 'z':
hasLower = true
case char >= '0' && char <= '9':
hasDigit = true
case strings.ContainsRune(specialChars, char):
hasSpecial = true
}
}
return hasUpper && hasLower && hasDigit && hasSpecial
}
func ValidateTimeStamp(timeStamp int64) error {
if globalConfig.Debug {
return nil
}
currentTime := time.Now().UnixMilli()
if currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000 {
return errors.New("timestamp out of valid range")
}
return nil
}