R
Interview Deckreact · next.js · senior/staff
0·0·122
Básico FE

Formulário de Login — Boas Práticas

Controlled inputs, validação client-side, UX de erros, proteção contra brute-force e CSRF.

Um formulário de login seguro combina: estado controlado via React, validação síncrona antes de chamar a API, tratamento explícito de erros da API (credenciais inválidas vs servidor fora), e proteção via CSRF token ou SameSite cookies. UX: desabilitar botão durante loading, focar no campo de erro, não revelar se email existe.

Problema
Formulários que enviam requisição com campos vazios, expõem se o email existe, não desabilitam o botão durante o request, ou mostram erros técnicos para o usuário.
Solução
Validar client-side antes do fetch, usar estado loading/error explícito, mensagem genérica de erro ("Email ou senha inválidos"), cookie HttpOnly para o token de sessão, e rate limiting no servidor.
Tip
Nunca diga "email não encontrado" — sempre "email ou senha inválidos". Isso evita enumeração de usuários. O erro deve ser o mesmo para email errado e senha errada.
function LoginForm() {
  const [form, setForm] = useState({ email: '', password: '' })
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(false)

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (!form.email || !form.password) {
      setError('Preencha todos os campos')
      return
    }
    setLoading(true)
    setError('')
    try {
      await login(form)
    } catch {
      setError('Email ou senha inválidos') // genérico
    } finally {
      setLoading(false)
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={form.email}
        onChange={e => setForm(f => ({...f, email: e.target.value}))} />
      <input type="password" value={form.password}
        onChange={e => setForm(f => ({...f, password: e.target.value}))} />
      {error && <p role="alert">{error}</p>}
      <button type="submit" disabled={loading}>
        {loading ? 'Entrando...' : 'Entrar'}
      </button>
    </form>
  )
}
Q.Como evitar que o botão de submit seja clicado duas vezes?
A.Setar um estado `loading: true` no início do submit e `disabled={loading}` no botão. Isso previne double-submit que cria requests duplicados.
Q.Onde guardar o token JWT após login?
A.HttpOnly cookie (não acessível via JS, protege contra XSS). localStorage é conveniente mas vulnerável a XSS. sessionStorage some ao fechar a tab. Cookie HttpOnly + SameSite=Strict é o padrão de segurança.
Q.Como implementar "lembrar de mim"?
A.Ajustar o Max-Age do cookie de sessão. Com JWT, emitir um token com expiração longa (7-30 dias) vs curta (1h). O "lembrar" não deve armazenar a senha em lugar nenhum.
49/122← →