# OIDC Session Metadata e Zero Trust: Uma Decisão de Arquitetura

O suporte a metadados de sessão no Sign in with Google abre uma janela real para acesso adaptativo baseado em sinais de identidade contínuos — não apenas no momento do login. Neste ADR, analiso as forças arquiteturais, as opções consideradas e a decisão que tomaria em um sistema financeiro de alta criticidade integrado à AWS.

- URL: https://fernando.moretes.com/blog/oidc-session-metadata-e-acesso-adaptativo

- Markdown: https://fernando.moretes.com/blog/oidc-session-metadata-e-acesso-adaptativo/article.md?lang=pt

- Published: 2026-06-12T00:00:00.000Z

- Category: Segurança & Resiliência

- Tags: oidc, zero-trust, cognito, identity, step-up-auth, session-metadata, lambda-authorizer, financial-grade

- Reading time: 9 min

- Source: [Session metadata for Sign in with Google](https://developers.googleblog.com/)

---

Por anos, tratamos a sessão autenticada como um estado binário: o usuário fez login ou não. O suporte a metadados de sessão no Sign in with Google muda essa equação ao expor sinais contínuos de identidade — risco de conta, estado de verificação, alterações recentes de credencial — diretamente no fluxo OIDC. Para sistemas financeiros que operam sob Zero Trust, isso não é uma feature de conveniência; é uma mudança de modelo de ameaça. A questão arquitetural real não é 'como integrar isso ao Cognito', mas sim 'onde na minha cadeia de autorização esses sinais devem ser avaliados, com que latência, e o que acontece quando o provedor de identidade não os entrega?'

## Contexto e Forças: Por Que Isso Importa Agora

Em ambientes financeiros regulados — PCI-DSS, SOC 2 Type II, BACEN 4.658 no Brasil — a autenticação federada com provedores externos como Google sempre foi uma faca de dois gumes. Você ganha UX e reduz a superfície de gerenciamento de credenciais, mas perde visibilidade sobre o ciclo de vida da sessão do usuário no lado do IdP. Um token JWT válido emitido às 09h00 ainda é aceito às 17h00 mesmo que o usuário tenha trocado a senha às 14h00, que o Google tenha detectado acesso suspeito à conta às 15h00, ou que o dispositivo tenha sido marcado como comprometido às 16h00.

O modelo tradicional de mitigação era simples e brutal: tokens de curta duração (15-30 minutos) com refresh agressivo. Isso funciona, mas tem custo: latência de re-autenticação, fricção em fluxos de longa duração (relatórios, exportações, operações batch iniciadas pelo usuário), e pressão sobre os rate limits do Cognito — que impõe 120 requisições de token por segundo por user pool por padrão, um limite que sistemas de médio porte atingem facilmente em horários de pico.

Metadados de sessão OIDC mudam o vetor de defesa. Em vez de encurtar a vida do token, você enriquece a decisão de autorização com sinais do IdP em tempo real. O token pode ter vida mais longa, mas cada requisição sensível passa por uma avaliação que considera o estado atual da sessão no Google — não apenas o estado no momento da emissão. Isso é, conceitualmente, o que o NIST SP 800-207 chama de 'continuous verification' dentro de um modelo Zero Trust.

## As Forças em Tensão

Antes de chegar às opções, é preciso nomear as forças que tornam essa decisão não trivial:

**Latência vs. Frescor do Sinal.** Consultar metadados de sessão do Google a cada requisição adiciona uma chamada de rede externa ao caminho crítico. Em APIs financeiras com SLO de p99 < 300ms, isso é inaceitável sem cache. Mas cache introduz janela de staleness — exatamente o problema que estamos tentando resolver.

**Disponibilidade do IdP vs. Continuidade do Serviço.** Se o endpoint de metadados do Google estiver indisponível, o que fazemos? Fail-open (aceitar a requisição) ou fail-closed (negar)? Em sistemas financeiros, fail-closed é o padrão regulatório correto, mas isso significa que uma degradação do Google pode derrubar seu serviço.

**Granularidade de Política vs. Complexidade Operacional.** Quanto mais granular a política de acesso adaptativo — 'este endpoint requer score de risco < 20 E verificação de dispositivo recente' — mais difícil é depurar, auditar e explicar para times de compliance.

**Cobertura de Claims vs. Dependência de Vendor.** Os metadados que o Google expõe são proprietários. Se amanhã você precisar suportar Microsoft Entra ou Okta como IdP alternativo, sua lógica de política precisa abstrair sobre diferentes schemas de claims. Isso é um risco de lock-in que poucos times de arquitetura modelam explicitamente.

Essas quatro tensões definem o espaço de design. As opções abaixo são respostas diferentes para o mesmo conjunto de forças.

## Opções Consideradas para Avaliação de Metadados de Sessão

### Opção A: Token de Curta Duração (Status Quo)

**Pros**
- Sem dependência de endpoint externo no caminho crítico
- Simples de implementar e auditar
- Comportamento previsível em falhas do IdP

**Cons**
- Janela de risco entre revogação no IdP e expiração do token
- Pressão sobre rate limits do Cognito em escala
- Fricção em fluxos de longa duração

**Verdict:** Adequado para sistemas de baixo risco; insuficiente para operações financeiras críticas

### Opção B: Avaliação Inline no Lambda Authorizer

**Pros**
- Sinal mais fresco possível por requisição
- Política centralizada e auditável no authorizer
- Integração natural com API Gateway e ALB

**Cons**
- Latência adicionada: p99 de chamada ao Google ~80-150ms sem cache
- Disponibilidade do serviço acoplada ao Google
- Cold start do Lambda amplifica latência em picos

**Verdict:** Viável com cache agressivo e circuit breaker; requer SLO de latência revisado

### Opção C: Avaliação Assíncrona com Step-Up Reativo

**Pros**
- Zero latência adicionada ao caminho crítico
- Degradação graciosa quando o Google está indisponível
- Permite políticas diferenciadas por nível de sensibilidade da operação

**Cons**
- Janela de risco entre detecção assíncrona e revogação da sessão
- Maior complexidade: EventBridge + Step Functions + DynamoDB
- Requer lógica de step-up no cliente (redirecionamento para re-auth)

**Verdict:** Melhor equilíbrio para sistemas financeiros de alta escala; é a opção que eu escolheria

### Opção D: Abstração via IDSA/SPIFFE com Claims Normalizados

**Pros**
- Elimina lock-in em schema proprietário do Google
- Portabilidade entre IdPs (Entra, Okta, Google)
- Alinha com padrões de identidade de workload (SPIFFE/SPIRE)

**Cons**
- Overhead de implementação significativo; requer camada de tradução de claims
- Maturidade operacional alta exigida do time
- Fora do escopo para a maioria dos projetos em 2025

**Verdict:** Ideal como visão de longo prazo; prematuro como primeira implementação

## A Decisão: Avaliação Assíncrona com Step-Up Síncrono Seletivo

Depois de modelar as quatro opções contra as forças identificadas, a decisão que eu tomaria é uma variante da Opção C com um elemento síncrono cirúrgico: **avaliação assíncrona de metadados de sessão para a maioria das requisições, com step-up síncrono obrigatório para operações de alto impacto financeiro**.

A lógica é a seguinte: nem todas as requisições têm o mesmo perfil de risco. Uma consulta de saldo ou leitura de extrato tem tolerância a uma janela de staleness de 5 minutos no sinal de risco. Uma transferência acima de R$ 10.000, uma alteração de dados cadastrais ou uma aprovação de crédito não têm. Forçar o mesmo modelo de avaliação para ambas é over-engineering no primeiro caso e under-engineering no segundo.

A implementação concreta usa três componentes AWS em coordenação:

1. **DynamoDB como cache de estado de sessão**: chave de partição `userId#sessionId`, TTL de 5 minutos, com atributos `riskScore`, `verificationState`, `lastMetadataRefresh` e `stepUpRequired`. Capacidade provisionada com auto-scaling: 100 RCU/WCU base, escala até 2.000 em pico. Criptografia com KMS CMK, ponto-a-ponto.

2. **Lambda Authorizer com leitura síncrona do DynamoDB**: o authorizer lê o estado de sessão cacheado (latência p99 < 5ms com DAX opcional) e toma a decisão de autorização localmente. Não há chamada ao Google no caminho crítico. Se `stepUpRequired = true`, retorna 401 com `WWW-Authenticate: step-up` e o cliente inicia o fluxo de re-autenticação.

3. **EventBridge Scheduler + Lambda de refresh assíncrono**: a cada 4 minutos, um job consulta o endpoint de metadados do Google para sessões ativas e atualiza o DynamoDB. Se o sinal de risco mudar, o atributo `stepUpRequired` é setado atomicamente com uma condição DynamoDB (`ConditionExpression: attribute_not_exists(stepUpRequired) OR stepUpRequired = :false`), evitando race conditions.

## Fluxo de Avaliação de Metadados de Sessão OIDC com Step-Up Adaptativo

Caminho crítico (síncrono) à esquerda; pipeline de refresh assíncrono à direita. O Lambda Authorizer nunca chama o Google diretamente — lê apenas do cache DynamoDB.

### 🟧 AWS — API Layer

- API Gateway REST / HTTP API (edge)
- Lambda Authorizer step-up policy eval (security)

### 🟧 AWS — Session State

- DynamoDB userId#sessionId / TTL 5m (data)
- KMS CMK encrypt at rest (security)

### 🟧 AWS — Async Refresh Pipeline

- EventBridge Scheduler every 4 min (messaging)
- Refresh Lambda fetch + diff + write (compute)
- Step Functions step-up orchestration (compute)

### 🟧 AWS — Observability

- CloudWatch stepUpRate / riskSignalAge (data)

### 🔵 Google — Identity

- Sign in with Google OIDC + session metadata (external)

### 🟧 AWS — Backend

- Financial API Lambda / EKS (compute)

### Fluxos

- client -> apigw: 1. JWT Bearer
- apigw -> authorizer: 2. invoke authorizer
- authorizer -> ddb: 3. GetItem (cache)
- ddb -> kms: decrypt
- authorizer -> apigw: 4. Allow / 401 step-up
- apigw -> backend: 5. forward (if allowed)
- scheduler -> refreshlambda: trigger
- refreshlambda -> google_oidc: 6. fetch session metadata
- refreshlambda -> ddb: 7. UpdateItem (conditional)
- refreshlambda -> stepfn: 8. start step-up flow (if risk↑)
- authorizer -> cw: metrics
- refreshlambda -> cw: riskSignalAge

## Detalhes de Implementação que Importam em Produção

**IAM e Least Privilege no Lambda Authorizer.** O authorizer precisa apenas de `dynamodb:GetItem` na tabela de sessões, com condição `aws:ResourceTag/Classification = SessionCache`. Nenhuma permissão de escrita. A role do refresh Lambda tem `dynamodb:UpdateItem` com condição `dynamodb:LeadingKeys` restrita ao prefixo `userId#` — impedindo que um bug no código escreva em partições arbitrárias. Ambas as roles usam `kms:Decrypt` com condição `kms:EncryptionContext:TableName = session-state`.

**Circuit Breaker no Refresh Lambda.** A chamada ao endpoint de metadados do Google deve ter timeout de 2 segundos e no máximo 2 retries com backoff exponencial. Se a taxa de erro superar 50% em uma janela de 60 segundos, o circuit breaker abre e o refresh Lambda para de tentar — mas não seta `stepUpRequired = true` automaticamente. A política correta aqui é conservar o último estado conhecido e emitir um alarme CloudWatch (`MetricName: GoogleMetadataUnavailable`, threshold > 3 minutos). A decisão de fail-open vs. fail-closed durante indisponibilidade do Google deve ser explícita na política e documentada no ADR — não implícita no código.

**Observabilidade do Sinal de Risco.** Os metadados do Google incluem campos como `credential_age`, `account_risk_level` e `device_verified`. Esses valores devem ser emitidos como dimensões de métricas customizadas no CloudWatch — não apenas logados. Isso permite criar alarmes como 'percentual de sessões ativas com `account_risk_level = HIGH` > 5%', que é um sinal de ataque de credential stuffing em andamento. O SLO que eu definiria: `riskSignalAge` (tempo desde o último refresh bem-sucedido) < 6 minutos para 99% das sessões ativas.

**Cognito User Pools e o Problema do Token Opaco.** O Cognito não repassa claims customizados do Google automaticamente para o access token. Você precisa de um Lambda trigger `Pre Token Generation` que leia o estado de sessão do DynamoDB e injete os claims relevantes. Atenção: esse trigger adiciona latência ao fluxo de refresh de token — mantenha-o abaixo de 100ms ou o Cognito vai timeout na chamada.

> **Consequências e Riscos da Decisão:** **Janela de risco residual de até 4 minutos.** A arquitetura assíncrona aceita explicitamente que um usuário com sessão comprometida pode fazer até ~4 minutos de requisições de baixo impacto antes de ser bloqueado. Para operações de alto impacto, o step-up síncrono elimina essa janela — mas o time de compliance precisa aceitar e documentar esse trade-off formalmente. Não tente esconder isso no design.

**Dependência de disponibilidade do Google para o pipeline de refresh.** Se o Google tiver uma degradação de 30 minutos, o `riskSignalAge` vai ultrapassar o SLO. Você precisa de um runbook explícito: o que o sistema faz automaticamente (conserva último estado), o que o operador faz manualmente (pode forçar step-up global via feature flag no DynamoDB), e quando escalar para o time de segurança.

**Custo de DynamoDB em escala.** Com 100.000 sessões ativas e refresh a cada 4 minutos, o pipeline assíncrono gera ~25.000 writes/minuto. No modo on-demand, isso é ~$0.0000125 por write = ~$0.31/minuto = ~$450/mês apenas para o refresh pipeline. Não é caro, mas precisa estar no modelo de custo — especialmente se o número de sessões escalar 10x.

**Risco de amplificação de step-up.** Se o Google retornar `account_risk_level = HIGH` para um segmento grande de usuários simultaneamente (falso positivo em massa), você pode acionar step-up para milhares de usuários ao mesmo tempo, gerando uma avalanche de re-autenticações que sobrecarrega o Cognito. Implemente rate limiting no número de step-ups simultâneos e um mecanismo de override de emergência.

## Governança, Auditoria e o Caminho para Zero Trust Maduro

Uma decisão de arquitetura de identidade não termina na implementação técnica. Em sistemas financeiros regulados, a rastreabilidade de cada decisão de autorização é tão importante quanto a decisão em si. Cada chamada ao Lambda Authorizer deve emitir um evento estruturado para o CloudWatch Logs com: `userId`, `sessionId`, `decision` (allow/deny/step-up), `riskScore` no momento da decisão, `signalAge` em segundos, e o `requestId` do API Gateway. Esse log é a evidência de auditoria para demonstrar ao regulador que o sistema estava operando com verificação contínua.

No contexto do AWS Well-Architected, essa arquitetura toca diretamente o pilar de Segurança — especificamente os princípios de 'apply security at all layers' e 'enable traceability'. O uso de KMS CMK com rotation anual, IAM conditions granulares, e CloudTrail habilitado para todas as operações DynamoDB e KMS fecha o loop de auditoria.

O caminho para Zero Trust maduro a partir daqui tem dois passos naturais. O primeiro é adicionar sinais de contexto de dispositivo — não apenas o que o Google sabe sobre a conta, mas o que você sabe sobre o dispositivo: fingerprint, geolocalização, padrão de comportamento. Isso pode ser implementado via Amazon Fraud Detector ou via um modelo customizado no SageMaker alimentado pelos logs de auditoria. O segundo é mover a política de autorização para um motor externo — Open Policy Agent (OPA) rodando no EKS, ou AWS Verified Permissions com Cedar — desacoplando a lógica de política do código do authorizer e permitindo que o time de compliance edite políticas sem deploy de código.

Essa evolução deve estar no roadmap, mas não no MVP. A armadilha clássica é tentar construir o sistema Zero Trust completo na primeira iteração e não entregar nada.

## Avaliação pelos Pilares Well-Architected

- **security**: Verificação contínua de sessão via metadados OIDC, KMS CMK, IAM least-privilege com condições granulares, CloudTrail para auditoria completa. Step-up síncrono para operações de alto impacto elimina a janela de risco nas operações críticas.
- **reliability**: Circuit breaker no refresh Lambda protege contra indisponibilidade do Google. Estado de sessão cacheado no DynamoDB garante que o authorizer funcione mesmo sem conectividade ao IdP. Runbook explícito para degradação do sinal.
- **performance**: Lambda Authorizer lê apenas do DynamoDB (p99 < 5ms com DAX). Nenhuma chamada ao Google no caminho crítico. Cache TTL de 5 minutos balanceia frescor e latência.

## Anti-Padrões a Evitar

- Chamar o endpoint de metadados do Google diretamente no Lambda Authorizer sem cache — você vai violar seu SLO de latência e acoplar disponibilidade ao Google.
- Tratar `account_risk_level` como binário (alto/baixo) sem considerar a distribuição histórica — um threshold fixo vai gerar falsos positivos em eventos de segurança legítimos do Google (ex: mudança de senha voluntária).
- Armazenar o token OIDC completo no DynamoDB como estado de sessão — armazene apenas os claims relevantes para a política, nunca o token bruto.
- Não documentar explicitamente a janela de risco residual no ADR — o time de compliance vai descobrir isso em uma auditoria e será pior.
- Usar o mesmo Lambda Authorizer para todos os endpoints sem diferenciar sensibilidade — aplique step-up síncrono apenas onde o risco justifica a latência adicional.

> **Nota do Arquiteto:** Na prática, a decisão mais difícil aqui não é técnica — é política: convencer o time de compliance a aceitar formalmente uma janela de risco de 4 minutos em troca de um sistema que é auditável, observável e que não quebra quando o Google tem uma degradação. Já vi times construírem sistemas de avaliação síncrona 'perfeitos' que ficaram indisponíveis por 45 minutos durante um incidente do Google e ninguém havia documentado o fallback. O circuit breaker e o runbook explícito não são detalhes de implementação — são o núcleo da decisão arquitetural. E o ADR precisa viver no repositório, não em uma wiki que ninguém lê depois do go-live.

## Veredicto: Adote Metadados de Sessão OIDC com Avaliação Assíncrona e Step-Up Seletivo

O suporte a metadados de sessão no Sign in with Google é um avanço real para sistemas que precisam de verificação contínua de identidade. A decisão correta para ambientes financeiros não é avaliação síncrona em cada requisição — é avaliação assíncrona com cache DynamoDB, circuit breaker explícito, e step-up síncrono cirúrgico para operações de alto impacto. Essa arquitetura entrega Zero Trust prático: verificação contínua sem sacrificar latência, com degradação graciosa e auditabilidade completa. A janela de risco residual de 4 minutos é um trade-off aceitável e documentável — não uma falha de design. Implemente, documente no ADR, e evolua para AWS Verified Permissions com Cedar quando a maturidade do time permitir.

**Rating:** Strongly Recommended with documented tra

## Referências

- [NIST SP 800-207: Zero Trust Architecture](https://csrc.nist.gov/publications/detail/sp/800-207/final)
- [AWS Docs: Amazon Cognito — Pre Token Generation Lambda Trigger](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html)
- [AWS Docs: API Gateway Lambda Authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html)
- [AWS Docs: Amazon Verified Permissions and Cedar](https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/what-is-avp.html)
- [AWS Blog: Implementing Zero Trust with AWS](https://aws.amazon.com/blogs/security/zero-trust-architectures-an-aws-perspective/)
- [Google Developers: Sign in with Google — Session Metadata](https://developers.google.com/identity/gsi/web/guides/overview)
- [OpenID Connect Core 1.0 Specification](https://openid.net/specs/openid-connect-core-1_0.html)
- [AWS Docs: DynamoDB Condition Expressions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html)
