🔐 Guia de Integração de Autenticação - C-Suite
Visão Geral
Este guia explica como integrar o sistema de autenticação centralizado (csuite-auth) em todos os serviços do ecossistema C-Suite.
Arquitetura
Cliente
│
├─> POST /auth/login (csuite-auth)
│ └─> Retorna: { access_token, refresh_token, user }
│
└─> GET /api/protected (qualquer serviço)
└─> Header: Authorization: Bearer <token>
└─> Serviço valida token via csuite-auth ou common_auth
Módulos Disponíveis
1. common/common_sso.py
get_current_user_from_token(): Valida token via auth serviceget_current_user_optional(): Retorna usuário ou None (para endpoints opcionais)
2. common/common_auth.py
get_current_user(): Valida token localmente (se JWT_SECRET_KEY compartilhado)require_role(): Dependency para verificar roles
Integração Passo a Passo
Passo 1: Adicionar Imports
# No main.py do serviço
AUTH_ENABLED = os.getenv("AUTH_ENABLED", "false").lower() == "true"
if AUTH_ENABLED:
try:
from common.common_sso import get_current_user_from_token, get_current_user_optional
from common.common_auth import require_role
except ImportError:
AUTH_ENABLED = False
logger.warning("Autenticação não disponível")
Passo 2: Proteger Endpoints
Opção A: Autenticação Obrigatória
from fastapi import Depends
@app.get("/api/protected")
async def protected_endpoint(
current_user: dict = Depends(get_current_user_from_token)
):
org_id = current_user.get("org_id")
user_id = current_user.get("user_id")
# Usa org_id e user_id do token
return {"data": "protegido", "org_id": org_id}
Opção B: Autenticação Opcional
@app.get("/api/public-or-protected")
async def optional_auth_endpoint(
current_user: Optional[dict] = Depends(get_current_user_optional)
):
if current_user:
# Usuário autenticado
return {"data": "personalizado", "user": current_user["email"]}
else:
# Usuário não autenticado - retorna dados públicos
return {"data": "público"}
Opção C: Verificar Roles
@app.post("/api/admin-only")
async def admin_endpoint(
current_user: dict = Depends(get_current_user_from_token),
_: None = Depends(require_role("admin", "super_admin"))
):
return {"message": "Apenas admins podem acessar"}
Passo 3: Habilitar Autenticação
Adicione variável de ambiente no docker-stack.yml:
environment:
- AUTH_ENABLED=true
- AUTH_SERVICE_URL=https://csuite.internut.com.br/auth
Exemplo Completo: Cashflow
# app/routers/cash_accounts.py
from fastapi import APIRouter, Depends, HTTPException
from common.common_sso import get_current_user_from_token
from common.common_errors import CSuiteError, ErrorCode
router = APIRouter()
@router.post("/cash-accounts", response_model=CashAccountResponse)
async def create_cash_account(
account: CashAccountCreate,
current_user: dict = Depends(get_current_user_from_token), # ← Adicionado
db: AsyncSession = Depends(get_db)
):
"""Cria uma nova conta de caixa"""
# Usa org_id do token para isolar dados
org_id = current_user.get("org_id")
if org_id:
account.org_id = org_id # Garante que a conta pertence à org do usuário
return await CashAccountService.create_account(db, account)
@router.get("/cash-accounts", response_model=List[CashAccountResponse])
async def list_cash_accounts(
active_only: bool = True,
current_user: dict = Depends(get_current_user_from_token), # ← Adicionado
db: AsyncSession = Depends(get_db)
):
"""Lista todas as contas de caixa da organização do usuário"""
org_id = current_user.get("org_id")
if not org_id:
raise CSuiteError(
code=ErrorCode.UNAUTHORIZED,
message="Organização não identificada",
status_code=401
)
# Filtra por org_id
return await CashAccountService.list_accounts(db, active_only, org_id=org_id)
SSO (Single Sign-On)
O SSO funciona automaticamente quando:
1. Usuário faz login em /auth/login
2. Token JWT é armazenado em cookie csuite_access_token
3. Todos os serviços validam o mesmo token
Configuração de Cookie
O csuite-auth pode configurar cookies automaticamente. Para habilitar:
# No csuite-auth/app/routers/auth.py
@router.post("/login")
async def login(request: LoginRequest, response: Response):
# ... autenticação ...
# Define cookie para SSO
response.set_cookie(
key="csuite_access_token",
value=tokens["access_token"],
max_age=30 * 60, # 30 minutos
httponly=True,
secure=True,
samesite="lax",
path="/"
)
return tokens
Rate Limiting
Já integrado automaticamente via common/common_rate_limit.py:
# No main.py
from common.common_rate_limit import RateLimitMiddleware
app.add_middleware(RateLimitMiddleware, enabled=True)
Configuração via env:
- RATE_LIMIT_ENABLED=true
- RATE_LIMIT_REQUESTS_PER_MINUTE=60
- RATE_LIMIT_REQUESTS_PER_HOUR=1000
Tratamento de Erros Padronizado
Todos os serviços já usam common/common_errors.py:
from common.common_errors import CSuiteError, ErrorCode
# Em um endpoint
if not resource:
raise CSuiteError(
code=ErrorCode.NOT_FOUND,
message="Recurso não encontrado",
status_code=404,
details={"resource_id": resource_id}
)
Checklist de Integração
- [ ] Adicionar imports de autenticação no
main.py - [ ] Adicionar
AUTH_ENABLEDcheck - [ ] Proteger endpoints sensíveis com
Depends(get_current_user_from_token) - [ ] Usar
org_iddo token para isolar dados - [ ] Adicionar
AUTH_ENABLED=truenodocker-stack.yml - [ ] Testar login e acesso a endpoints protegidos
- [ ] Verificar rate limiting funcionando
- [ ] Confirmar tratamento de erros padronizado
Migração Gradual
Para serviços em produção, recomenda-se migração gradual:
- Fase 1: Adicionar autenticação opcional (
get_current_user_optional) - Fase 2: Habilitar autenticação obrigatória em endpoints críticos
- Fase 3: Habilitar autenticação em todos os endpoints
Isso permite testar sem quebrar clientes existentes.