Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var ErrTokenInvalid = errors.New("token invalid or expired")
|
||||
|
||||
// IssueSetPasswordToken creates a signed, time-limited token (no DB row).
|
||||
// secret must come from env (TOKEN_SIGNING_SECRET); never commit secrets.
|
||||
func IssueSetPasswordToken(secret string, userID uuid.UUID, ttl time.Duration) (string, error) {
|
||||
if strings.TrimSpace(secret) == "" {
|
||||
return "", errors.New("token signing secret not configured")
|
||||
}
|
||||
if ttl <= 0 {
|
||||
ttl = 72 * time.Hour
|
||||
}
|
||||
exp := time.Now().Add(ttl).Unix()
|
||||
nonce, err := RandomToken(8)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payload := fmt.Sprintf("%s.%d.%s", userID.String(), exp, nonce)
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = mac.Write([]byte(payload))
|
||||
sig := hex.EncodeToString(mac.Sum(nil))
|
||||
raw := payload + "." + sig
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(raw)), nil
|
||||
}
|
||||
|
||||
func ParseSetPasswordToken(secret, token string) (uuid.UUID, error) {
|
||||
if strings.TrimSpace(secret) == "" || strings.TrimSpace(token) == "" {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
raw, err := base64.RawURLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
parts := strings.Split(string(raw), ".")
|
||||
if len(parts) != 4 {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
userID, err := uuid.Parse(parts[0])
|
||||
if err != nil {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
exp, err := strconv.ParseInt(parts[1], 10, 64)
|
||||
if err != nil || time.Now().Unix() > exp {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
payload := parts[0] + "." + parts[1] + "." + parts[2]
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = mac.Write([]byte(payload))
|
||||
expected := hex.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(expected), []byte(parts[3])) {
|
||||
return uuid.Nil, ErrTokenInvalid
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
Reference in New Issue
Block a user