Autenticação
Como alguém entra — e como alguém deixa de entrar: convite, registro aberto com aprovação, login
social nos quatro provedores, a página única de login opcional (/login), proteção anti-robô, os
três estados da conta e o que a rota pública / mostra (e o que ela deliberadamente não mostra).
A ordem exata das checagens de acesso a um painel — indisponibilidade, aprovação pendente,
master_global, papel — está no diagrama Regra de acesso ao painel
(DG-03), na página de arquitetura em diagramas.
Sequência: login por senha e 2FA
sequenceDiagram
%% DG-04
accTitle: Login por senha e segundo fator
accDescr: A checagem canAccessPanel() vem antes de qualquer desafio de 2FA, que só aparece dentro de um bloco condicional no próximo request ao painel.
participant visitante as Visitante
participant tela_login as Tela de login
participant vendor_login as Login (Filament)
participant authenticate as Authenticate
participant authenticate_session as AuthenticateSession
participant must_two_factor as MustTwoFactor
participant locker as Locker
participant exigir_email as ExigirEmailVerificado (/app, KIT_REGISTRO_VERIFICAR_EMAIL)
participant resposta_login as RespostaDeLogin
visitante->>tela_login: credenciais
tela_login->>vendor_login: authenticate()
note over vendor_login: rate limit 5 tentativas
alt conta indisponível (senha certa)
vendor_login-->>visitante: recusa (403) via ContaIndisponivelController
else canAccessPanel() permite
vendor_login->>resposta_login: attemptWhen, sessão regenerada
resposta_login-->>visitante: decide o destino
visitante->>authenticate: request seguinte ao painel
authenticate->>authenticate_session: sessão validada
authenticate_session->>must_two_factor: checagem de 2FA
alt 2FA ligado na conta
must_two_factor-->>visitante: desafio 2FA
end
must_two_factor->>locker: checa bloqueio (ociosidade/manual)
locker->>exigir_email: e-mail verificado?
end
A tela de login do kit intercepta a falha do Filament só para explicar a conta indisponível
(app/Filament/Pages/Auth/TelaLogin.php:authenticate:196); o rate limit de 5 tentativas é do
próprio Filament (vendor/filament/filament/src/Auth/Pages/Login.php:rateLimit(5):70), que também
decide com canAccessPanel() (vendor/filament/filament/src/Auth/Pages/Login.php:canAccessPanel:188).
O destino do login é sempre app(LoginResponse::class), vinculado a RespostaDeLogin
(app/Providers/KitServiceProvider.php:LoginResponse:847). A pilha do próximo request (ordem real,
conferida com php artisan route:list --json) é Authenticate → AuthenticateSession →
MustTwoFactor → Locker → ExigirEmailVerificado (só obrigatório no /app, com a chave ligada).
Sessão autenticada
stateDiagram-v2 %% DG-10 accTitle: Sessão autenticada accDescr: Depois do login, a sessão alterna entre autenticada, aguardando 2FA, bloqueada e encerrada, conforme a ociosidade, o bloqueio manual e as tentativas de senha. state "Autenticada" as autenticada state "Aguardando 2FA" as aguardando_2fa state "Bloqueada" as bloqueada state "Encerrada" as encerrada [*] --> autenticada autenticada --> aguardando_2fa : ainda não confirmou o 2FA aguardando_2fa --> autenticada : confirma o 2FA autenticada --> bloqueada : ociosidade 1800s ou bloqueio manual bloqueada --> autenticada : senha correta bloqueada --> encerrada : 5 tentativas erradas (force logout) autenticada --> encerrada : logout manual
O desafio de 2FA só existe para quem o confirmou antes — é o MustTwoFactor do próprio diagrama de
cima. O bloqueio por ociosidade usa config('lockscreen.idle_timeout') = 1800
(config/lockscreen.php:idle_timeout:16); os três painéis compartilham o mesmo limite de
tentativas com força de logout (app/Providers/Filament/AdminPanelProvider.php:enableRateLimit:268,
app/Providers/Filament/AppPanelProvider.php:enableRateLimit:375,
app/Providers/Filament/InfraPanelProvider.php:enableRateLimit:291).
Desbloquear leva de volta à página que a trava interceptou, como o intended do login do Laravel: a URL
pedida (com a query) quando a ociosidade travou a sessão, ou a última que a pessoa tentou abrir com ela
já travada — pela senha e pela volta do provedor social da mesma conta. Sem página guardada (a tela
aberta por favorito), vai à raiz do painel; no /app, à organização em que a pessoa estava, se ela
ainda a acessa. Pedido que espera JSON e chamada Livewire não viram destino, e outra conta que entra
pela tela não herda a página. Quem guarda a página é App\Http\Middleware\BloquearSessaoComRetorno,
que substitui o middleware do pacote por bind no AppServiceProvider (o pacote só guardava a página
no bloqueio manual).