Tus datos en la mira: por qué la autenticación en TypeScript no es una broma
\n\nImagina: creaste una aplicación increíble en TypeScript. Vuela, los tipos son estrictos, el código es hermoso. Pero hay un pequeño detalle: la autenticación. Y aquí empieza lo interesante. Un error y los datos de los usuarios se escapan como agua entre los dedos. ¿Te suena?
\n\nEn 2024, la autenticación segura no es solo "poner un inicio de sesión". Es toda una ciencia. Y TypeScript aquí es tu mejor amigo. No te dejará pasar accidentalmente un tipo de dato incorrecto, te indicará dónde olvidaste verificar el token y te salvará de errores tontos durante la compilación.
\n\nEn este artículo, analizaremos cómo construir una autenticación segura en aplicaciones TypeScript desde cero. Sin rodeos: solo ejemplos reales, mejores prácticas y un poco de humor negro (no puede faltar).
\n\nJWT: amor a primera vista con el token
\n\nJSON Web Token (JWT) es el estándar de facto para la autenticación en aplicaciones web modernas. Pero, como decía el tío Ben, "un gran poder conlleva una gran responsabilidad". JWT puede ser tanto un escudo como una bomba de tiempo.
\n\nCómo NO hacerlo: anti-ejemplo
\n\nEl pecado más común de los principiantes es almacenar el token en localStorage y transmitirlo abiertamente. Sí, funciona. Pero es como dejar las llaves del departamento debajo del felpudo: es cómodo, pero cualquier transeúnte puede entrar. Un ataque XSS y todos tus tokens están en manos del atacante.
\n\nTrabajo correcto con JWT en TypeScript
\n\nEscribamos un envoltorio seguro para trabajar con tokens. TypeScript nos ayudará a no confundir los tipos y a no olvidar la validación.
\n\nimport jwt from 'jsonwebtoken';\n\ninterface TokenPayload {\n userId: string;\n role: 'user' | 'admin';\n iat?: number;\n exp?: number;\n}\n\n// Generamos el token con tipado claro\nfunction generateAccessToken(payload: TokenPayload): string {\n return jwt.sign(\n { ...payload },\n process.env.JWT_SECRET as string,\n { expiresIn: '15m' }\n );\n}\n\n// Verificamos y casteamos inmediatamente a nuestra interfaz\nfunction verifyAccessToken(token: string): TokenPayload | null {\n try {\n return jwt.verify(\n token, \n process.env.JWT_SECRET as string\n ) as TokenPayload;\n } catch (error) {\n console.error('El token no pasó la verificación:', error);\n return null;\n }\n}\n\nObserva: siempre verificamos que JWT_SECRET exista y sea una cadena. TypeScript nos obliga a hacerlo: ¡nada de undefined en tiempo de ejecución!
Hash de contraseñas: bcrypt al rescate
\n\nAlmacenar contraseñas en texto plano es un delito. Incluso si tu base de datos está mejor protegida que Fort Knox, un solo ataque y aparecerás en las noticias como otro "filtrado de contraseñas". El hash de contraseñas es tu salvavidas.
\n\n¿Por qué bcrypt y no MD5?
\n\nMD5 está muerto. SHA-256 tampoco es una opción. Las GPU modernas pueden procesar miles de millones de hashes por segundo. Bcrypt, en cambio, está diseñado específicamente para ser lento. Usa sal (datos aleatorios) y un factor de costo ajustable. Cuanto mayor es el costo, más tiempo se tarda en calcular el hash. ¡Y eso es bueno!
\n\nImplementación en TypeScript
\n\nimport bcrypt from 'bcrypt';\n\nconst SALT_ROUNDS = 12; // Valor óptimo en 2024\n\nasync function hashPassword(password: string): Promise<string> {\n const salt = await bcrypt.genSalt(SALT_ROUNDS);\n return bcrypt.hash(password, salt);\n}\n\nasync function comparePassword(\n password: string, \n hash: string\n): Promise<boolean> {\n return bcrypt.compare(password, hash);\n}\n\n// Función tipada para el registro\ninterface CreateUserInput {\n email: string;\n password: string;\n name: string;\n}\n\nasync function createUser(input: CreateUserInput): Promise<Omit<User, 'passwordHash'>> {\n const passwordHash = await hashPassword(input.password);\n \n // Guardamos en la BD (aquí va tu c