Appearance
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 terUservinculado). Cadastro pastoral: CPF, data de nascimento, função eclesiástica, congregação, etc.- Staff/Colaborador —
Userque 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ção | Cria | Consome slot max_users? |
|---|---|---|
| Self-registration (Fortify) | User + Member vinculados | Não |
| Social login (Google/Facebook) | User + Member vinculados | Não |
Admin cria via POST /users | User (sem Member automático) | Não (sem role ainda) |
Admin atribui role via PATCH /users/{ulid}/role | — | Sim (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
| Campo | Tipo | Descrição |
|---|---|---|
| ulid | string | Identificador público (nunca expor ID) |
| name | string | Nome do usuário |
| string | E-mail (unique) | |
| cpf | string(11) | CPF (unique, nullable) |
| ativo | boolean | Conta ativa/inativa |
| profile_completed | boolean | Perfil completo |
| termo_aceite | datetime | Aceite 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.
| Role | Descrição |
|---|---|
| super-admin | Acesso total (bypass) |
| admin-igreja | Administrador da igreja (todas as permissões) |
| admin-congregacao | Admin de congregação específica |
| secretario | Secretaria (visualização + cadastro) |
| tesoureiro | Tesoureiro (financeiro) |
| lider-grupo | Líder de grupo/célula (futuro) |
| membro | Membro comum (acesso limitado) |
Permissões (26 total)
| Domínio | Permissões |
|---|---|
| User | user.view, user.create, user.edit, user.delete, user.manage |
| Church | church-profile.view/update, congregation.view/create/update/delete, church-official.assign/remove, reference-data.view/manage |
| Member | member.view, member.create, member.edit, member.delete |
| Finance | finance.view, finance.create, finance.edit, finance.delete, finance.confirm |
| Bank Account | bank-account.view, bank-account.manage |
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| GET | /users | Lista de usuários |
| POST | /users | Criar usuário |
| GET | /users/ | Detalhes do usuário |
| PUT | /users/ | Atualizar usuário |
| DELETE | /users/ | Excluir (anonimiza LGPD) |
| PATCH | /users/{ulid}/toggle | Ativar/desativar |
| PATCH | /users/{ulid}/role | Atribuir role |
| GET/POST | /roles | CRUD de roles |
Middleware
auth— requer autenticaçãoprofile.completed— requer perfil completo (redireciona para /settings/profile)
O middleware
verifiednão é usado em nenhuma rota atualmente. Ver seção abaixo "Verificação de e-mail".
Menu lateral gated por permissão
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):
| Item | Permissão | Feature |
|---|---|---|
| Perfil da Igreja | church-profile.view | — |
| Congregações | congregation.view | — |
| Grupos | group.view | — |
| Patrimônio | asset.view | — |
| Eventos | event.view | — |
| Mural de Oração | prayer.view | prayer_wall |
| Notificações / Preferências | notification.view | notifications |
| E-mail em Massa | mass_email.view | mass_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):
class User extends Authenticatable implements MustVerifyEmail(adicionar a interface).- Configurar mailer real no
.env(SMTP/SES/Resend/Mailgun) e smtp queue. - Recolocar o middleware
verifiednas rotas de domínio (routes/tenant.phplinha ~134) — a mudança é simples: trocar['auth', 'profile.completed']por['auth', 'verified', 'profile.completed']. Também voltar['auth', 'verified']emsettings.phpe no Dashboard. - Adicionar um segundo banner na dashboard para o estado "e-mail não verificado" (atualmente só temos o banner de CPF).
- Os testes em
tests/Feature/Auth/EmailVerificationTest.phpjá existem e usamskipUnlessFortifyFeature(Features::emailVerification()); voltarão a rodar naturalmente quando a interface for adicionada. - Considerar se
profile_completeddeve voltar a exigirhasVerifiedEmail()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:
ProfileController@editrenderizasettings/Profile.vuecom o formulário (nome, e-mail, CPF, avatar).ProfileUpdateRequestvalida viaProfileValidationRules::profileRules()— agora incluicpf(nullable|string|size:11|unique:users,cpf,{userId}). OprepareForValidation()faz strip da máscara (123.456.789-01→12345678901) porque oCpfInput/InputMaskmantém a máscara no<input>DOM e o Inertia<Form>submete o DOM.ProfileController@update:- Preenche os campos validados via
$user->fill(...). - Se o usuário alterou o e-mail, limpa
email_verified_at. - Flipa
profile_completed = trueautomaticamente quandoname && cpf(verificação de e-mail é ortogonal — o middlewareverifiedcuida dela separadamente). - Persiste com
$user->save().
- Preenche os campos validados via
- Inertia compartilha
auth.user.cpfviaHandleInertiaRequestspara 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
cpfexiste 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.