--- name: jwt_validate kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func JWTValidate(token string, secret string) (JWTClaims, error)" description: "Verifica la firma HMAC-SHA256 de un JWT y decodifica sus claims. Rechaza tokens mal formados, con firma invalida o expirados." tags: [jwt, auth, token, hmac, verify, infra] uses_functions: [] uses_types: [JWTClaims_go_infra] returns: [JWTClaims_go_infra] returns_optional: false error_type: error_go_core imports: [crypto/hmac, crypto/sha256, encoding/base64, encoding/json, errors, strings, time] params: - name: token desc: "JWT string en formato header.payload.signature (base64url, sin padding)" - name: secret desc: "clave HMAC usada para firmar el token. Debe coincidir con la usada en JWTGenerate" output: "claims decodificadas si el token es valido; error si firma invalida, expirado o malformado" tested: true tests: ["valida token generado por JWTGenerate", "rechaza firma invalida", "rechaza token expirado", "rechaza token malformado", "rechaza algoritmo distinto de HS256"] test_file_path: "functions/infra/jwt_validate_test.go" file_path: "functions/infra/jwt_validate.go" --- ## Ejemplo ```go auth := r.Header.Get("Authorization") token := strings.TrimPrefix(auth, "Bearer ") claims, err := JWTValidate(token, os.Getenv("JWT_SECRET")) if err != nil { HTTPErrorResponse(w, HTTPError{Status: 401, Code: "invalid_token", Message: err.Error()}) return } userID := claims.Subject role, _ := claims.Custom["role"].(string) ``` ## Notas Impura — usa `time.Now()` para comparar contra `exp`. Usa `hmac.Equal` para comparacion constant-time de firmas (mitiga timing attacks). Solo acepta alg=HS256 en el header, otros algoritmos se rechazan explicitamente para evitar el ataque "alg=none". Si `exp` es 0 (no fijado) no se valida expiracion — es responsabilidad del caller asegurar que sus tokens siempre tengan exp fijado. Errores descriptivos con prefijo `jwt_validate:` para facilitar debugging; en respuestas HTTP conviene mapear todos a un mensaje generico "token invalido" para no filtrar informacion.