eff5771b03
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
84 lines
2.2 KiB
Go
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")
|
|
}
|
|
}
|