Inicio / Trilha 3 / Modulo 3.5
Trilha 3 - Avancado Seguranca

🔐 Autenticacao e Seguranca

Proteja seus dashboards enterprise com autenticacao robusta, controle de acesso granular e auditoria completa de acoes.

6
Topicos
4h
Duracao
Expert
Nivel
Critico
Importancia
1

🔑 OAuth2 e OpenID Connect

Fluxos de autenticacao modernos para aplicacoes enterprise

📋 O que e OAuth2/OIDC

OAuth2 e um protocolo de autorizacao que permite que aplicacoes obtenham acesso limitado a contas de usuarios sem expor credenciais. OpenID Connect (OIDC) e uma camada de identidade sobre OAuth2 que adiciona autenticacao, permitindo verificar a identidade do usuario.

OAuth2
  • • Autoriza acesso a recursos
  • • Access Token para APIs
  • • Refresh Token para renovacao
  • • Scopes definem permissoes
OIDC
  • • Autentica identidade do usuario
  • • ID Token com claims do usuario
  • • UserInfo endpoint
  • • Discovery e JWKS endpoints

🔄 Fluxos de Autorizacao

Authorization Code + PKCE (Recomendado)

Mais seguro para SPAs e apps mobile. PKCE previne ataques de interceptacao.

1. App gera code_verifier e code_challenge
2. Usuario autoriza no IdP
3. IdP retorna authorization_code
4. App troca code + verifier por tokens
Client Credentials

Para comunicacao machine-to-machine (M2M). Nao envolve usuario.

Device Authorization

Para dispositivos sem browser (TVs, CLIs, IoT).

Implicit (Deprecado)

Inseguro - tokens expostos na URL. Use Authorization Code + PKCE.

💻 Codigo: Implementacao PKCE

// Geracao PKCE
function generatePKCE() {
  const verifier = crypto.randomBytes(32).toString('base64url');
  const challenge = crypto
    .createHash('sha256')
    .update(verifier)
    .digest('base64url');
  return { verifier, challenge };
}

// Inicio do fluxo
const { verifier, challenge } = generatePKCE();
sessionStorage.setItem('pkce_verifier', verifier);

const authUrl = new URL('https://auth.empresa.com/authorize');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('code_challenge', challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('state', crypto.randomUUID());

window.location.href = authUrl.toString();

// Callback - troca do code por tokens
async function handleCallback(code: string) {
  const verifier = sessionStorage.getItem('pkce_verifier');

  const response = await fetch('https://auth.empresa.com/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: CLIENT_ID,
      redirect_uri: REDIRECT_URI,
      code,
      code_verifier: verifier!,
    }),
  });

  const { access_token, id_token, refresh_token } = await response.json();
  // Armazenar tokens de forma segura
}

🏢 Identity Providers Populares

🔵
Auth0
Enterprise
🟠
Okta
Enterprise
🔷
Azure AD
Microsoft
🔴
Keycloak
Open Source
2

🎫 JWT e Gestao de Sessions

Tokens seguros e gerenciamento de sessoes de usuario

📋 Anatomia do JWT

JSON Web Token (JWT) e um formato compacto e seguro para transmitir informacoes entre partes. Consiste em tres partes separadas por pontos: Header, Payload e Signature.

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header
{"alg": "RS256", "typ": "JWT"}
Payload (Claims)
{"sub": "123", "role": "admin"}
Signature
HMAC ou RSA do header+payload

⚖️ JWT vs Sessions Tradicionais

🎫 JWT (Stateless)
  • Escalavel horizontalmente
  • Sem estado no servidor
  • Funciona entre dominios
  • Dificil invalidar antes do expiry
  • Payload visivel (nao criptografado)
🍪 Sessions (Stateful)
  • Revogacao imediata
  • Dados sensiveis no servidor
  • Controle total de sessoes
  • Requer storage centralizado
  • Mais complexo escalar

💻 Codigo: Token Refresh Automatico

// Token Manager com Refresh Automatico
class TokenManager {
  private accessToken: string | null = null;
  private refreshToken: string | null = null;
  private refreshPromise: Promise<void> | null = null;

  async getAccessToken(): Promise<string> {
    if (this.isTokenValid()) {
      return this.accessToken!;
    }

    // Evita multiplas chamadas de refresh simultaneas
    if (!this.refreshPromise) {
      this.refreshPromise = this.refreshAccessToken();
    }

    await this.refreshPromise;
    this.refreshPromise = null;
    return this.accessToken!;
  }

  private isTokenValid(): boolean {
    if (!this.accessToken) return false;

    const payload = JSON.parse(atob(this.accessToken.split('.')[1]));
    const expiresAt = payload.exp * 1000;
    const bufferMs = 60000; // 1 min de buffer

    return Date.now() < expiresAt - bufferMs;
  }

  private async refreshAccessToken(): Promise<void> {
    const response = await fetch('/api/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: this.refreshToken }),
    });

    if (!response.ok) {
      this.logout();
      throw new Error('Session expired');
    }

    const { access_token, refresh_token } = await response.json();
    this.accessToken = access_token;
    this.refreshToken = refresh_token;
  }
}

// Axios Interceptor para refresh automatico
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401 && !error.config._retry) {
      error.config._retry = true;
      const token = await tokenManager.getAccessToken();
      error.config.headers.Authorization = `Bearer ${token}`;
      return axios(error.config);
    }
    return Promise.reject(error);
  }
);

💡 Dica de Seguranca

Nunca armazene JWTs no localStorage para aplicacoes sensiveis - use httpOnly cookies para o refresh token e mantenha o access token apenas em memoria. Isso protege contra ataques XSS.

3

🏢 SSO Empresarial

Single Sign-On com Active Directory, SAML e integracao corporativa

📋 O que e SSO

Single Sign-On permite que usuarios autentiquem uma vez e acessem multiplos sistemas sem login repetido. Em ambientes corporativos, geralmente integra com Active Directory (AD) ou outros Identity Providers via protocolos como SAML, OIDC ou LDAP.

🔷
Azure AD / Entra ID
Microsoft cloud identity
📁
Active Directory
On-premise Windows
🔐
LDAP
Protocolo directory

📊 SAML vs OpenID Connect

Aspecto SAML 2.0 OIDC
Formato XML (verboso) JSON (compacto)
Transporte HTTP POST/Redirect REST APIs
Ideal para Enterprise legado Apps modernas, mobile
Complexidade Alta Media
Suporte Corporacoes tradicionais Providers modernos

🔄 Fluxo SAML SP-Initiated

1
Usuario acessa dashboard (Service Provider)
2
SP gera SAML AuthnRequest e redireciona para IdP
3
Usuario autentica no IdP (Active Directory)
4
IdP envia SAML Response assinado para SP (ACS URL)
5
SP valida assinatura, extrai atributos e cria sessao

💻 Codigo: Integracao Azure AD (MSAL)

// Configuracao MSAL para Azure AD
import { PublicClientApplication, Configuration } from '@azure/msal-browser';

const msalConfig: Configuration = {
  auth: {
    clientId: 'SEU_CLIENT_ID',
    authority: 'https://login.microsoftonline.com/SEU_TENANT_ID',
    redirectUri: 'https://dashboard.empresa.com/callback',
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: false,
  },
};

const msalInstance = new PublicClientApplication(msalConfig);

// Scopes para Graph API
const loginRequest = {
  scopes: ['User.Read', 'GroupMember.Read.All'],
};

// Login
async function signIn() {
  try {
    const response = await msalInstance.loginPopup(loginRequest);
    console.log('Usuario:', response.account?.username);

    // Obter token para API
    const tokenResponse = await msalInstance.acquireTokenSilent({
      scopes: ['api://SEU_API_ID/.default'],
      account: response.account!,
    });

    return tokenResponse.accessToken;
  } catch (error) {
    console.error('Erro de login:', error);
    throw error;
  }
}

// Obter grupos do usuario para RBAC
async function getUserGroups(token: string) {
  const response = await fetch('https://graph.microsoft.com/v1.0/me/memberOf', {
    headers: { Authorization: `Bearer ${token}` },
  });

  const data = await response.json();
  return data.value
    .filter((item: any) => item['@odata.type'] === '#microsoft.graph.group')
    .map((group: any) => group.displayName);
}
4

👥 RBAC - Role-Based Access Control

Controle de acesso granular baseado em papeis e permissoes

📋 Modelo RBAC

Role-Based Access Control atribui permissoes a roles (papeis), e usuarios recebem roles. Isso simplifica a gestao de acesso - ao inves de gerenciar permissoes individuais por usuario, voce gerencia roles.

👤
Usuario
🎭
Role
🔓
Permissoes

🏛️ Exemplo: Hierarquia de Roles para Dashboard

🔴 Super Admin

Acesso total ao sistema

*
🟠 Admin

Gerencia usuarios, configs, dashboards

users:*, config:*, dashboard:*
🟡 Manager

Cria dashboards, ve todos os dados

dashboard:create, data:read:all
🟢 Analyst

Ve dashboards do seu departamento

dashboard:read:own, data:read:dept
🔵 Viewer

Apenas visualiza dashboards compartilhados

dashboard:read:shared

💻 Codigo: Sistema RBAC Completo

// Definicao de permissoes
type Permission =
  | 'dashboard:create' | 'dashboard:read' | 'dashboard:update' | 'dashboard:delete'
  | 'data:read:all' | 'data:read:dept' | 'data:export'
  | 'users:read' | 'users:manage'
  | 'config:read' | 'config:update';

type Role = 'super_admin' | 'admin' | 'manager' | 'analyst' | 'viewer';

// Matriz de permissoes por role
const rolePermissions: Record<Role, Permission[]> = {
  super_admin: ['*'] as any, // Todas as permissoes
  admin: [
    'dashboard:create', 'dashboard:read', 'dashboard:update', 'dashboard:delete',
    'data:read:all', 'data:export',
    'users:read', 'users:manage',
    'config:read', 'config:update',
  ],
  manager: [
    'dashboard:create', 'dashboard:read', 'dashboard:update',
    'data:read:all', 'data:export',
    'users:read',
  ],
  analyst: [
    'dashboard:read',
    'data:read:dept',
  ],
  viewer: [
    'dashboard:read',
  ],
};

// Verificador de permissao
class PermissionChecker {
  constructor(private user: { roles: Role[]; department?: string }) {}

  hasPermission(permission: Permission): boolean {
    return this.user.roles.some(role => {
      const perms = rolePermissions[role];
      return perms.includes('*' as any) || perms.includes(permission);
    });
  }

  canAccessDepartment(deptId: string): boolean {
    if (this.hasPermission('data:read:all')) return true;
    return this.user.department === deptId;
  }

  canAccessDashboard(dashboard: { ownerId: string; shared: boolean; deptId: string }): boolean {
    if (this.hasPermission('dashboard:read') && this.hasPermission('data:read:all')) {
      return true;
    }
    if (dashboard.shared) return true;
    return this.canAccessDepartment(dashboard.deptId);
  }
}

// Middleware Express
const requirePermission = (permission: Permission) => {
  return (req: Request, res: Response, next: NextFunction) => {
    const checker = new PermissionChecker(req.user);

    if (!checker.hasPermission(permission)) {
      return res.status(403).json({
        error: 'Forbidden',
        required: permission,
      });
    }

    next();
  };
};

// Uso
app.post('/api/dashboards',
  requirePermission('dashboard:create'),
  createDashboardHandler
);

✅ Fazer vs ❌ Evitar

✅ Fazer
  • • Principio do menor privilegio
  • • Revisar permissoes periodicamente
  • • Usar grupos/roles, nao permissoes diretas
  • • Implementar heranca de roles
  • • Logar todas as mudancas de acesso
❌ Evitar
  • • Permissoes hardcoded no codigo
  • • Verificacao apenas no frontend
  • • Roles muito amplas (God mode)
  • • Ignorar separacao de ambientes
  • • Nao ter processo de offboarding
5

🛡️ Protecao de Rotas

Guards, middlewares e protecao em multiplas camadas

📋 Defesa em Profundidade

Protecao de rotas deve acontecer em multiplas camadas - frontend, API gateway, backend e banco de dados. Nunca confie apenas na verificacao do frontend.

🖥️
Frontend
Route Guards, UI hiding
🚪
API Gateway
Rate limiting, Auth
⚙️
Backend
Middlewares, RBAC
🗄️
Database
RLS, Row filters

💻 Codigo: React Route Guards

// ProtectedRoute component
interface ProtectedRouteProps {
  children: React.ReactNode;
  requiredPermissions?: Permission[];
  requiredRoles?: Role[];
  fallback?: React.ReactNode;
}

function ProtectedRoute({
  children,
  requiredPermissions = [],
  requiredRoles = [],
  fallback = <Navigate to="/unauthorized" />
}: ProtectedRouteProps) {
  const { user, isAuthenticated, isLoading } = useAuth();

  if (isLoading) {
    return <LoadingSpinner />;
  }

  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} />;
  }

  const hasRequiredRoles = requiredRoles.length === 0 ||
    requiredRoles.some(role => user.roles.includes(role));

  const hasRequiredPermissions = requiredPermissions.length === 0 ||
    requiredPermissions.every(perm => user.permissions.includes(perm));

  if (!hasRequiredRoles || !hasRequiredPermissions) {
    return fallback;
  }

  return children;
}

// Uso nas rotas
<Routes>
  <Route path="/login" element={<LoginPage />} />

  <Route element={<ProtectedRoute><MainLayout /></ProtectedRoute>}>
    <Route path="/dashboard" element={<DashboardPage />} />

    <Route
      path="/admin"
      element={
        <ProtectedRoute requiredRoles={['admin', 'super_admin']}>
          <AdminPage />
        </ProtectedRoute>
      }
    />

    <Route
      path="/reports/export"
      element={
        <ProtectedRoute requiredPermissions={['data:export']}>
          <ExportPage />
        </ProtectedRoute>
      }
    />
  </Route>

  <Route path="/unauthorized" element={<UnauthorizedPage />} />
</Routes>

💻 Codigo: Backend Middleware Chain

// Middleware de autenticacao
const authenticate = async (req: Request, res: Response, next: NextFunction) => {
  const authHeader = req.headers.authorization;

  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Token required' });
  }

  try {
    const token = authHeader.split(' ')[1];
    const payload = await verifyJWT(token);

    req.user = {
      id: payload.sub,
      email: payload.email,
      roles: payload.roles,
      permissions: await getPermissionsForRoles(payload.roles),
    };

    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

// Middleware de rate limiting por usuario
const rateLimitByUser = rateLimit({
  windowMs: 60 * 1000, // 1 minuto
  max: 100,
  keyGenerator: (req) => req.user?.id || req.ip,
  handler: (req, res) => {
    res.status(429).json({ error: 'Too many requests' });
  },
});

// Middleware de audit logging
const auditLog = (action: string) => {
  return (req: Request, res: Response, next: NextFunction) => {
    const originalSend = res.send;

    res.send = function(body) {
      logger.info({
        action,
        userId: req.user?.id,
        method: req.method,
        path: req.path,
        statusCode: res.statusCode,
        ip: req.ip,
        userAgent: req.headers['user-agent'],
        timestamp: new Date().toISOString(),
      });

      return originalSend.call(this, body);
    };

    next();
  };
};

// Aplicacao em cascata
app.use('/api', authenticate);
app.use('/api', rateLimitByUser);
app.use('/api/admin', requireRole('admin'));
app.use('/api/admin', auditLog('admin_action'));

🗄️ Row Level Security (PostgreSQL)

-- Habilitar RLS na tabela
ALTER TABLE dashboards ENABLE ROW LEVEL SECURITY;

-- Policy: usuarios veem apenas dashboards do seu departamento
CREATE POLICY department_isolation ON dashboards
  FOR SELECT
  USING (
    department_id = current_setting('app.current_department')::uuid
    OR shared = true
    OR created_by = current_setting('app.current_user')::uuid
  );

-- Policy: apenas admins podem deletar
CREATE POLICY admin_delete ON dashboards
  FOR DELETE
  USING (
    current_setting('app.current_role') IN ('admin', 'super_admin')
  );

-- Setar contexto na conexao (via aplicacao)
-- No backend, antes de cada query:
SET LOCAL app.current_user = 'user-uuid-here';
SET LOCAL app.current_department = 'dept-uuid-here';
SET LOCAL app.current_role = 'analyst';
6

📜 Audit Logs e Rastreabilidade

Registre todas as acoes para compliance, seguranca e debugging

📋 Por que Audit Logs

Audit logs sao registros imutaveis de todas as acoes significativas no sistema. Sao essenciais para compliance (LGPD, SOX, HIPAA), investigacao de incidentes de seguranca, e debugging de problemas em producao.

🛡️ Seguranca
  • • Detectar acessos suspeitos
  • • Identificar vazamentos
  • • Forense de incidentes
📋 Compliance
  • • LGPD - quem acessou dados
  • • SOX - controles financeiros
  • • ISO 27001 - auditoria
🔧 Operacional
  • • Debug de problemas
  • • Analise de uso
  • • Rollback de acoes

📝 O que Registrar

Sempre Logar:
✓ Login/Logout
✓ Falhas de autenticacao
✓ Mudancas de permissao
✓ Acesso a dados sensiveis
✓ Exportacao de dados
✓ Alteracoes de configuracao
Nunca Logar:
✗ Senhas (mesmo hasheadas)
✗ Tokens de acesso
✗ Dados de cartao de credito
✗ PII completo desnecessario
✗ Secrets/API keys
✗ Health check requests

💻 Codigo: Sistema de Audit Log

// Schema do Audit Log
interface AuditLog {
  id: string;
  timestamp: Date;

  // Quem
  userId: string;
  userEmail: string;
  userRoles: string[];

  // O que
  action: AuditAction;
  resourceType: string;
  resourceId: string;

  // Como
  method: string;
  path: string;
  statusCode: number;

  // Contexto
  ip: string;
  userAgent: string;
  sessionId: string;

  // Detalhes
  changes?: {
    before: Record<string, any>;
    after: Record<string, any>;
  };
  metadata?: Record<string, any>;
}

type AuditAction =
  | 'LOGIN' | 'LOGOUT' | 'LOGIN_FAILED'
  | 'CREATE' | 'READ' | 'UPDATE' | 'DELETE'
  | 'EXPORT' | 'SHARE' | 'PERMISSION_CHANGE'
  | 'CONFIG_CHANGE' | 'BULK_OPERATION';

// Servico de Audit
class AuditService {
  private queue: AuditLog[] = [];
  private batchSize = 100;
  private flushInterval = 5000; // 5s

  constructor(private storage: AuditStorage) {
    setInterval(() => this.flush(), this.flushInterval);
  }

  log(entry: Omit<AuditLog, 'id' | 'timestamp'>) {
    this.queue.push({
      ...entry,
      id: crypto.randomUUID(),
      timestamp: new Date(),
    });

    if (this.queue.length >= this.batchSize) {
      this.flush();
    }
  }

  private async flush() {
    if (this.queue.length === 0) return;

    const batch = this.queue.splice(0, this.batchSize);
    await this.storage.writeBatch(batch);
  }

  // Helper para detectar mudancas
  static diff(before: any, after: any): { before: any; after: any } {
    const changes = { before: {}, after: {} };

    const allKeys = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]);

    for (const key of allKeys) {
      if (JSON.stringify(before?.[key]) !== JSON.stringify(after?.[key])) {
        changes.before[key] = before?.[key];
        changes.after[key] = after?.[key];
      }
    }

    return changes;
  }
}

// Uso com decorators
function Audited(action: AuditAction, resourceType: string) {
  return function(target: any, key: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;

    descriptor.value = async function(...args: any[]) {
      const before = await this.getResource?.(args);
      const result = await original.apply(this, args);
      const after = await this.getResource?.(args);

      auditService.log({
        action,
        resourceType,
        resourceId: result?.id || args[0],
        changes: AuditService.diff(before, after),
        ...getCurrentContext(), // userId, ip, etc
      });

      return result;
    };

    return descriptor;
  };
}

// Uso
class DashboardService {
  @Audited('UPDATE', 'dashboard')
  async updateDashboard(id: string, data: UpdateDashboardDTO) {
    return this.repository.update(id, data);
  }
}

📊 Dashboard de Auditoria

12,847
Logins hoje
23
Falhas de auth
156
Exportacoes
8
Mudancas config
🔴 3 tentativas de login falhas do IP 192.168.1.100 2 min atras
🟡 Usuario admin@empresa.com exportou relatorio financeiro 15 min atras
🟢 Novo usuario joao@empresa.com criado por admin 1 hora atras

💡 Politica de Retencao

Defina politicas claras de retencao de logs considerando requisitos legais e custos de armazenamento:

  • Hot storage (30 dias): Elasticsearch/OpenSearch para busca rapida
  • Warm storage (1 ano): S3 Standard para acesso ocasional
  • Cold storage (7 anos): S3 Glacier para compliance

📝 Resumo do Modulo

OAuth2/OIDC - Fluxos de autenticacao modernos, PKCE para SPAs
JWT e Sessions - Gestao de tokens, refresh automatico, storage seguro
SSO Empresarial - SAML, Azure AD, integracao com Active Directory
RBAC - Controle de acesso baseado em roles e permissoes
Protecao de Rotas - Guards, middlewares, defesa em profundidade
Audit Logs - Rastreabilidade, compliance, deteccao de anomalias