Login por matrícula sem quebrar o "esqueci a senha"
Antes de trocar o e-mail pela matrícula no login, garanta unicidade no banco, um canal de recuperação verificado e um fluxo de redefinição testado de ponta a ponta.
Começo de semestre, a Escola Técnica Horizonte troca o login do portal: em vez do e-mail, os 2.300 alunos entram com a matrícula. Na primeira manhã, a secretaria recebe 140 pedidos de senha nova. O "esqueci a senha" pedia a matrícula e mandava o link para o e-mail cadastrado, só que 312 alunos nunca tinham preenchido e-mail. E duas alunas descobriram que compartilhavam a mesma matrícula, herdada de um sistema antigo.
A resposta curta: trocar o identificador de login é mexer em três coisas juntas: a unicidade do novo campo, garantida pelo banco; o canal de recuperação, que precisa ser algo que a pessoa controla e que foi verificado; e o fluxo de redefinição inteiro, testado de ponta a ponta. Se o e-mail é o único jeito de recuperar a conta, ele não pode ser opcional.
Por que a troca parece simples e não é
Na tela, a mudança é um rótulo: "E-mail" vira "Matrícula". No código, é um campo diferente na consulta de autenticação. Por isso parece uma tarefa de meia hora. O problema é que o e-mail fazia dois papéis ao mesmo tempo, e só um deles foi trocado.
O e-mail era o identificador, aquilo que diz quem você afirma ser. E era o canal de recuperação, o lugar onde o sistema manda a prova de que você é você. A matrícula pode assumir o primeiro papel. Ela não assume o segundo: ninguém recebe mensagem numa matrícula. Se o fluxo de recuperação continua dependendo do e-mail, ele precisa existir, estar correto e ter sido confirmado pela pessoa.
Frameworks reforçam o tropeço. O fluxo padrão de redefinição de senha do Laravel, por exemplo, parte do e-mail. Mudar o campo do login sem revisar a redefinição deixa as duas pontas desencontradas. As questões de interface e de mensagem da troca de rótulo estão no post sobre login por matrícula; aqui o foco é o dado e a recuperação.
Como fazer a troca sem trancar ninguém para fora
- Audite os dados antes de mudar a tela. Procure matrículas repetidas, vazias e com espaços, e contas sem e-mail.
- Resolva as duplicidades com a secretaria, uma a uma, e só então crie um índice único no banco.
- Defina o canal de recuperação e quem não tem canal: e-mail verificado para a maioria, atendimento presencial com documento para o restante.
- Peça e verifique o e-mail de quem não tem, antes da troca, com um link de confirmação.
- Reescreva o "esqueci a senha" para receber a matrícula, mandar o link só para canal verificado e responder sempre a mesma mensagem.
- Aposente matrículas de quem saiu em vez de reaproveitá-las.
- Teste com uma conta nova, do cadastro à redefinição, não só o POST do login.
A auditoria em SQL
-- matrículas repetidas (com e sem espaços nas pontas)
SELECT TRIM(matricula) AS mat, COUNT(*) AS qtd
FROM usuarios
GROUP BY TRIM(matricula)
HAVING COUNT(*) > 1;
-- contas ativas sem canal de recuperação verificado
SELECT COUNT(*) AS sem_canal
FROM usuarios
WHERE ativo = 1
AND (email IS NULL OR email = '' OR email_verificado_em IS NULL);
-- depois de resolver as duplicidades
UPDATE usuarios SET matricula = TRIM(matricula);
ALTER TABLE usuarios ADD UNIQUE KEY uk_usuarios_matricula (matricula);
Na Escola Horizonte, a primeira consulta trouxe 9 matrículas repetidas, quase todas por espaço no fim vindo de importação de planilha. O espaço invisível que quebra login quebra também a unicidade. A segunda consulta trouxe os 312 alunos sem e-mail verificado. Com esses números, a troca foi adiada duas semanas e a secretaria fez uma campanha de atualização cadastral. A primeira manhã do semestre seguinte teve 6 chamados, não 140.
O índice único não é opcional. Validação só na aplicação deixa passar duas inserções simultâneas e qualquer carga feita direto no banco. Com o índice, a segunda inserção falha e você trata o erro.
O "esqueci a senha" que parte da matrícula
O link de redefinição carrega um token aleatório. O banco guarda só o hash do token, com validade curta. A resposta para quem pede é sempre a mesma, exista a matrícula ou não, para o formulário não virar uma ferramenta de descobrir quem estuda ali.
<?php
function solicitarRedefinicao(PDO $pdo, string $matricula): void
{
$stmt = $pdo->prepare(
'SELECT id, email FROM usuarios
WHERE matricula = ? AND ativo = 1 AND email_verificado_em IS NOT NULL'
);
$stmt->execute([trim($matricula)]);
$usuario = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$usuario) {
return;
}
$token = bin2hex(random_bytes(32));
$pdo->prepare(
'INSERT INTO redefinicoes_senha (usuario_id, token_hash, expira_em)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 30 MINUTE))'
)->execute([$usuario['id'], hash('sha256', $token)]);
$link = 'https://portal.exemplo.edu.br/redefinir?token=' . $token;
enviarEmail($usuario['email'], 'Redefinição de senha', 'Use este link em até 30 minutos: ' . $link);
}
A tela, em qualquer caso, mostra algo como "Se a matrícula estiver cadastrada com e-mail confirmado, enviamos as instruções. Se não receber, procure a secretaria com um documento." A função enviarEmail é a sua rotina de envio; o guia de formulário que chega ajuda a garantir que a mensagem não caia no spam.
Na hora de redefinir, busque pelo hash do token, confira que não expirou e que não foi usado, grave a nova senha com password_hash, marque o token como usado e encerre as outras sessões daquela conta. Coloque também um limite de pedidos por matrícula e por IP, como descreve o post sobre limite no esqueci a senha.
A matrícula de quem saiu não volta para a fila
Reaproveitar matrícula parece organizado e cria um risco silencioso: o novo aluno herda histórico, permissões e, às vezes, o e-mail de recuperação do anterior. Desative a conta, mantenha a matrícula reservada e gere uma nova para quem chega. Se a regra da instituição exige reaproveitar, a troca de titular precisa apagar o canal de recuperação antigo e exigir verificação do novo.
Erros comuns
- Trocar o rótulo e manter a redefinição baseada em e-mail sem revisar: quem não tem e-mail fica trancado para fora no primeiro esquecimento.
- Unicidade só na validação do formulário: duas importações ou dois cadastros simultâneos criam matrículas repetidas.
- E-mail opcional sendo o único canal de recuperação: a conta não tem como ser recuperada sem atendimento humano.
- Mandar o link para e-mail nunca verificado: um erro de digitação no cadastro entrega a conta para um desconhecido.
- Mensagem diferente para matrícula inexistente: o formulário passa a revelar quem é aluno, como alerta o post sobre erro de login genérico.
- Reaproveitar matrícula de quem saiu: o novo titular herda acesso e histórico que não são dele.
Checklist
- A consulta de matrículas repetidas retorna zero linhas.
- Existe índice único na coluna de matrícula.
- Todas as contas ativas têm canal de recuperação verificado ou um procedimento presencial definido.
- O link de redefinição só vai para e-mail confirmado.
- O token é aleatório, guardado como hash, com validade curta e uso único.
- A resposta do "esqueci a senha" é igual para matrícula existente e inexistente.
- Matrículas de contas encerradas não são reatribuídas.
- Uma conta nova passou por cadastro, login, esquecimento e redefinição num teste completo.
Pegue um usuário real do seu sistema sem e-mail preenchido: se ele esquecer a senha hoje, qual é exatamente o caminho para ele voltar a entrar?
Referência: https://github.com/laravelbrasil/forum/issues/81