JWT (JSON Web Token) es un estándar abierto (RFC 7519) que permite transmitir información de manera segura entre un cliente y un servidor como un objeto JSON firmado digitalmente.
Se usa principalmente para autenticación y autorización, reemplazando las sesiones tradicionales.
Un JWT tiene tres partes separadas por puntos:
HEADER.PAYLOAD.SIGNATURE
Contiene metadatos sobre el algoritmo de firma y el tipo de token.
{
"alg": "HS256",
"typ": "JWT"
}Contiene la información (claims) que se desea transmitir.
{
"sub": 1,
"email": "user@example.com",
"role": "admin",
"exp": 1730860800
}Garantiza que el token no ha sido modificado.
Se genera así:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
La secret_key es la clave privada usada para firmar el token, y debe almacenarse en un archivo .env, nunca en el código.
JWT_SECRET=my_super_secret_key_123
JWT_EXPIRES_IN=1h🧠 Importante:
- Nunca publiques tu
.enven GitHub.- Agrégalo a tu
.gitignore.- Cambia el secreto si sospechas que fue expuesto.
pnpm add jsonwebtoken dotenvimport express from "express";
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
dotenv.config();
const app = express();
app.use(express.json());
app.post("/login", (req, res) => {
const user = { id: 1, email: "user@example.com" };
const token = jwt.sign(user, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
});
res.json({ token });
});
app.get("/profile", (req, res) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (!token) return res.status(401).json({ message: "No token provided" });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
res.json({ user: decoded });
} catch {
res.status(403).json({ message: "Invalid token" });
}
});
app.listen(3000, () => console.log("Server running on port 3000"));NestJS integra JWT a través de módulos oficiales:
pnpm add @nestjs/jwt @nestjs/passport passport-jwtimport { Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { PassportModule } from "@nestjs/passport";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { AuthService } from "./auth.service";
import { AuthController } from "./auth.controller";
import { JwtStrategy } from "./strategies/jwt.strategy";
@Module({
imports: [
ConfigModule.forRoot(),
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get("JWT_SECRET"),
signOptions: { expiresIn: configService.get("JWT_EXPIRES_IN") },
}),
inject: [ConfigService],
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
})
export class AuthModule {}import { Injectable } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
login(user: any) {
const payload = { sub: user.id, email: user.email };
return {
access_token: this.jwtService.sign(payload),
};
}
verifyToken(token: string) {
return this.jwtService.verify(token);
}
}import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get("JWT_SECRET"),
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}| Concepto | Express.js | NestJS |
|---|---|---|
| Configuración | Manual (todo en el mismo archivo o rutas) | Modular y escalable (AuthModule, Strategy, Guards) |
| Seguridad | Depende del middleware | Usa PassportJS y Guards integrados |
| Inyección de dependencias | Manual | Automática con @Injectable() |
| Escalabilidad | Limitada sin estructura extra | Alta, gracias al diseño modular |
| Buenas prácticas | A elección del desarrollador | Estandarizadas por el framework |
- JWT permite autenticación sin sesiones, ideal para APIs RESTful.
- El secreto (
JWT_SECRET) es esencial y debe manejarse de forma segura. - NestJS facilita la implementación mediante módulos, estrategias y guards.
- En Express debes implementar la lógica manualmente, lo cual te da flexibilidad pero menos estructura.
🧠 Tip final:
Si estás construyendo un backend grande o de largo plazo, usa NestJS con Passport y JWT: tendrás una base sólida, mantenible y lista para producción.