Leodario.com

Leodario.com – Tudo sobre Tecnologia

Aula 61 – API JavaScript, Node.js e Express – Authentication vs Authorization – Conceitos fundamentais

Imagem destacada da aula de API

Introdução (3 min)

Caros alunos, sejam muito bem-vindos à Aula 61, onde desvendaremos dois pilares essenciais da segurança em APIs: a Autenticação e a Autorização. Imagine que você está chegando a um evento exclusivo, como um show de rock lendário ou uma conferência de altíssimo nível. Na entrada, há um porteiro que solicita seu ingresso e sua identidade. Este processo de verificar “quem você é” e se você tem o direito de entrar no local é a autenticação. Uma vez lá dentro, você percebe que há diferentes áreas: o acesso geral, a área VIP e o palco. Você, com seu ingresso comum, pode acessar a área geral, mas não a VIP nem o palco. A decisão de “o que você pode fazer” ou “quais áreas você pode acessar” é a autorização.

Em um mundo onde as APIs são o combustível que move a inovação digital, proteger esses valiosos pontos de acesso é imprescindível. Sem Autenticação e Autorização robustas, suas APIs seriam portas abertas para acessos indevidos, vazamento de dados e vulnerabilidades catastróficas. Este conhecimento é a base para construir APIs seguras, confiáveis e que respeitam a privacidade de seus usuários, atendendo aos mais rigorosos padrões de mercado.

Nesta aula, você não apenas compreenderá as diferenças conceituais entre Autenticação e Autorização, mas também dominará como aplicá-las de forma prática e eficaz em um ambiente Node.js com Express. Entenderemos como esses mecanismos se encaixam no ecossistema Node.js/Express, utilizando middlewares para gerenciar o controle de acesso de forma elegante e performática. Prepare-se para elevar o nível de segurança de suas aplicações!

Conceito Fundamental (7 min)

Vamos mergulhar nos detalhes técnicos para solidificar sua compreensão.

Autenticação: “Quem é você?”

A Autenticação é o processo pelo qual um sistema verifica a identidade de um usuário, serviço ou aplicação que tenta acessar um recurso. É a etapa inicial e vital para qualquer interação segura. Ela responde à pergunta fundamental: “Você é realmente quem afirma ser?”.

  • Terminologia da Indústria:

Identidade: A representação única de um usuário ou serviço.
Credenciais: Informações secretas usadas para provar a identidade (senhas, chaves de API, certificados, tokens).
Provedor de Identidade (IdP): Serviço que armazena e gerencia identidades (ex: Google, Facebook, Okta, seu próprio banco de dados de usuários).
Token de Autenticação: Um objeto digital (como um JWT – JSON Web Token) que representa a identidade verificada do usuário e é usado para subsequentes requisições sem a necessidade de enviar credenciais repetidamente.

  • Casos de Uso Reais:

Um usuário inserindo nome de usuário e senha para fazer login em um e-commerce.
Um aplicativo mobile usando um token de acesso para interagir com uma API RESTful.
Um serviço de backend autenticando outro serviço via chave de API secreta.

  • Como se Integra:

Frequentemente utiliza protocolos como OAuth2 e OpenID Connect para delegação de autenticação e obtenção de tokens.
Em Node.js/Express, bibliotecas como Passport.js ou jsonwebtoken são fundamentais para implementar diferentes estratégias de autenticação.

  • Vantagens e Desvantagens:

Vantagens: Estabelece a confiança inicial, protege contra acessos anônimos e não autorizados, é a base para a autorização.
Desvantagens: Não especifica o que o usuário pode fazer, pode ser complexo gerenciar diferentes métodos de autenticação, vulnerável a ataques de força bruta ou phishing se mal implementado.

Autorização: “O que você pode fazer?”

Uma vez que a identidade de um usuário foi comprovada (autenticada), a Autorização entra em ação. É o processo de determinar se essa identidade autenticada tem permissão para realizar uma ação específica ou acessar um recurso particular. Ela responde à pergunta crucial: “Você está permitido a fazer isso?”.

  • Terminologia da Indústria:

Papel (Role): Um conjunto de permissões atribuídas a um tipo de usuário (ex: ‘admin’, ‘editor’, ‘usuário comum’).
Permissão (Permission): O direito de executar uma ação específica (ex: ‘criar_post’, ‘deletar_usuario’, ‘ver_relatorio_financeiro’).
Recurso (Resource): O objeto ou dado ao qual o acesso está sendo controlado (ex: POST /api/produtos, GET /api/pedidos/{id}).
Controle de Acesso Baseado em Papéis (RBAC - Role-Based Access Control): Estratégia comum onde permissões são atribuídas a papéis, e usuários são atribuídos a papéis.
Controle de Acesso Baseado em Atributos (ABAC - Attribute-Based Access Control): Estratégia mais granular que usa atributos do usuário, do recurso e do ambiente para determinar o acesso.

  • Casos de Uso Reais:

Um usuário comum não consegue acessar a página de administração de um site.
Apenas administradores podem excluir contas de outros usuários.
Um editor pode publicar artigos, mas não pode alterar as configurações do sistema.
Um membro VIP acessa conteúdo exclusivo que não está disponível para membros padrão.

  • Como se Integra:

Geralmente implementada após a autenticação, usando as informações da identidade (como o papel do usuário no token JWT) para tomar decisões de permissão.
Em Node.js/Express, middlewares customizados são a maneira padrão de implementar lógicas de autorização.

  • Vantagens e Desvantagens:

Vantagens: Oferece controle de acesso granular e flexível, garante que usuários só interajam com recursos aos quais têm direito, aumenta a segurança da aplicação.
Desvantagens: Pode se tornar complexo de gerenciar em sistemas com muitas permissões ou papéis, requer design cuidadoso para evitar brechas de segurança, pode adicionar sobrecarga de processamento se as regras forem muito complexas.

A compreensão profunda e a implementação correta de ambos os conceitos são vitais para o desenvolvimento de qualquer API moderna e segura.

Implementação Prática (10 min)

Vamos agora construir um exemplo funcional que ilustra como Autenticação e Autorização são implementadas em uma aplicação Node.js com Express. Este código roda imediatamente e segue melhores práticas enterprise, além de ser compatível com ambientes como o HostGator Plano M.

Primeiro, crie um novo projeto Node.js e instale as dependências:

mkdir auth-authz-api
cd auth-authz-api
npm init -y
npm install express jsonwebtoken bcrypt body-parser

Crie um arquivo chamado server.js e insira o código abaixo.

// server.js

// Importações de módulos essenciais const express = require('express'); const jwt = require('jsonwebtoken'); // Para trabalhar com JSON Web Tokens (JWT) const bcrypt = require('bcrypt'); // Para hashing e comparação de senhas de forma segura const bodyParser = require('body-parser'); // Para analisar o corpo das requisições JSON

// --- Configurações Iniciais da Aplicação --- const app = express(); // Define a porta do servidor, preferindo a variável de ambiente (HostGator) ou 3000 const PORT = process.env.PORT || 3000; // Chave secreta para assinar e verificar JWTs. VITAL que seja uma variável de ambiente em produção! const JWT_SECRET = process.env.JWT_SECRET || 'umaChaveSuperSecretaEforteParaDesenvolvimento';

// Middleware para analisar requisições com corpo JSON app.use(bodyParser.json());

// --- Simulação de Banco de Dados de Usuários (em memória para simplicidade da aula) --- // Em uma aplicação real, estes dados viriam de um banco de dados persistente. // As senhas são HASHEADAS para segurança. NUNCA armazene senhas em texto puro! const users = [ { id: 1, username: 'user1', passwordHash: bcrypt.hashSync('senha123', 10), role: 'user' }, { id: 2, username: 'admin1', passwordHash: bcrypt.hashSync('admin123', 10), role: 'admin' }, { id: 3, username: 'editor1', passwordHash: bcrypt.hashSync('editor123', 10), role: 'editor' }, ];

// --- Função de Logging Profissional --- // Uma função simples para registrar eventos com nível de severidade e timestamp. // Em produção, você usaria uma biblioteca como Winston ou Pino. const log = (level, message, data = {}) => { // Formata a data/hora para o registro const timestamp = new Date().toISOString(); // Exibe no console, incluindo dados adicionais se fornecidos console.log([${timestamp}] [${level.toUpperCase()}] ${message}, data); };

// --- Middleware de Autenticação (Verificação de Token JWT) --- // Este middleware é responsável por verificar se uma requisição possui um JWT válido. const authenticateToken = (req, res, next) => { // Tenta obter o cabeçalho 'Authorization' da requisição. // Espera-se o formato "Bearer SEU_TOKEN_AQUI". const authHeader = req.headers['authorization']; // Extrai o token da string 'Bearer '. Se não houver, 'token' será null. const token = authHeader && authHeader.split(' ')[1];

// Se não há token, o usuário não está autenticado. Retorna 401 (Unauthorized). if (token == null) { log('warn', 'Acesso negado: Token JWT não fornecido.', { ip: req.ip }); return res.status(401).json({ message: 'Acesso negado. Token JWT não fornecido.' }); }

// Verifica a validade do token usando a chave secreta. jwt.verify(token, JWT_SECRET, (err, user) => { // Se houver erro (token inválido, expirado, etc.), retorna 403 (Forbidden). if (err) { log('error', 'Acesso negado: Token JWT inválido ou expirado.', { error: err.message, ip: req.ip }); return res.status(403).json({ message: 'Acesso negado. Token JWT inválido ou expirado.' }); } // Se o token for válido, anexa as informações do usuário (payload do JWT) ao objeto de requisição. // Isso permite que as rotas subsequentes saibam "quem" está fazendo a requisição. req.user = user; log('info', Usuário autenticado: ${user.username} (ID: ${user.id}, Papel: ${user.role})); // Chama o próximo middleware ou a função da rota. next(); }); };

// --- Middleware de Autorização (Baseado em Papéis - RBAC) --- // Este middleware verifica se o usuário autenticado (req.user) possui um dos papéis necessários. const authorizeRoles = (allowedRoles) => { // Retorna uma função de middleware, permitindo passar os papéis esperados. return (req, res, next) => { // Garante que o usuário foi previamente autenticado e possui um papel. if (!req.user || !req.user.role) { log('warn', 'Autorização falhou: Usuário não autenticado ou sem papel definido.', { ip: req.ip }); return res.status(403).json({ message: 'Acesso negado. Usuário sem papel definido ou não autenticado.' }); } // Verifica se o papel do usuário autenticado está na lista de papéis permitidos. if (!allowedRoles.includes(req.user.role)) { log('warn', Autorização falhou para usuário '${req.user.username}': Papel '${req.user.role}' não permitido., { requiredRoles: allowedRoles, ip: req.ip }); return res.status(403).json({ message: Acesso negado. Você não possui as permissões necessárias. Seu papel é '${req.user.role}'. }); } // Se o papel for permitido, chama o próximo middleware ou a função da rota. log('info', Usuário '${req.user.username}' autorizado com papel: ${req.user.role}); next(); }; };

// --- Rotas da API ---

// Rota de Login: Gera um token JWT após autenticação bem-sucedida. app.post('/api/login', async (req, res) => { const { username, password } = req.body;

// 1. Validação de Entrada: Garante que os campos necessários foram fornecidos. if (!username || !password) { log('warn', 'Tentativa de login com credenciais incompletas.', { ip: req.ip }); return res.status(400).json({ message: 'Por favor, forneça nome de usuário e senha.' }); }

// 2. Encontrar Usuário: Procura o usuário no "banco de dados" simulado. const user = users.find(u => u.username === username); if (!user) { log('warn', Tentativa de login falhou: Usuário '${username}' não encontrado., { ip: req.ip }); return res.status(401).json({ message: 'Credenciais inválidas.' }); }

// 3. Verificar Senha: Compara a senha fornecida com o hash armazenado. const isPasswordValid = await bcrypt.compare(password, user.passwordHash); if (!isPasswordValid) { log('warn', Tentativa de login falhou para '${username}': Senha incorreta., { ip: req.ip }); return res.status(401).json({ message: 'Credenciais inválidas.' }); }

// 4. Gerar Token JWT: Se autenticação bem-sucedida, cria um token. // O payload do token inclui informações essenciais como id, username e role. const token = jwt.sign({ id: user.id, username: user.username, role: user.role }, JWT_SECRET, { expiresIn: '1h' }); log('info', Usuário '${username}' logado com sucesso. Token gerado., { userId: user.id, ip: req.ip }); // Retorna o token para o cliente, que o usará em requisições futuras. res.json({ message: 'Login bem-sucedido!', token: token }); });

// Rota Pública: Não exige autenticação nem autorização. app.get('/api/public', (req, res) => { log('info', 'Acesso à rota pública.', { ip: req.ip }); res.json({ message: 'Esta é uma rota pública. Qualquer um pode acessá-la sem autenticação.' }); });

// Rota para Usuários Autenticados: Exige autenticação, mas qualquer papel pode acessar. // Usa o middleware authenticateToken. app.get('/api/users/profile', authenticateToken, (req, res) => { log('info', Acesso ao perfil do usuário '${req.user.username}'., { userId: req.user.id, ip: req.ip }); res.json({ message: Bem-vindo, ${req.user.username}! Seu ID é ${req.user.id} e seu papel é ${req.user.role}., user: req.user }); });

// Rota para Editores/Administradores: Exige autenticação E que o papel seja 'editor' ou 'admin'. // Usa authenticateToken e authorizeRoles(['editor', 'admin']). app.post('/api/content/publish', authenticateToken, authorizeRoles(['editor', 'admin']), (req, res) => { log('info', Usuário '${req.user.username}' publicou conteúdo., { userId: req.user.id, ip: req.ip }); res.json({ message: Conteúdo publicado com sucesso por ${req.user.username} (${req.user.role}). }); });

// Rota de Administração: Exige autenticação E que o papel seja 'admin'. // Usa authenticateToken e authorizeRoles(['admin']). app.delete('/api/admin/users/:id', authenticateToken, authorizeRoles(['admin']), (req, res) => { const userIdToDelete = parseInt(req.params.id);

// Exemplo de regra de negócio adicional para autorização: um admin não pode deletar a si mesmo. if (req.user.id === userIdToDelete) { log('warn', Tentativa de exclusão de auto-usuário por '${req.user.username}'., { userId: req.user.id, targetUserId: userIdToDelete, ip: req.ip }); return res.status(403).json({ message: 'Ação não permitida: Você não pode excluir seu próprio usuário.' }); } // Lógica real para deletar o usuário do banco de dados (aqui, apenas simulado). log('info', Usuário '${req.user.username}' (Admin) deletou usuário com ID: ${userIdToDelete}., { userId: req.user.id, targetUserId: userIdToDelete, ip: req.ip }); res.json({ message: Usuário com ID ${userIdToDelete} deletado com sucesso pelo administrador ${req.user.username}. }); });

// --- Middleware de Tratamento de Erros Global --- // Captura erros que ocorrerem em qualquer lugar da aplicação Express. // VITAL para uma aplicação robusta em produção. app.use((err, req, res, next) => { log('error', 'Erro interno do servidor. Capturado pelo middleware de erro.', { error: err.message, stack: err.stack, path: req.path, method: req.method, ip: req.ip }); res.status(500).json({ message: 'Ocorreu um erro inesperado no servidor. Por favor, tente novamente mais tarde.' }); });

// --- Inicialização do Servidor --- app.listen(PORT, () => { log('info', Servidor Express rodando na porta ${PORT}); log('info', 'Instruções para testar:'); log('info', '1. Acesse http://localhost:3000/api/public no navegador.'); log('info', '2. Use ferramentas como Postman ou curl para as outras rotas.'); });

Para rodar o servidor:

node server.js

Você verá as mensagens de log no console, indicando que o servidor está ativo.

Configurações Específicas para HostGator Plano M

  • Variáveis de Ambiente: No HostGator, você configuraria a JWT_SECRET e PORT através do painel de controle ou .htaccess (se for cPanel) para que process.env.JWT_SECRET e process.env.PORT sejam populados corretamente. Nunca deixe chaves secretas diretamente no código em produção.
  • Dependências: As dependências express, jsonwebtoken, bcrypt, body-parser são padrões Node.js e devem ser instaláveis via npm install no ambiente do HostGator, desde que o Node.js esteja configurado. bcrypt possui dependências nativas, mas geralmente os ambientes de hospedagem já vêm com os compiladores necessários.
  • Porta: A porta process.env.PORT garante que o servidor escute na porta que o ambiente de hospedagem designar, o que é um padrão crucial para deployments.

Testes Básicos (com curl ou Postman/Insomnia)

  • Rota Pública (sem autenticação):
    curl http://localhost:3000/api/public
    # Saída esperada: {"message":"Esta é uma rota pública. Qualquer um pode acessá-la sem autenticação."}

  • Login (para obter um token):

Use as credenciais admin1/admin123.

    curl -X POST -H "Content-Type: application/json" -d '{"username": "admin1", "password": "admin123"}' http://localhost:3000/api/login
    # Saída esperada: {"message":"Login bem-sucedido!","token":"SEU_TOKEN_JWT_AQUI"}

Copie o SEU_TOKEN_JWT_AQUI.

  • Acesso ao Perfil (requer autenticação):

Substitua SEU_TOKEN_AQUI pelo token obtido no login.

    curl -H "Authorization: Bearer SEU_TOKEN_AQUI" http://localhost:3000/api/users/profile
    # Saída esperada: {"message":"Bem-vindo, admin1! Seu ID é 2 e seu papel é admin.","user":{"id":2,"username":"admin1","role":"admin"}}

  • Publicar Conteúdo (requer papel ‘editor’ ou ‘admin’):

Com token de admin1 (que tem papel ‘admin’):

        curl -X POST -H "Authorization: Bearer SEU_TOKEN_DE_ADMIN1" http://localhost:3000/api/content/publish
        # Saída esperada: {"message":"Conteúdo publicado com sucesso por admin1 (admin)."}

Com token de user1 (que tem papel ‘user’ – deve falhar):

        curl -X POST -H "Authorization: Bearer SEU_TOKEN_DE_USER1" http://localhost:3000/api/content/publish
        # Saída esperada: {"message":"Acesso negado. Você não possui as permissões necessárias. Seu papel é 'user'."} (Status 403)

  • Deletar Usuário (requer papel ‘admin’):

Com token de admin1 tentando deletar user1 (ID 1):

        curl -X DELETE -H "Authorization: Bearer SEU_TOKEN_DE_ADMIN1" http://localhost:3000/api/admin/users/1
        # Saída esperada: {"message":"Usuário com ID 1 deletado com sucesso pelo administrador admin1."}

Com token de admin1 tentando deletar a si mesmo (ID 2):

        curl -X DELETE -H "Authorization: Bearer SEU_TOKEN_DE_ADMIN1" http://localhost:3000/api/admin/users/2
        # Saída esperada: {"message":"Ação não permitida: Você não pode excluir seu próprio usuário."} (Status 403)

Este exemplo demonstra de forma clara e prática a distinção e a colaboração entre Autenticação e Autorização, aplicando padrões enterprise desde o primeiro dia.

Exercício Hands-On (5 min)

Agora é sua vez de solidificar o aprendizado com um desafio prático!

Desafio: Adicione uma nova rota à API existente que se comporte da seguinte forma:

  • A rota deve ser /api/financial/report.
  • Apenas usuários autenticados com o papel admin ou editor devem ter acesso a esta rota.
  • Dentro desta rota, se o usuário for um admin, ele deve receber um relatório financeiro “completo” (simulado).
  • Se o usuário for um editor, ele deve receber um relatório financeiro “simplificado” (simulado), sem dados considerados sensíveis aos administradores.

Solução Detalhada Passo a Passo:

  • Abra o arquivo server.js que você criou.
  • Adicione a nova rota abaixo das rotas existentes, antes do middleware de tratamento de erros global:
    // Nova Rota para Relatório Financeiro
    app.get('/api/financial/report', authenticateToken, authorizeRoles(['admin', 'editor']), (req, res) => {
        // Asseguramos que chegamos aqui, o usuário está autenticado e é admin ou editor.
        log('info', Usuário '${req.user.username}' acessando relatório financeiro., { userId: req.user.id, ip: req.ip });

let reportData; // Lógica de autorização interna para diferenciar o tipo de relatório if (req.user.role === 'admin') { reportData = { title: 'Relatório Financeiro Completo', date: new Date().toISOString(), totalRevenue: 1250000.75, totalExpenses: 800000.50, netProfit: 450000.25, sensitiveData: 'Informações detalhadas de contas bancárias e investimentos.' }; log('info', Admin '${req.user.username}' recebeu relatório completo., { userId: req.user.id, ip: req.ip }); } else { // Deve ser 'editor' devido ao authorizeRoles reportData = { title: 'Relatório Financeiro Simplificado', date: new Date().toISOString(), totalRevenue: 1250000.75, totalExpenses: 800000.50, summary: 'Visão geral das finanças da empresa.' }; log('info', Editor '${req.user.username}' recebeu relatório simplificado., { userId: req.user.id, ip: req.ip }); } res.json({ message: 'Relatório financeiro gerado com sucesso!', report: reportData }); });

  • Salve o arquivo e reinicie o servidor Node.js (Ctrl+C e node server.js).

Como Testar e Validar o Resultado:

Utilize curl ou seu cliente HTTP favorito (Postman, Insomnia) para testar esta nova rota com diferentes tokens.

  • Obtenha o token para admin1:
    curl -X POST -H "Content-Type: application/json" -d '{"username": "admin1", "password": "admin123"}' http://localhost:3000/api/login
    # Copie o token do admin1

Teste com admin1:

    curl -H "Authorization: Bearer SEU_TOKEN_DE_ADMIN1" http://localhost:3000/api/financial/report
    # Saída esperada: Relatório completo, incluindo 'sensitiveData'.

  • Obtenha o token para editor1:
    curl -X POST -H "Content-Type: application/json" -d '{"username": "editor1", "password": "editor123"}' http://localhost:3000/api/login
    # Copie o token do editor1

Teste com editor1:

    curl -H "Authorization: Bearer SEU_TOKEN_DE_EDITOR1" http://localhost:3000/api/financial/report
    # Saída esperada: Relatório simplificado, SEM 'sensitiveData'.

  • Obtenha o token para user1:
    curl -X POST -H "Content-Type: application/json" -d '{"username": "user1", "password": "senha123"}' http://localhost:3000/api/login
    # Copie o token do user1

Teste com user1 (deve falhar na autorização):

    curl -H "Authorization: Bearer SEU_TOKEN_DE_USER1" http://localhost:3000/api/financial/report
    # Saída esperada: {"message":"Acesso negado. Você não possui as permissões necessárias. Seu papel é 'user'."} (Status 403)

  • Teste sem token (deve falhar na autenticação):
    curl http://localhost:3000/api/financial/report
    # Saída esperada: {"message":"Acesso negado. Token JWT não fornecido."} (Status 401)

Troubleshooting dos Erros Mais Comuns:

  • 401 Unauthorized: Significa que o token JWT não foi fornecido (ou está faltando o prefixo Bearer ), ou é completamente inválido. Verifique se o cabeçalho Authorization está correto.
  • 403 Forbidden: Indica que o token é válido e o usuário está autenticado, mas ele não possui o papel ou as permissões necessárias para acessar aquele recurso. Revise a lista de allowedRoles no middleware authorizeRoles e o papel do usuário no token.
  • 400 Bad Request: Geralmente ocorre quando o corpo da requisição de login está malformado ou faltando dados (username, password).
  • 500 Internal Server Error: Um erro inesperado no servidor. Verifique o console do seu servidor Node.js para os logs de erro detalhados, eles são seu melhor amigo!

Próximos Passos Sugeridos:

Para continuar sua jornada de domínio em segurança de APIs:

  • Integre um Banco de Dados Real: Substitua a simulação de usuários em memória por um banco de dados persistente (MongoDB com Mongoose, PostgreSQL com Sequelize/Prisma) para gerenciar usuários e papéis.
  • Refresh Tokens: Implemente um sistema de refresh tokens para melhorar a experiência do usuário e a segurança, permitindo que os tokens de acesso tenham vida útil curta.
  • OAuth2 e OpenID Connect: Explore a implementação de protocolos de autenticação e autorização mais avançados para delegação de acesso e federação de identidades.
  • Permissões Mais Granulares: Em vez de apenas papéis, crie um sistema de permissões mais detalhado onde cada papel pode ter múltiplas permissões específicas (ex: admin tem can_delete_users, can_view_sensitive_reports).
  • Testes de Integração e Unidade: Desenvolva testes automatizados para seus middlewares de autenticação e autorização, garantindo que funcionem conforme o esperado em todas as situações.

Parabéns! Você não apenas dominou os conceitos de Autenticação e Autorização, mas também os aplicou de maneira prática e robusta. Este é um conhecimento inestimável para qualquer desenvolvedor de APIs.

🚀 Pronto para a próxima aula?

Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!

📚 Ver todas as aulas