Skip to content

Autenticação & Usuários

Visão Geral

O sistema usa Laravel Fortify para autenticação headless com views Inertia. Cada tenant tem seus próprios usuários no banco isolado. A autenticação acontece apenas no contexto do tenant (subdomínio).

Modelo conceitual (User vs Member vs Staff)

Três conceitos distintos — não confundir:

  • User — identidade de autenticação. Quem pode fazer login no sistema. Tem email, senha, 2FA, etc.
  • Member — pessoa da igreja (pode ou não ter User vinculado). Cadastro pastoral: CPF, data de nascimento, função eclesiástica, congregação, etc.
  • Staff/ColaboradorUser que tem ao menos uma role Spatie atribuída. É quem consome slot no plano (max_users).

Regra geral: todo Member pode virar User (recebe acesso ao sistema), mas só vira Staff quando um admin atribui uma role. Self-registration e social login criam User + Member automaticamente via RegisterMemberUserAction, sem role — o usuário entra como "pessoa da igreja", não consome slot de staff.

OperaçãoCriaConsome slot max_users?
Self-registration (Fortify)User + Member vinculadosNão
Social login (Google/Facebook)User + Member vinculadosNão
Admin cria via POST /usersUser (sem Member automático)Não (sem role ainda)
Admin atribui role via PATCH /users/{ulid}/roleSim (se for a primeira role)

Contagem: Tenant::isAtUserLimit() faz whereHas('roles'). Users sem role contam em max_members (via seus Members), não em max_users.

User Model

Namespace: App\Domain\User\Models\User

CampoTipoDescrição
ulidstringIdentificador público (nunca expor ID)
namestringNome do usuário
emailstringE-mail (unique)
cpfstring(11)CPF (unique, nullable)
ativobooleanConta ativa/inativa
profile_completedbooleanPerfil completo
termo_aceitedatetimeAceite dos termos

Funcionalidades

  • Login com e-mail + senha
  • Registro de novos usuários
  • Reset de senha via e-mail
  • Verificação de e-mail
  • Two-Factor Authentication (TOTP)
  • Toggle ativo/inativo (admin)

Roles e Permissões

Gerenciado via Spatie Permission. O super-admin tem bypass automático via Gate::before.

RoleDescrição
super-adminAcesso total (bypass)
admin-igrejaAdministrador da igreja (todas as permissões)
admin-congregacaoAdmin de congregação específica
secretarioSecretaria (visualização + cadastro)
tesoureiroTesoureiro (financeiro)
lider-grupoLíder de grupo/célula (futuro)
membroMembro comum (acesso limitado)

Permissões (26 total)

DomínioPermissões
Useruser.view, user.create, user.edit, user.delete, user.manage
Churchchurch-profile.view/update, congregation.view/create/update/delete, church-official.assign/remove, reference-data.view/manage
Membermember.view, member.create, member.edit, member.delete
Financefinance.view, finance.create, finance.edit, finance.delete, finance.confirm
Bank Accountbank-account.view, bank-account.manage

Endpoints

MétodoRotaDescrição
GET/usersLista de usuários
POST/usersCriar usuário
GET/users/Detalhes do usuário
PUT/users/Atualizar usuário
DELETE/users/Excluir (anonimiza LGPD)
PATCH/users/{ulid}/toggleAtivar/desativar
PATCH/users/{ulid}/roleAtribuir role
GET/POST/rolesCRUD de roles

Middleware

  • auth — requer autenticação
  • profile.completed — requer perfil completo (redireciona para /settings/profile)

O middleware verified não é usado em nenhuma rota atualmente. Ver seção abaixo "Verificação de e-mail".

resources/js/components/AppSidebar.vue monta os grupos (Secretaria, Igreja, Financeiro, Comunicação, WhatsApp) a partir das permissões do usuário logado (auth.user.permissions + fallback super-admin). Cada item só aparece se o usuário tiver a permissão (e, quando aplicável, a feature correspondente do tenant):

ItemPermissãoFeature
Perfil da Igrejachurch-profile.view
Congregaçõescongregation.view
Gruposgroup.view
Patrimônioasset.view
Eventosevent.view
Mural de Oraçãoprayer.viewprayer_wall
Notificações / Preferênciasnotification.viewnotifications
E-mail em Massamass_email.viewmass_email

O grupo "Igreja" só é adicionado ao menu se houver pelo menos um item visível — evita header órfão para usuários sem permissão nenhuma nessa área.

Verificação de e-mail (pendente)

No momento a aplicação não envia e-mail de verificação. O Fortify tem Features::emailVerification() declarado, mas o model User não implementa MustVerifyEmail, então o fluxo de verificação não é disparado no registro e o middleware verified foi removido das rotas para não bloquear usuários indevidamente.

Para ativar no futuro (roadmap):

  1. class User extends Authenticatable implements MustVerifyEmail (adicionar a interface).
  2. Configurar mailer real no .env (SMTP/SES/Resend/Mailgun) e smtp queue.
  3. Recolocar o middleware verified nas rotas de domínio (routes/tenant.php linha ~134) — a mudança é simples: trocar ['auth', 'profile.completed'] por ['auth', 'verified', 'profile.completed']. Também voltar ['auth', 'verified'] em settings.php e no Dashboard.
  4. Adicionar um segundo banner na dashboard para o estado "e-mail não verificado" (atualmente só temos o banner de CPF).
  5. Os testes em tests/Feature/Auth/EmailVerificationTest.php já existem e usam skipUnlessFortifyFeature(Features::emailVerification()); voltarão a rodar naturalmente quando a interface for adicionada.
  6. Considerar se profile_completed deve voltar a exigir hasVerifiedEmail() ou se continua ortogonal (o controller atual flipa só com name+cpf).

Completar perfil (CPF)

Usuários recém-registrados (registro direto ou social login) começam com profile_completed = false e são redirecionados para /settings/profile pelo middleware profile.completed ao tentar acessar qualquer rota de domínio.

Fluxo:

  1. ProfileController@edit renderiza settings/Profile.vue com o formulário (nome, e-mail, CPF, avatar).
  2. ProfileUpdateRequest valida via ProfileValidationRules::profileRules() — agora inclui cpf (nullable|string|size:11|unique:users,cpf,{userId}). O prepareForValidation() faz strip da máscara (123.456.789-0112345678901) porque o CpfInput/InputMask mantém a máscara no <input> DOM e o Inertia <Form> submete o DOM.
  3. ProfileController@update:
    • Preenche os campos validados via $user->fill(...).
    • Se o usuário alterou o e-mail, limpa email_verified_at.
    • Flipa profile_completed = true automaticamente quando name && cpf (verificação de e-mail é ortogonal — o middleware verified cuida dela separadamente).
    • Persiste com $user->save().
  4. Inertia compartilha auth.user.cpf via HandleInertiaRequests para que a view exiba o valor atual após o save.

Componente frontend: CpfInput (emite apenas dígitos, 11 chars).

Testes: tests/Feature/Settings/ProfileUpdateTest.php cobre persistência, unicidade e o flip do profile_completed.

Atenção: o campo cpf existe tanto no User quanto no Member (tabelas diferentes). O CPF do User serve para identificação do titular da conta; o do Member pode ou não coincidir se o usuário também for um membro da igreja.