Programação Por Equipe DevBras

Capa do artigo: Login por matrícula no Laravel: busca, limite de tentativas e recuperação de senha

Login por matrícula no Laravel: busca, limite de tentativas e recuperação de senha

Troque o e-mail pela matrícula no login do Laravel sem quebrar o resto: coluna única, Auth::attempt com a chave certa, limite por matrícula e um caminho real para recuperar acesso.

O almoxarifado da Distribuidora Ponte pediu para a equipe entrar com a matrícula do crachá, não com e-mail. O desenvolvedor trocou o rótulo do campo, renomeou o name do input e mudou uma linha no controller. O login funcionou. Na semana seguinte, a operadora Rita esqueceu a senha, digitou a matrícula 004187 na tela de recuperação e nada aconteceu: a tela ainda procurava um e-mail, e o "Rita" da conta tinha o e-mail genérico do setor, que ninguém lê.

A resposta curta: no Laravel, trocar o identificador de login é mexer em quatro lugares juntos — a coluna no banco, a chamada a Auth::attempt, a chave do limite de tentativas e o fluxo de recuperação de senha. Se só o primeiro muda, o login entra e a recuperação de acesso fica quebrada. Este guia mostra a implementação; o raciocínio geral, válido para qualquer framework, está em trocar o campo de login de e-mail para matrícula.

Por que não é só trocar o rótulo

O e-mail cumpre dois papéis ao mesmo tempo: identifica a conta e é o canal para falar com a pessoa. Quando você troca o identificador para matrícula, separa esses papéis. A matrícula identifica, mas não recebe mensagem. Tudo que dependia do e-mail como canal — link de redefinição de senha, aviso de login suspeito, confirmação de troca — precisa de outro destino.

Há também uma diferença de risco. E-mails são difíceis de adivinhar em sequência; matrículas costumam ser números sequenciais. Quem tenta entrar sabe que 004186 e 004188 provavelmente existem. Isso torna o limite de tentativas e a mensagem de erro genérica ainda mais importantes do que no login por e-mail.

Por fim, se a mesma aplicação tem clientes entrando com e-mail e equipe entrando com matrícula, são duas portas. Misturar as duas no mesmo formulário com um if costuma acabar em sessão compartilhada; a separação por guard está em dois logins no Laravel.

Como implementar o login por matrícula

  1. Crie a coluna com índice único e normalize o formato antes de gravar.
  2. Decida o canal de recuperação: e-mail de contato cadastrado à parte ou atendimento presencial.
  3. Troque a chave em Auth::attempt de email para matricula.
  4. Troque a chave do limite de tentativas, que no código gerado pelos kits iniciais também usa o e-mail.
  5. Mantenha a mensagem de erro genérica, igual para matrícula inexistente e senha errada.
  6. Ajuste a recuperação de senha para buscar pela matrícula e enviar ao canal decidido no passo 2.

A migração

Schema::table('funcionarios', function (Blueprint $table) {
    $table->string('matricula', 20)->unique()->after('id');
    $table->string('email_contato')->nullable();
    $table->boolean('ativo')->default(true);
});

A matrícula é string, não inteiro. "004187" com zeros à esquerda é um identificador, não uma quantidade; guardado como número, vira 4187 e a pessoa que digita os zeros não entra. O índice único garante que a busca encontre no máximo uma conta e deixa a consulta rápida.

Normalizar antes de buscar

Crachás impressos com espaço ("004 187"), letras maiúsculas e minúsculas em matrículas alfanuméricas e zeros que alguém omite são as fontes clássicas de "a senha está certa e não entra". Escolha uma forma canônica e aplique tanto no cadastro quanto no login:

function normalizarMatricula(string $valor): string
{
    return strtoupper(preg_replace('/\s+/', '', $valor));
}

Se a regra da empresa é que matrículas sempre têm seis dígitos, você pode completar com zeros à esquerda aqui também. O importante é que o cadastro e o login passem pela mesma função. Espaços invisíveis colados de planilha são outro clássico, descrito em espaço em branco que quebra o login.

O controller de login

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Validation\ValidationException;

public function store(Request $request)
{
    $dados = $request->validate([
        'matricula' => ['required', 'string', 'max:20'],
        'password'  => ['required', 'string'],
    ]);

    $matricula = normalizarMatricula($dados['matricula']);
    $chave = 'login-equipe:' . $matricula . '|' . $request->ip();

    if (RateLimiter::tooManyAttempts($chave, 5)) {
        $segundos = RateLimiter::availableIn($chave);
        throw ValidationException::withMessages([
            'matricula' => 'Muitas tentativas. Tente novamente em ' . $segundos . ' segundos.',
        ]);
    }

    $credenciais = [
        'matricula' => $matricula,
        'password'  => $dados['password'],
        'ativo'     => true,
    ];

    if (! Auth::guard('equipe')->attempt($credenciais, $request->boolean('lembrar'))) {
        RateLimiter::hit($chave);
        throw ValidationException::withMessages([
            'matricula' => 'Matrícula ou senha incorretas.',
        ]);
    }

    RateLimiter::clear($chave);
    $request->session()->regenerate();

    return redirect()->intended('/painel');
}

O provider Eloquent do Laravel usa todas as chaves do array de credenciais, exceto a senha, como condição de busca. Por isso 'matricula' e 'ativo' viram um WHERE, e a senha é conferida contra o hash. Funcionário desligado com ativo = false não entra, sem código extra.

Se o projeto nasceu de um kit inicial como o Breeze, o login fica numa classe de request com um método que chama Auth::attempt com e-mail e outro que monta a chave do limite de tentativas também com o e-mail. Troque os dois. Esquecer o segundo faz o limite contar tentativas por um campo vazio, e o bloqueio deixa de funcionar ou bloqueia todo mundo junto.

A mensagem de erro

"Matrícula não encontrada" e "senha incorreta" como mensagens diferentes ensinam a quem está testando quais matrículas existem. Como matrícula é sequencial, a lista inteira sai em minutos. Use a mesma mensagem para os dois casos, como explica login que diz "usuário não existe".

Recuperação de senha com matrícula

O broker de senhas do Laravel busca o usuário pelas credenciais que você passa, grava o token associado ao valor de getEmailForPasswordReset() e envia uma notificação por e-mail. Por padrão, tanto esse método quanto o roteamento da notificação usam a coluna email. Dá para aproveitar o mecanismo: busque pela matrícula e aponte os dois para o e-mail de contato.

// No model Funcionario
public function getEmailForPasswordReset()
{
    return $this->email_contato;
}

public function routeNotificationForMail($notification)
{
    return $this->email_contato;
}

// No controller de "esqueci a senha"
$status = Password::broker('funcionarios')->sendResetLink([
    'matricula' => normalizarMatricula($request->input('matricula')),
]);

return back()->with('status', 'Se a matrícula estiver cadastrada com um e-mail de contato, enviamos as instruções.');

Três cuidados. A resposta é sempre a mesma, exista ou não a matrícula. O formulário de redefinição gerado pelos kits espera o e-mail no link e no formulário, então revise essa tela para trabalhar com o identificador que você escolheu. E o broker funcionarios precisa estar configurado em config/auth.php apontando para o provider da equipe.

Quando parte da equipe não tem e-mail nenhum, não invente um. O caminho honesto é a redefinição assistida: um supervisor autenticado gera um link de uso único, válido por pouco tempo, e entrega pessoalmente. Fica registrado quem gerou e quando. Isso é mais seguro do que um "e-mail do setor" que dez pessoas leem.

Erros comuns

  • Guardar matrícula como inteiro. Zeros à esquerda somem e quem digita "004187" não entra.
  • Trocar o attempt e esquecer a chave do limite de tentativas. O bloqueio por excesso de tentativas para de funcionar ou bloqueia todos.
  • Mensagens diferentes para matrícula inexistente e senha errada. Matrícula sequencial vira lista de contas válidas.
  • Manter a recuperação procurando e-mail. A pessoa pede o link e nada chega; a próxima ligação é para o suporte.
  • Cadastrar e-mail genérico do setor como canal de recuperação. Qualquer colega pode redefinir a senha de outro.
  • Normalizar só no login. O cadastro grava "004 187" e o login procura "004187".

Checklist

  • A coluna matricula é texto, única e indexada.
  • Cadastro e login passam pela mesma função de normalização.
  • Auth::attempt usa a matrícula e filtra contas inativas.
  • O limite de tentativas usa a matrícula normalizada na chave.
  • A mensagem de erro é igual para matrícula inexistente e senha errada.
  • A recuperação busca pela matrícula e envia a um canal individual.
  • Quem não tem e-mail tem um processo de redefinição assistida com registro.

Pegue uma conta de teste da equipe, peça a recuperação de senha usando só a matrícula e acompanhe: em qual caixa de entrada o link chega, e quem mais lê essa caixa?

Avalie este artigo

Referência: https://blog.impulso.network/autenticacao-em-laravel-modificando-a-validacao-das-credenciais/