fix(auth): corrige hash seed e documenta alinhamento do PASSWORD_PEPPER

- Atualiza hash hardcoded em 010_seed_super_admin.sql para hash válido
  gerado com pepper=gohorse-pepper (o antigo hash estava inválido e causava
  AUTH_INVALID_CREDENTIALS em qualquer reset do banco)
- Corrige valor de PASSWORD_PEPPER e CORS_ORIGINS no DEVOPS.md para
  refletir os valores reais do Coolify DEV
- Adiciona seção de troubleshooting no DEVOPS.md com diagnóstico e fix
  passo-a-passo para mismatch de pepper
- Adiciona seção "Known Gotchas" no AGENTS.md documentando:
  * Regra do PASSWORD_PEPPER (deve ser gohorse-pepper em todos ambientes)
  * Campo de login é email no DTO, não identifier
  * Hashes bcrypt em SQL devem usar arquivo -f, nunca -c ($ é expandido)
  * Credenciais de teste do ambiente DEV

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tiago Yamamoto 2026-02-22 11:43:35 -06:00
parent dda447e4b6
commit fcf960381c
3 changed files with 120 additions and 9 deletions

View file

@ -1,7 +1,14 @@
-- Migration: Create Super Admin and System Company
-- Description: Inserts the default System Company and Super Admin user.
-- Uses unified tables (companies, users, user_roles)
-- HARDCODED: This is the official superadmin - password: Admin@2025! (with pepper: gohorse-pepper)
--
-- ⚠️ PEPPER CRITICAL: This hash was generated with PASSWORD_PEPPER=gohorse-pepper
-- The backend (Coolify env var PASSWORD_PEPPER) MUST be set to: gohorse-pepper
-- If the pepper does not match, ALL logins will fail with "invalid credentials".
--
-- Credentials: identifier=superadmin / password=Admin@2025!
-- Hash: bcrypt("Admin@2025!" + "gohorse-pepper", cost=10)
-- Generated with bcryptjs 2.4.x / golang.org/x/crypto/bcrypt — both compatible.
-- 1. Insert System Company (for SuperAdmin context)
INSERT INTO companies (name, slug, type, document, email, description, verified, active)
@ -17,17 +24,16 @@ VALUES (
) ON CONFLICT (slug) DO NOTHING;
-- 2. Insert Super Admin User
-- Hash: bcrypt(Admin@2025! + gohorse-pepper)
INSERT INTO users (identifier, password_hash, role, full_name, email, status, active)
VALUES (
'superadmin',
'$2a$10$LtQroKXfdtgp7B9eO81bAuMY8BTpc5sRu76J0gFttCKZYDTFfMNA.',
'$2b$10$4759wJhnXnBpcwSnVZm9Eu.wTqGYVCHkxAU5a2NxhsFHU42nV3tzW',
'superadmin',
'Super Administrator',
'admin@gohorsejobs.com',
'ACTIVE',
true
) ON CONFLICT (identifier) DO UPDATE SET
) ON CONFLICT (identifier) DO UPDATE SET
password_hash = EXCLUDED.password_hash,
status = 'ACTIVE';

View file

@ -304,6 +304,55 @@ git push pipe dev
---
## ⚠️ Known Gotchas
### 1. PASSWORD_PEPPER must be `gohorse-pepper` everywhere
All migration seeds (`010_seed_super_admin.sql`) and the seeder-api use `gohorse-pepper` as the bcrypt pepper.
The Coolify/production env var `PASSWORD_PEPPER` **must match** or every login returns `AUTH_INVALID_CREDENTIALS`.
| Location | Value |
|----------|-------|
| `backend/.env` (local) | `gohorse-pepper` |
| `seeder-api/.env` | `gohorse-pepper` |
| Coolify DEV (`iw4sow8s0kkg4cccsk08gsoo`) | `gohorse-pepper` |
| Migration `010_seed_super_admin.sql` hash | computed with `gohorse-pepper` |
If you suspect a mismatch, see the full fix procedure in [DEVOPS.md](DEVOPS.md#troubleshooting-login-retorna-invalid-credentials).
### 2. Login API field: `email`, not `identifier`
The frontend login form sends `{ "email": "<username_or_email>", "password": "..." }`.
The backend core DTO (`internal/core/dto/user_auth.go`) has field `Email`, **not** `Identifier`.
The repository resolves it via `WHERE email = $1 OR identifier = $1`, so both username and email work.
Do **not** send `{ "identifier": "..." }` directly to `/api/v1/auth/login` — the field will be ignored and login will fail silently.
### 3. Bcrypt hashes in SQL — always use a file, never `-c`
Shell interprets `$` in bcrypt hashes as variable expansions. Example of **broken** approach:
```bash
# ❌ WRONG — $2b gets expanded by shell, hash gets corrupted
docker exec postgres psql -U user -d db -c "UPDATE users SET password_hash='$2b$10$abc...'"
```
Correct approach — write to file first, then use `-f`:
```bash
cat > /tmp/fix.sql <<'EOF'
UPDATE users SET password_hash = '$2b$10$4759wJhnXnBpcwSnVZm9Eu.wTqGYVCHkxAU5a2NxhsFHU42nV3tzW' WHERE identifier = 'lol';
EOF
docker cp /tmp/fix.sql <postgres_container>:/tmp/fix.sql
docker exec <postgres_container> psql -U gohorsejobs -d gohorsejobs -f /tmp/fix.sql
```
### 4. Test credentials (DEV/local.gohorsejobs.com)
| Role | Identifier | Password | Email |
|------|-----------|----------|-------|
| superadmin | `lol` | `Admin@2025!` | lol@gohorsejobs.com |
| superadmin | `superadmin` | `Admin@2025!` | admin@gohorsejobs.com |
---
## Documentation Index
| Document | Location | Description |

View file

@ -150,14 +150,70 @@ Configured via Coolify UI or API:
DATABASE_URL=postgres://gohorsejobs:gohorsejobs123@bgws48os8wgwk08o48wg8k80:5432/gohorsejobs?sslmode=disable
BACKEND_PORT=8521
ENV=development
JWT_SECRET=<configured>
JWT_SECRET=gohorsejobs-dev-jwt-secret-2024-very-secure-key-32ch
JWT_EXPIRATION=7d
PASSWORD_PEPPER=<configured>
COOKIE_SECRET=<configured>
COOKIE_DOMAIN=.gohorsejobs.com
CORS_ORIGINS=http://coolify-dev.gohorsejobs.com,https://coolify-dev.gohorsejobs.com
PASSWORD_PEPPER=gohorse-pepper
COOKIE_SECRET=gohorsejobs-cookie-secret-dev
CORS_ORIGINS=https://local.gohorsejobs.com,https://b-local.gohorsejobs.com,http://localhost:3000,http://localhost:8521
```
> ⚠️ **`PASSWORD_PEPPER` é crítico.** Todas as migration seeds e o seeder-api usam `gohorse-pepper`.
> Se este valor for alterado no Coolify sem regravar os hashes no banco, **todos os logins falharão**
> com `invalid credentials`. Veja a seção de troubleshooting abaixo.
### ⚠️ Troubleshooting: Login retorna `invalid credentials`
**Causa:** O `PASSWORD_PEPPER` no Coolify não coincide com o pepper usado para gerar os hashes no banco.
**Diagnóstico via SSH:**
```bash
ssh redbull
# Verificar pepper no container em execução:
docker inspect <backend_container_name> --format '{{range .Config.Env}}{{println .}}{{end}}' | grep PEPPER
# Testar login direto (sem escaping de shell):
cat > /tmp/login.json <<'EOF'
{"email":"lol","password":"Admin@2025!"}
EOF
docker run --rm --network coolify -v /tmp/login.json:/tmp/login.json \
curlimages/curl:latest -s -X POST \
http://<backend_container>:8521/api/v1/auth/login \
-H 'Content-Type: application/json' -d @/tmp/login.json
```
**Fix — opção 1: corrigir o pepper no Coolify (preferível):**
```bash
TOKEN=$(cat ~/.ssh/coolify-redbull-token)
curl -s -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"key":"PASSWORD_PEPPER","value":"gohorse-pepper"}' \
"https://redbull.rede5.com.br/api/v1/applications/iw4sow8s0kkg4cccsk08gsoo/envs"
# Reiniciar o backend
curl -s -H "Authorization: Bearer $TOKEN" \
"https://redbull.rede5.com.br/api/v1/applications/iw4sow8s0kkg4cccsk08gsoo/restart"
```
**Fix — opção 2: regravar o hash no banco (se o pepper mudou intencionalmente):**
```bash
# Gerar novo hash com o pepper correto (ex: dentro de container node):
docker run --rm node:20-alpine sh -c \
'cd /tmp && npm init -y > /dev/null && npm install bcryptjs > /dev/null && \
node -e "console.log(require(\"./node_modules/bcryptjs\").hashSync(\"Admin@2025!\"+process.env.PEPPER,10))" \
PEPPER=gohorse-pepper'
# Aplicar no banco (usar arquivo para preservar os $ do hash):
cat > /tmp/fix_hash.sql <<'EOF'
UPDATE users SET password_hash = '<hash_gerado>' WHERE identifier IN ('lol','superadmin');
EOF
docker cp /tmp/fix_hash.sql bgws48os8wgwk08o48wg8k80:/tmp/fix_hash.sql
docker exec bgws48os8wgwk08o48wg8k80 psql -U gohorsejobs -d gohorsejobs -f /tmp/fix_hash.sql
```
> **Nota:** Sempre use um arquivo (ou `docker cp` + `-f`) para executar SQL com hashes bcrypt.
> Passar o hash via `-c '...'` na linha de comando faz o shell interpretar os `$` como variáveis.
### Deploy via API
```bash