---
name: jwt_generate
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func JWTGenerate(claims JWTClaims, secret string) (string, error)"
description: "Codifica y firma un JWT con HMAC-SHA256 (HS256). Retorna el token en formato header.payload.signature. Setea IssuedAt automaticamente si viene en cero."
tags: [jwt, auth, token, hmac, sign, infra]
uses_functions: []
uses_types: [JWTClaims_go_infra]
returns: []
returns_optional: false
error_type: error_go_core
imports: [crypto/hmac, crypto/sha256, encoding/base64, encoding/json, errors, time]
params:
  - name: claims
    desc: "claims del JWT (sub, iss, aud, exp, iat, custom). Si IssuedAt es 0 se rellena con time.Now()"
  - name: secret
    desc: "clave HMAC para firmar. No debe estar vacia. Obtenerla de env var o pass_get, nunca hardcoded"
output: "token JWT firmado en formato base64url header.payload.signature"
tested: true
tests: ["genera token valido con claims completas", "setea IssuedAt si viene en cero", "error si secret vacio"]
test_file_path: "functions/infra/jwt_generate_test.go"
file_path: "functions/infra/jwt_generate.go"
---

## Ejemplo

```go
claims := JWTClaims{
    Subject:   "user-123",
    ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
    Custom:    map[string]any{"role": "admin"},
}
token, err := JWTGenerate(claims, os.Getenv("JWT_SECRET"))
if err != nil {
    return err
}
w.Header().Set("Authorization", "Bearer " + token)
```

## Notas

Impura — usa `time.Now()` para el claim `iat` cuando no viene fijado. Implementa HS256 sin libreria externa (solo stdlib crypto/hmac + crypto/sha256). Solo soporta HS256: para RS256/ES256 se crearia una funcion separada. El secret debe tener al menos 256 bits de entropia (32+ bytes aleatorios) para resistencia real. NO apto para escenarios multi-servicio donde se necesita clave publica/privada — usa RS256 en ese caso.
