Seus dados na mira: por que a autenticação em TypeScript não é brincadeira
\n\nImagine: você criou um aplicativo incrível em TypeScript. Ele voa, os tipos são rigorosos, o código é bonito. Mas há um pequeno detalhe — a autenticação. E é aí que a coisa fica interessante. Um erro — e os dados dos usuários vazam como água entre os dedos. Familiar?
\n\nEm 2024, autenticação segura não é apenas "colocar um login". É uma ciência completa. E o TypeScript aqui é seu melhor amigo. Ele não vai deixar você passar acidentalmente o tipo de dado errado, vai te avisar onde você esqueceu de verificar o token e vai te salvar de erros bobos durante a compilação.
\n\nNeste artigo, vamos analisar como construir autenticação segura em aplicações TypeScript do zero. Sem enrolação — apenas exemplos reais, melhores práticas e um toque de humor negro (não dá para viver sem).
\n\nJWT: amor à primeira vista com o token
\n\nJSON Web Token (JWT) é o padrão de facto para autenticação em aplicações web modernas. Mas, como o Tio Ben dizia, "com grandes poderes vêm grandes responsabilidades". O JWT pode ser tanto um escudo quanto uma bomba-relógio.
\n\nComo NÃO fazer: anti-exemplo
\n\nO pecado mais comum dos iniciantes é armazenar o token no localStorage e transmiti-lo abertamente. Sim, funciona. Mas é como deixar as chaves do apartamento debaixo do tapete — parece conveniente, mas qualquer transeunte pode entrar. Um ataque XSS — e todos os seus tokens estão nas mãos do invasor.
\n\nTrabalhando corretamente com JWT em TypeScript
\n\nVamos escrever um wrapper seguro para trabalhar com tokens. O TypeScript nos ajudará a não confundir os tipos e a não esquecer da validação.
\n\nimport jwt from 'jsonwebtoken';\n\ninterface TokenPayload {\n userId: string;\n role: 'user' | 'admin';\n iat?: number;\n exp?: number;\n}\n\n// Geramos o token com tipagem clara\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 e já fazemos o cast para nossa interface\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('Token não passou na verificação:', error);\n return null;\n }\n}\n\nObserve: sempre verificamos se JWT_SECRET existe e é uma string. O TypeScript nos obriga a fazer isso — nada de undefined em tempo de execução!
Hash de senhas: bcrypt vem ao resgate
\n\nArmazenar senhas em texto puro é um crime. Mesmo que seu banco de dados seja mais seguro que o Fort Knox, uma invasão — e você estará nas notícias como mais um "vazamento de senhas". O hash de senhas é sua tábua de salvação.
\n\nPor que bcrypt e não MD5?
\n\nO MD5 morreu. SHA-256 também não é uma opção. GPUs modernas podem processar bilhões de hashes por segundo. O Bcrypt, por outro lado, foi propositalmente criado para ser lento. Ele usa salt (dados aleatórios) e um fator de custo ajustável. Quanto maior o cost, mais tempo leva para calcular o hash. E isso é bom!
\n\nImplementação em TypeScript
\n\nimport bcrypt from 'bcrypt';\n\nconst SALT_ROUNDS = 12; // Valor ideal em 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// Função tipada para 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 // Salva no banco de dados (aqui seu c