package auth import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "strconv" "testing" "time" "github.com/google/uuid" ) func TestIssueAndParseSetPasswordToken(t *testing.T) { t.Parallel() const secret = "test-signing-secret-not-for-prod" uid := uuid.MustParse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") token, err := IssueSetPasswordToken(secret, uid, time.Hour) if err != nil { t.Fatalf("IssueSetPasswordToken: %v", err) } got, err := ParseSetPasswordToken(secret, token) if err != nil { t.Fatalf("ParseSetPasswordToken: %v", err) } if got != uid { t.Fatalf("user id = %s, want %s", got, uid) } } func TestParseSetPasswordTokenRejectsWrongSecret(t *testing.T) { t.Parallel() uid := uuid.New() token, err := IssueSetPasswordToken("secret-a", uid, time.Hour) if err != nil { t.Fatalf("IssueSetPasswordToken: %v", err) } if _, err := ParseSetPasswordToken("secret-b", token); err == nil { t.Fatal("expected invalid token for wrong secret") } } func TestParseSetPasswordTokenRejectsExpired(t *testing.T) { t.Parallel() uid := uuid.New() const secret = "secret" // Build an already-expired signed token (IssueSetPasswordToken coerces ttl<=0 to 72h). exp := time.Now().Add(-time.Hour).Unix() nonce := "deadbeefdeadbeef" payload := uid.String() + "." + strconv.FormatInt(exp, 10) + "." + nonce mac := hmac.New(sha256.New, []byte(secret)) _, _ = mac.Write([]byte(payload)) sig := hex.EncodeToString(mac.Sum(nil)) token := base64.RawURLEncoding.EncodeToString([]byte(payload + "." + sig)) if _, err := ParseSetPasswordToken(secret, token); err == nil { t.Fatal("expected expired token to fail") } } func TestIssueSetPasswordTokenRequiresSecret(t *testing.T) { t.Parallel() if _, err := IssueSetPasswordToken("", uuid.New(), time.Hour); err == nil { t.Fatal("expected error when secret is empty") } }