Autenticação e Autorização

O Velox possui um sistema completo de autenticação com suporte a:

  • Login tradicional (usuário/senha)

  • OAuth (Google, GitHub, Facebook, Discord)

  • RBAC (Roles e Permissões)

  • Rate Limiting (proteção contra brute force)

  • Sessions server-side (seguro, escalável)

Configuração

Configure via .env:

# Backend: memory (dev) ou database (produção)
AUTH_BACKEND=memory

# Sessões
SESSION_EXPIRE_HOURS=24

# Rate Limiting
MAX_LOGIN_ATTEMPTS=5
LOGIN_WINDOW_SECONDS=900

# OAuth Google
GOOGLE_CLIENT_ID=seu-client-id
GOOGLE_CLIENT_SECRET=seu-client-secret
GOOGLE_REDIRECT_URI=https://seusite.com/auth/google/callback

# OAuth GitHub
GITHUB_CLIENT_ID=seu-client-id
GITHUB_CLIENT_SECRET=seu-client-secret
GITHUB_REDIRECT_URI=https://seusite.com/auth/github/callback

Autenticação Tradicional

Importar funções:

from velox.auth import (
    authenticate, login, logout, login_required,
    create_user, get_current_user
)

Criar Usuário

from velox.auth import create_user

# Cria usuário e faz hash da senha automaticamente
user = create_user('joao', 'joao@email.com', 'senha123')
# ✓ Usuário criado: joao (1)

Login

@app.post('/login')
def do_login(req, res):
    username = req.form.get('username')
    password = req.form.get('password')

    user = authenticate(username, password)

    if user:
        # Cria sessão e retorna session_key
        session_key = login(req, user)
        # Define cookie com a sessão
        res.set_cookie('session_key', session_key, httponly=True)
        res.redirect('/dashboard')
    else:
        res.json({'error': 'Credenciais inválidas'}, status=401)

Logout

@app.post('/logout')
def do_logout(req, res):
    logout(req)
    res.delete_cookie('session_key')
    res.redirect('/')

Proteger Rotas

Use o decorador @login_required:

from velox.auth import login_required

@app.get('/perfil')
@login_required
def perfil(req, res):
    res.json(req.user.to_dict())

# Resultado:
# {
#   "id": 1,
#   "username": "joao",
#   "email": "joao@email.com",
#   "is_active": true,
#   "roles": [],
#   "permissions": []
# }

Middleware Global

Em vez de usar @login_required em cada rota, use o middleware:

from velox.auth import AuthMiddleware

app = Velox(__name__)
app.use(AuthMiddleware())

@app.get('/dashboard')
def dashboard(req, res):
    # req.user sempre disponível
    if req.user.is_authenticated:
        res.json({'user': req.user.username})
    else:
        res.json({'user': 'visitante'})

OAuth (Google, GitHub, Facebook, Discord)

Google OAuth

from velox.auth import get_oauth_provider, oauth_authenticate, login

@app.get('/auth/google')
def google_login(req, res):
    provider = get_oauth_provider('google')
    # Gera URL de autorização
    auth_url = provider.get_auth_url()
    res.redirect(auth_url)

@app.get('/auth/google/callback')
def google_callback(req, res):
    code = req.args.get('code')
    if not code:
        res.json({'error': 'Código não fornecido'}, status=400)
        return

    user = oauth_authenticate('google', code)

    if user:
        session_key = login(req, user)
        res.set_cookie('session_key', session_key)
        res.redirect('/')
    else:
        res.json({'error': 'Falha na autenticação OAuth'}, status=400)

GitHub OAuth

@app.get('/auth/github')
def github_login(req, res):
    provider = get_oauth_provider('github')
    res.redirect(provider.get_auth_url())

@app.get('/auth/github/callback')
def github_callback(req, res):
    user = oauth_authenticate('github', req.args.get('code'))
    if user:
        session_key = login(req, user)
        res.set_cookie('session_key', session_key)
        res.redirect('/')

Facebook OAuth

@app.get('/auth/facebook')
def facebook_login(req, res):
    provider = get_oauth_provider('facebook')
    res.redirect(provider.get_auth_url())

Discord OAuth

@app.get('/auth/discord')
def discord_login(req, res):
    provider = get_oauth_provider('discord')
    res.redirect(provider.get_auth_url())

RBAC (Controle de Acesso por Papel)

O Velox suporta roles e permissões granulares.

Criar Roles

from velox.auth import create_role, assign_role, revoke_role

# Criar papel com permissões
create_role('admin', [
    'posts.criar', 'posts.editar', 'posts.excluir',
    'usuarios.ver', 'usuarios.editar'
])

# Criar papel sem permissões iniciais
create_role('editor', ['posts.criar', 'posts.editar'])

create_role('moderador')

Atribuir Roles a Usuários

from velox.auth import assign_role, revoke_role

user = User.get(1)

# Atribuir role
assign_role(user, 'admin')

# Remover role
revoke_role(user, 'editor')

Verificar Roles

@app.get('/admin')
@login_required
def admin_panel(req, res):
    if req.user.has_role('admin'):
        res.json({'admin': True})
    else:
        res.json({'error': 'Acesso negado'}, status=403)

Decoradores de Role

from velox.auth import role_required

@app.get('/painel')
@role_required('admin', 'editor')  # qualquer um dos dois
def painel(req, res):
    res.json({'access': 'granted'})

Permissões Granulares

Conceder Permissão Extra

from velox.auth import grant_permission, revoke_permission

user = User.get(1)

# Conceder permissão específica
grant_permission(user, 'relatorios.exportar')

# Remover permissão
revoke_permission(user, 'relatorios.exportar')

Verificar Permissões

@app.get('/exportar')
@login_required
def exportar(req, res):
    if req.user.has_permission('relatorios.exportar'):
        # código de exportação
        res.json({'exported': True})
    else:
        res.json({'error': 'Sem permissão'}, status=403)

Decorador de Permissão

from velox.auth import permission_required

@app.post('/usuarios/excluir')
@permission_required('usuarios.excluir')
def excluir_usuario(req, res):
    # código para excluir
    res.json({'deleted': True})

Proteger Rotas por Tipo de Usuário

from velox.auth import (
    login_required, staff_required, superuser_required
)

# Requer usuário logado
@app.get('/conta')
@login_required
def conta(req, res):
    res.json(req.user.to_dict())

# Requer usuário com is_staff=True
@app.get('/admin/relatorios')
@staff_required
def relatorios(req, res):
    res.json({'reports': []})

# Requer usuário com is_superuser=True
@app.get('/admin/config')
@superuser_required
def config(req, res):
    res.json({'config': {}})

Banco de Dados vs Memória

Memory (padrão): - Rápido para desenvolvimento - Não persiste entre reinicializações - Ideal para testes

AUTH_BACKEND=memory

Database: - Persiste usuários e sessões - Suporta múltiplos workers (com Redis) - Recomendado para produção

AUTH_BACKEND=database
DATABASE_URI=db/app.db
# ou PostgreSQL:
DATABASE_URI=postgresql://user:pass@localhost:5432/mydb

Rate Limiting (Proteção contra Brute Force)

O Velox bloqueia tentativas de login excessivas automaticamente:

# Padrão: 5 tentativas em 900 segundos (15 minutos)
MAX_LOGIN_ATTEMPTS=5
LOGIN_WINDOW_SECONDS=900

Se exceder o limite, authenticate() retorna None:

user = authenticate(username, password)
if user is None:
    # Pode ser senha errada OU bloqueado por rate limit
    res.json({'error': 'Tentativas excedidas, tente mais tarde'}, status=429)

Modelo User

from velox.auth import User

user = User(
    id=1,
    username='joao',
    email='joao@email.com',
    is_active=True,
    is_staff=False,
    is_superuser=False,
    oauth_provider=None,  # 'google', 'github', etc.
    last_login=datetime.now(),
    roles=['admin'],
    permissions={'posts.criar'}
)

# Verificar senha
user.check_password('senha123')  # True/False

# Alterar senha
user.set_password('nova_senha')

# Converter para dict
user.to_dict()

Logout em Todos os Dispositivos

Para o backend de banco, você pode destruir todas as sessões de um usuário:

from velox.auth import auth_backend

@app.post('/logout-everywhere')
@login_required
def logout_everywhere(req, res):
    auth_backend.destroy_all_sessions(req.user)
    res.json({'message': 'Deslogado de todos os dispositivos'})

Próximos Passos

  • Configure HTTPS em produção (SESSION_COOKIE_SECURE=true)

  • Use Redis como cache para múltiplos workers

  • Adicione ** двухфакторную autenticação** (2FA)

  • Implemente recuperação de senha por email