Files
egutierrez eff5771b03 feat: jwt_generate, jwt_validate, password_hash, password_verify
Fase 2 del issue 0010 — auth core:
- jwt_generate/validate: HS256 manual con crypto/hmac + crypto/sha256
- password_hash/verify: wrappers de golang.org/x/crypto/bcrypt (cost 12 default)
- JWT rechaza alg != HS256 para mitigar ataque 'alg=none'
- hmac.Equal para comparacion constant-time de firmas
2026-04-18 17:39:00 +02:00

84 lines
2.2 KiB
Go

package infra
import (
"strings"
"testing"
"time"
)
func TestJWTValidate_ValidatesGeneratedToken(t *testing.T) {
claims := JWTClaims{
Subject: "user-42",
Issuer: "tester",
ExpiresAt: time.Now().Add(time.Hour).Unix(),
Custom: map[string]any{"role": "admin"},
}
token, err := JWTGenerate(claims, "super-secret")
if err != nil {
t.Fatalf("JWTGenerate error: %v", err)
}
got, err := JWTValidate(token, "super-secret")
if err != nil {
t.Fatalf("JWTValidate error: %v", err)
}
if got.Subject != "user-42" {
t.Errorf("Subject = %q, esperado user-42", got.Subject)
}
if got.Issuer != "tester" {
t.Errorf("Issuer = %q, esperado tester", got.Issuer)
}
if got.Custom["role"] != "admin" {
t.Errorf("Custom[role] = %v, esperado admin", got.Custom["role"])
}
}
func TestJWTValidate_RejectsInvalidSignature(t *testing.T) {
token, err := JWTGenerate(JWTClaims{Subject: "x", ExpiresAt: time.Now().Add(time.Hour).Unix()}, "secret-a")
if err != nil {
t.Fatalf("JWTGenerate error: %v", err)
}
if _, err := JWTValidate(token, "secret-b"); err == nil {
t.Fatal("esperaba error con secret distinto")
}
}
func TestJWTValidate_RejectsExpiredToken(t *testing.T) {
token, err := JWTGenerate(JWTClaims{
Subject: "x",
ExpiresAt: time.Now().Add(-1 * time.Hour).Unix(),
}, "s")
if err != nil {
t.Fatalf("JWTGenerate error: %v", err)
}
if _, err := JWTValidate(token, "s"); err == nil {
t.Fatal("esperaba error con token expirado")
}
}
func TestJWTValidate_RejectsMalformedToken(t *testing.T) {
cases := []string{
"",
"not-a-token",
"one.two",
"one.two.three.four",
}
for _, tok := range cases {
if _, err := JWTValidate(tok, "s"); err == nil {
t.Errorf("esperaba error con token %q", tok)
}
}
}
func TestJWTValidate_RejectsOtherAlgorithms(t *testing.T) {
// Token con alg=none no es aceptado aunque la firma sea vacia
// Construimos manualmente: header con alg=none
// "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0" = {"alg":"none","typ":"JWT"}
token := "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJ4In0."
if _, err := JWTValidate(token, "s"); err == nil {
t.Fatal("esperaba error con alg=none")
}
if !strings.Contains(token, ".") {
t.Fatal("token debe tener puntos")
}
}