Autenticação no MongoDB Atlas com Federação de Identidade de Saída do AWS IAM

Este arquivo foi traduzido automaticamente por IA, erros podem ocorrer
Estive escrevendo vários posts sobre tópicos de autenticação e autorização, usando o padrão PEP e PDP. PEP e PDP para Autorização Segura com Cognito, depois estendi isso com Amazon Verified Permissions, e mais tarde adicionei ABAC no topo. Tudo isso era sobre autorização de usuário para serviço. Ou seja, um humano faz login, obtém um token e o backend decide o que ele pode fazer.
Mas e quanto a serviço a serviço? Máquina para máquina? Seu Lambda precisa falar com um banco de dados ou chamar uma API externa, e não há um humano no loop. Você ainda precisaria de credenciais.
Todo o código-fonte está disponível no Serverless Handbook.
A Maneira Comum de Lidar com Tokens M2M
Uma das abordagens mais comuns que vejo é usar Cognito User Pools, Auth0 ou similares com o fluxo de credenciais do cliente OAuth 2.0. Você cria um servidor de recursos, registra um cliente de aplicativo com um ID e segredo do cliente, e seu Lambda troca isso por um token de acesso. Isso funciona bem e, se você já estiver usando Cognito para autenticação de usuário, parece natural estendê-lo para M2M também. O que deve ser lembrado é que os tokens M2M desses serviços podem rapidamente se tornar caros.
Outra abordagem clássica é simplesmente armazenar credenciais no Secrets Manager. Senhas do banco de dados, chaves de API, o que você precisar. Habilite a rotação e siga em frente. Funciona, mas é mais uma coisa para gerenciar, mais uma coisa que pode falhar e mais um segredo que pode vazar.
Federação de Identidade de Saída
Pouco antes do re:Invent 2025, houve um lançamento muito interessante, a Federação de Identidade de Saída do AWS IAM.
Estamos usando federação de identidade na AWS há muito tempo. Google, Okta, Entra ou outros emitem um token, a AWS confia nele e você obtém acesso aos recursos da AWS. Isso é federação entrante. A AWS é quem está confiando.
A Federação de Identidade de Saída é o inverso. Agora a AWS é quem emite os tokens. Seu Lambda (ou EC2, ou tarefa ECS) chama o STS para trocar um papel IAM por um token JWT assinado que basicamente diz "ei, eu sou este papel IAM, e aqui está uma prova criptográfica." Depois, você entrega esse token para qualquer serviço externo ou interno com o qual está falando, e eles podem verificá-lo usando fluxos padrão.
Isso significa: sem segredos de cliente ou senhas armazenadas. O token emitido é de curta duração, vinculado ao papel IAM e assinado pela AWS. O serviço receptor só precisa confiar no emissor.
O Que Há no Token?
Quando chamamos sts:GetWebIdentityToken, obteremos um token JWT padrão, com algumas declarações que importam:
iss - O emissor. Este é o URL do Emissor de Tokens que obteremos ao habilitar o recurso, ele se parece com isso "https://a1810f8a-5a75-4e21-b1cd-a6b09f1836cb.tokens.sts.global.api.aws". É único para nossa conta AWS e não podemos modificá-lo.sub - O assunto. Este é o ARN do Papel IAM. É a identidade.aud - O público. Isso é definido por nós e pode ser qualquer coisa, "my-api", "atlas-demo", o que quiser. Ambos os lados só precisam concordar com o valor.exp - Expiração. A duração curta é o ponto principal.
Visão geral da arquitetura
Neste post, construiremos duas soluções diferentes que usam a Federação de Identidade de Saída, apenas para mostrar como configurá-la para um serviço interno, hospedado no Lambda atrás do API Gateway, e um banco de dados externo, neste caso, MongoDB Atlas.
No nosso caso de uso interno, temos uma função Lambda de trabalhador que chamará o STS para obter um token JWT de saída assinado. As funções chamam uma API no API Gateway onde temos um Lambda Authorizer que usa as chaves públicas do STS para validar o JWT.

No caso do MongoDB, temos praticamente o mesmo. Estabelecemos confiança entre o MongoDB e o STS fazendo alguma configuração no lado do MongoDB. Nossa função Lambda de trabalhador chamará o STS para obter o JWT e usá-lo como autenticação para o MongoDB, que usa as chaves públicas etc do STS para validar o usuário. Para o caso de uso do MongoDB, precisamos mapear tudo isso para um usuário, mas falaremos mais sobre essa configuração mais tarde.

Agora, vamos construir!
Habilitar a Federação de Identidade de Saída
Antes de podermos usar a Federação de Identidade de Saída, precisamos habilitá-la, por padrão ela está desativada. Portanto, vá para o AWS Console e IAM > Configurações da Conta.

Role para baixo até a seção Federação de Identidade de Saída e clique em Habilitar.

Uma vez habilitado, você obterá seu URL de Emissor de Tokens específico da conta imediatamente. Copie-o, você precisará dele mais tarde.

Você também pode habilitá-la via CLI se preferir:
aws iam enable-outbound-web-identity-federationAgora vamos tentar isso e chamar o STS para obter um token, executamos o comando CLI abaixo para obter um token para nosso principal conectado.
aws sts get-web-identity-token --audience "my-api" --signing-algorithm RS256Nós obteremos uma resposta como:
{
"WebIdentityToken": "Token Codificado em Base64",
"Expiration": "2026-03-19T08:20:06.177000+00:00"
}Se decodarmos o WebIdentityToken, usando uma página da web como jwt.io, poderemos ver as diferentes declarações.
{
"aud": "my-api",
"sub": "arn:aws:iam::<account_id><iam_role>",
"https://sts.amazonaws.com/": {
"org_id": "<AWS_Org_Id>",
"aws_account": "<account_id>",
"ou_path": [
"...."
],
"original_session_exp": "2026-03-19T10:15:04Z",
"source_region": "eu-west-1",
"principal_id": "<iam_principal>",
"identity_store_user_id": "...."
},
"iss": "https://....tokens.sts.global.api.aws",
"exp": 1773908406,
"iat": 1773908106,
"jti": "..."
}Verificando o Token com o API Gateway
Com isso funcionando, vamos construir uma API simples que uma função Lambda chama, onde temos um Lambda authorizer que valida o token.
Começamos criando a função chamadora, isso é bastante simples, chamamos o STS para obter um token e depois o apresentamos ao chamar a API.
import json
import logging
import os
import boto3
import requests
logger = logging.getLogger()
logger.setLevel(logging.INFO)
sts_client = boto3.client("sts")
API_ENDPOINT = os.environ["API_ENDPOINT"]
AUDIENCE = os.environ["AUDIENCE"]
def get_token():
response = sts_client.get_web_identity_token(
Audience=[AUDIENCE],
DurationSeconds=300,
SigningAlgorithm="RS256",
)
return response["WebIdentityToken"]
def handler(event, context):
try:
token = get_token()
response = requests.get(
API_ENDPOINT,
headers={"Authorization": f"Bearer {token}"},
timeout=10,
)
return {
"statusCode": response.status_code,
"body": json.dumps(
{
"api_status_code": response.status_code,
"api_response": response.json() if response.ok else response.text,
"request_id": context.aws_request_id,
}
),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps(
{
"message": "Erro ao chamar API protegida",
"error": str(e),
}
),
}Se olharmos a função get_token(), ela é muito simples, apenas três parâmetros e pronto. Não precisamos buscar credenciais de lugar algum. O Lambda apenas diz "dê-me um token para este público" e o STS assina um com a própria identidade do Lambda.
O Lambda Authorizer
O Lambda Authorizer anexado ao API Gateway é onde as coisas começam a ficar realmente interessantes. O que faremos é buscar as chaves públicas do STS e depois validar o token usando uma biblioteca JWT padrão.
Criamos o descoberta usando o URL do emissor e anexando /.well-known/openid-configuration Isso nos diz onde podemos encontrar as chaves públicas e qual deve ser o valor do emissor. Depois, criamos um PyJWKClient que lida com o fetch e o cache das chaves.
import json
import logging
import os
import urllib.request
import jwt
logger = logging.getLogger()
logger.setLevel(logging.INFO)
AUDIENCE = os.environ["AUDIENCE"]
ISSUER_URL = os.environ["ISSUER_URL"]
DISCOVERY_URL = f"{ISSUER_URL}/.well-known/openid-configuration"
discovery_doc = json.loads(urllib.request.urlopen(DISCOVERY_URL).read())
JWKS_URI = discovery_doc["jwks_uri"]
EXPECTED_ISSUER = discovery_doc["issuer"]
jwks_client = jwt.PyJWKClient(JWKS_URI)
def handler(event, context):
try:
token = extract_token(event)
if not token:
return generate_policy("Deny", event["methodArn"])
signing_key = jwks_client.get_signing_key_from_jwt(token)
decoded = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience=OIDC_AUDIENCE,
issuer=EXPECTED_ISSUER,
)
return generate_policy(
"Allow",
event["methodArn"],
principal_id=decoded.get("sub", "unknown"),
context={"sub": decoded.get("sub", ""), "iss": decoded.get("iss", "")},
)
except jwt.ExpiredSignatureError:
logger.warning("Token expirou")
except jwt.InvalidAudienceError:
logger.warning("Audiência inválida")
except jwt.InvalidIssuerError:
logger.warning("Emissor inválido")
except Exception as e:
logger.error("Verificação do token falhou: %s", e)
return generate_policy("Deny", event["methodArn"])A função jwt.decode() é o núcleo de tudo e validará os tokens reais contra os valores esperados e lançará uma exceção em caso de problema.
Se tudo passar, permitimos a solicitação e encaminhamos a declaração sub para que o backend saiba quem está chamando.
Vale notar que não há nada específico da AWS nesta lógica de verificação. Esta é apenas uma validação padrão de token JWT.
Autenticação no MongoDB Atlas
Agora vamos colocar isso em um caso de uso real, conectando-se a um cluster MongoDB Atlas usando tokens do IAM Outbound Federation, eliminando a necessidade de armazenar senhas no Secrets Manager.
Há uma coisa muito importante a notar. Para que isso funcione, você precisará executar um cluster dedicado M10+ do MongoDB Atlas com MongoDB 7.0.11 ou superior. A Federação de Identidade de Trabalho não é suportada em clusters gratuitos ou compartilhados (M0, M2, M5).
A ideia é bastante simples e diretaforward. Nossa função Lambda chama o STS para obter um token, depois apresentamos esse token ao Atlas em vez de um nome de usuário e senha. O Atlas validará o token, usando fluxos padrão, e mapeará o Papel IAM para um usuário do banco de dados.
Configurando o Atlas
O primeiro passo agora é configurar as coisas no lado do MongoDB, faça login na sua conta e navegue até Federação em Identidade e Acesso.

Em Federação, selecione Provedores de Identidade e começaremos a configurá-lo.

Na tela seguinte, selecione Identidade de Trabalho para que possamos começar a adicionar detalhes.

Na tela de configuração, preencha nome e descrição. O URL do emissor é aquele do AWS IAM Console, que vimos anteriormente neste post. Para audiência, defina um valor de sua escolha, importante que seja o mesmo na configuração do MongoDB e AWS (ao chamar o STS). A audiência deve corresponder. Para a declaração do usuário, mantenha o padrão sub

Salve e finalize sua configuração e você deverá ser apresentado com a tela de visão geral.

Em seguida, precisamos conectar o provedor de identidade que acabamos de criar com nossa organização. Selecione Organizações no menu e comece a conectar.

No diálogo de popup, selecione o provedor que acabamos de criar e clique em Conectar.

Na próxima visão, devemos ver a visão geral da conexão.

Implantar aplicação de teste
Foram muitos passos, mas agora podemos implantar nossa aplicação de teste e fazer a configuração final no Atlas antes de testar. Manteremos a infraestrutura de teste mínima. Apenas uma função Lambda que tem permissão sts:GetWebIdentityToken:
Resources:
DatabaseOpFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${Application}-database-op
CodeUri: src/DatabaseOp/
Handler: database_op.handler
Environment:
Variables:
MONGODB_URI: !Ref MongoDbUri
OIDC_AUDIENCE: !Ref OidcAudience
Policies:
- Statement:
- Effect: Allow
Action:
- sts:GetWebIdentityToken
Resource: "*"
Outputs:
DatabaseOpFunctionRoleArn:
Description: ARN do Papel IAM para a função Lambda (use isso no mapeamento de papel OIDC do Atlas)
Value: !GetAtt DatabaseOpFunctionRole.ArnAdicionamos o ARN do papel à saída da pilha, pois precisaremos disso para a parte final do Atlas. O código está dividido no manipulador Lambda e um cliente que lidará com a conexão ao banco de dados MongoDB.
O driver pymongo tem um ótimo mecanismo de callback para OIDC. Nós fornecemos uma classe, e o driver a chama sempre que precisa de um token. O driver acessa fetch() na primeira conexão e novamente quando o token está prestes a expirar. Definimos o valor para 280 segundos, um pouco abaixo do real de 300, para ter uma margem de segurança.
Definir authMechanism="MONGODB-OIDC" diz ao driver para pular nome de usuário/senha e usar OIDC em vez disso.
import os
import boto3
from pymongo import MongoClient
from pymongo.auth_oidc import OIDCCallback, OIDCCallbackResult
sts_client = boto3.client("sts")
cached_client = None
def get_oidc_token():
audience = os.environ["OIDC_AUDIENCE"]
response = sts_client.get_web_identity_token(
Audience=[audience],
DurationSeconds=300,
SigningAlgorithm="RS256",
)
return response["WebIdentityToken"]
class AwsOidcCallback(OIDCCallback):
def fetch(self, context):
token = get_oidc_token()
return OIDCCallbackResult(access_token=token, expires_in_seconds=280)
def get_mongo_client():
global cached_client
if cached_client is not None:
return cached_client
uri = os.environ["MONGODB_URI"]
properties = {"OIDC_CALLBACK": AwsOidcCallback()}
cached_client = MongoClient(
uri,
authMechanism="MONGODB-OIDC",
authMechanismProperties=properties,
)
return cached_clientDepois, em nosso manipulador Lambda, criamos o cliente MongoDB e interagimos com o banco de dados, neste exemplo simples, apenas inserindo um documento na coleção items.
def handler(event, context):
try:
client = get_mongo_client()
db = client["sample_db"]
collection = db["items"]
document = {
"message": "Olá do Lambda com autenticação OIDC",
"timestamp": datetime.now(timezone.utc).isoformat(),
"request_id": context.aws_request_id,
}
result = collection.insert_one(document)
doc = collection.find_one({"_id": result.inserted_id})
return {
"statusCode": 200,
"body": json.dumps({
"message": "Conectado com sucesso ao MongoDB Atlas com OIDC",
"insertedId": str(result.inserted_id),
}),
}
except Exception as e:
logger.error("Erro: %s", e)
return {"statusCode": 500, "body": json.dumps({"error": str(e)})}Criar um usuário do banco de dados no Atlas
Agora precisamos fazer o passo final no Atlas, criando um usuário do banco de dados e mapeando-o para o ARN do Papel IAM, para que possamos obter permissões para interagir com o banco de dados. Vá para Acesso ao Banco de Dados e adicione um novo usuário do banco de dados.

Selecione Autenticação Federada como o método de autenticação e defina o nome de usuário para o ARN do Papel da função Lambda, obtenha isso da saída da pilha. Atribua ao usuário as permissões Ler e escrever em qualquer banco de dados.

Esse ARN de papel corresponderá à declaração sub no JWT. É assim que o Atlas sabe qual usuário do banco de dados usar.
Allowlist de acesso à rede
Antes de testarmos tudo, há mais uma coisa que precisamos fazer, devemos permitir o acesso à rede para nossa função Lambda. Como não executamos em uma VPC, devemos permitir 0.0.0.0/0 em uma configuração de produção, não faça isso, mas para este teste está tudo bem.
Navegue até Lista de Acesso de IP no menu

Então adicione 0.0.0.0/0, defina-o como temporário para que seja removido automaticamente.

É isso! Agora podemos usar a Federação de Identidade de Saída no STS para autenticar no MongoDB Atlas e ler e escrever dados, sem senhas, sem segredos para gerenciar!!
Testando
Hora de fazer um pequeno teste, para isso, basta invocar a função Lambda implantada. Depois, vá para o Data Explorer no Atlas e verifique se os dados estão lá.

Palavras Finais
Eu realmente gosto desse padrão. Sem segredos armazenados. Tokens que expiram em 5 minutos. Você sabe exatamente qual papel IAM fez cada conexão. E funciona com qualquer coisa que fale OIDC, não apenas MongoDB. Qualquer serviço externo que possa verificar tokens OIDC pode usar a mesma função get_oidc_token(). O mesmo código, destino diferente.
Confira meus outros posts em jimmydqv.com e me siga no LinkedIn e X para mais conteúdo sobre serverless.
Agora vá construir!
