Agentes de IA em produção precisam de identidade segura
Colocar agentes de IA em produção vai muito além de fazer o modelo responder corretamente. Um dos desafios mais críticos é garantir que o agente acesse serviços externos de forma segura e com o escopo certo. A AWS endereça exatamente esse problema com o Amazon Bedrock AgentCore Identity, um serviço disponível de forma independente que gerencia como agentes de IA acessam recursos externos — seja rodando no Amazon ECS, no Amazon EKS, no AWS Lambda ou até em ambientes on-premises.
Um post anterior cobriu o gerenciamento de credenciais com o AgentCore Identity. Agora, a AWS foi além e publicou uma implementação completa de como usar o serviço em ambientes como o ECS, respondendo duas perguntas práticas: como construir um endpoint de Session Binding gerenciado pela própria aplicação, e como controlar o ciclo de vida dos tokens de acesso do workload.
OAuth 2.0 e OIDC: a base da solução
A solução utiliza dois protocolos complementares: OAuth 2.0 (RFC 6749) e Protocolo de Conexão OpenID (OIDC). O OIDC cuida da autenticação — ou seja, confirma quem é o usuário. O OAuth 2.0 cuida da autorização — ou seja, o que esse usuário pode fazer. O foco aqui é no fluxo Authorization Code Grant, indicado para workloads que precisam agir em nome de usuários reais.
Nesse fluxo, o usuário se autentica com um provedor de identidade e concede permissão para o agente acessar recursos específicos em seu nome. A aplicação troca o código de autorização resultante por um token de acesso com escopo definido, que o AgentCore Identity armazena no seu cofre de tokens. Como cada token está vinculado a uma identidade de usuário específica com consentimento explícito, a solução mantém uma cadeia auditável desde a autenticação até a ação do agente.
O Authorization Code Grant é especialmente adequado para workloads agênticos porque oferece três garantias importantes:
- Consentimento do usuário antes que o agente possa agir
- Session binding que verifica se o usuário que iniciou a requisição é o mesmo que concedeu o consentimento
- Delegação com escopo que limita o agente apenas às permissões aprovadas pelo usuário
Callback URL vs. Session Binding URL
Um ponto que costuma gerar confusão são as duas URLs envolvidas no fluxo:
- Callback URL: gerada automaticamente ao criar um cliente OAuth no AgentCore Identity. Aponta para o próprio AgentCore Identity e precisa ser registrada no servidor de autorização como destino do redirecionamento após a autenticação do usuário.
- Session Binding URL: aponta para um serviço gerenciado pelo cliente que completa o session binding entre o usuário autenticado e o fluxo OAuth. Esse endpoint é implementado e hospedado pelo próprio cliente.
Arquitetura da solução no Amazon ECS
A solução implanta dois serviços no Amazon ECS atrás de um Application Load Balancer (ALB). O primeiro é o Agentic Workload, que executa o agente de IA e processa as requisições dos usuários. O segundo é o Session Binding Service, responsável por processar os callbacks OAuth e vincular as sessões de usuário aos tokens de acesso de terceiros. O código-fonte completo e os pré-requisitos estão disponíveis no repositório GitHub que acompanha o post.
O walkthrough usa o Microsoft Entra ID como provedor de identidade, mas qualquer provedor compatível com OIDC funciona. A arquitetura inclui os seguintes componentes de suporte:
- Tráfego criptografado com HTTPS via AWS Certificate Manager
- Roteamento DNS com Amazon Route 53
- Logs capturados pelo Amazon CloudWatch e armazenados em bucket S3
- Imagens de container no Amazon ECR
- Regras básicas do AWS WAF no load balancer para proteção contra exploits comuns
- Chave gerenciada pelo cliente no AWS KMS para criptografia de dados
O fluxo completo do Authorization Code Grant
O diagrama de sequência da solução mostra como a identidade de workload do AgentCore Identity, os tokens de acesso de workload e o provedor de credenciais OAuth 2.0 trabalham juntos para entregar tokens OAuth ao agente em nome do usuário. O fluxo parte do pressuposto de que o usuário ainda não autorizou o agente, ou seja, não há token válido no Token Vault do AgentCore Identity.
O fluxo funciona assim:
- O usuário autenticado envia uma requisição ao workload agêntico.
- O workload extrai o ID do usuário do campo
sub no JWT assinado pelo ALB (header x-amzn-oidc-data).
- O workload chama a API
GetWorkloadAccessTokenForUserId, passando o userId e o workloadName, para obter um token de acesso de workload com escopo para aquele usuário.
- Em seguida, chama a API
GetResourceOauth2Token, passando o token de workload, o nome do provedor OAuth 2.0, a Session Binding URL (parâmetro callbackUrl) e os escopos necessários.
- Como não há token válido, o AgentCore Identity cria um
sessionURI que rastreia o estado do fluxo de autorização e retorna uma URL de autorização para o workload.
- O workload apresenta essa URL ao usuário, que clica e concede permissão na tela de consentimento do provedor.
- O servidor de autorização envia o código de autorização ao AgentCore Identity, que redireciona o usuário para o Session Binding Service via Session Binding URL com o
sessionURI anexado.
- O Session Binding Service chama a API
CompleteResourceTokenAuth com o sessionURI e o ID do usuário extraído do JWT, vinculando a autorização à sessão correta.
- O AgentCore Identity troca o código de autorização com o servidor de autorização, recebe o token OAuth2, armazena no Token Vault e retorna sucesso ao Session Binding Service.
Em requisições subsequentes, o comportamento depende das credenciais emitidas pelo servidor de autorização. Quando há um refresh token disponível — como no GitHub com expiração de token habilitada — o AgentCore Identity o usa automaticamente para renovar o access token sem precisar envolver o usuário novamente. Se não houver refresh token e o access token expirar, o usuário será solicitado a autorizar novamente. Tokens revogados pelo provedor podem ser tratados com o parâmetro forceAuthentication: true, que força um novo fluxo de autenticação.
Por que o session binding é crítico para segurança
O session binding protege contra dois tipos de ataque:
- Falsificação de Requisição entre Sites (CSRF): um atacante tenta vincular seu próprio token OAuth à identidade da vítima, fazendo o agente da vítima acessar recursos do atacante sem que ela perceba — o que possibilita exfiltração de dados e injeção de conteúdo.
- Browser Swapping Attack (ataque de troca de navegador): um atacante engana a vítima para que ela dê consentimento em nome do atacante, vinculando o token OAuth da vítima à identidade do atacante e concedendo acesso direto aos recursos da vítima.
O session binding previne ambos ao garantir que o ID do usuário no workload agêntico seja o mesmo no Session Binding Service, com ambas as identidades verificadas criptograficamente pela cadeia de autenticação. O AgentCore Identity também suporta o parâmetro opcional customState na API GetResourceOauth2Token para passar um nonce aleatório criptograficamente seguro, protegendo o endpoint de callback contra ataques CSRF conforme recomendado pela especificação OAuth 2.0.
Por que usar GetWorkloadAccessTokenForUserId com ALB e Microsoft Entra ID
A API recomendada para obter um token de acesso de workload é a GetWorkloadAccessTokenForJWT, mas essa solução usa a GetWorkloadAccessTokenForUserId. O motivo é uma incompatibilidade técnica entre o fluxo OIDC do ALB e o Microsoft Entra ID.
A GetWorkloadAccessTokenForJWT exige um JWT dinamicamente validável, com assinatura verificável em runtime e claim aud correspondente à aplicação. Para obter esse token do Entra ID, é necessário incluir o Application ID no escopo da requisição OIDC — veja a documentação do AgentCore para Microsoft Inbound. O problema é que isso é incompatível com o fluxo OIDC do ALB.
Como parte do handshake OIDC (descrito na documentação de OIDC do ALB), o ALB envia o access token para o endpoint UserInfo do Entra para recuperar os claims do usuário autenticado. Esse endpoint UserInfo fica no Microsoft Graph (https://graph.microsoft.com/oidc/userinfo) e só aceita tokens com escopo para o Microsoft Graph. Quando o Application ID é incluído no escopo, o access token resultante tem a aplicação como audiência — o endpoint UserInfo rejeita com 401 e o ALB retorna 561. Se o Application ID for removido do escopo, o Entra define a audiência do access token como Microsoft Graph, o handshake do ALB funciona, mas o JWT resultante não pode ser validado dinamicamente pelo AgentCore e é inutilizável com a GetWorkloadAccessTokenForJWT.
A solução adotada: o ALB completa o handshake usando o token com escopo para o Graph e injeta um JWT assinado pelo próprio ALB no header x-amzn-oidc-data com os claims do usuário, incluindo o sub. Esse JWT é validado usando as chaves de assinatura publicadas pela AWS, o sub é extraído e passado para a GetWorkloadAccessTokenForUserId.
Implementação: os principais trechos de código
O código completo está disponível no repositório GitHub. Abaixo estão os trechos mais relevantes.
Obtendo o token de acesso de workload
O servidor extrai o ID do usuário do claim sub do JWT e solicita um token de acesso de workload ao AgentCore Identity. Em seguida, usa esse token, o ID da sessão e a mensagem para invocar o agente em nome do usuário:
@router.post("/invocations")
async def invoke_agent(
request: InvocationRequest,
user_id: str = Depends(get_current_user),
settings: Settings = Depends(get_settings),
agent_service: AgentService = Depends(get_agent_service),
) -> StreamingResponse:
"""Invoke agent with streaming response."""
try:
agentcore = boto3.client("bedrock-agentcore", region_name=settings.identity_aws_region)
response = agentcore.get_workload_access_token_for_user_id(
workloadName=settings.workload_identity_name,
userId=user_id
)
workload_access_token = response["workloadAccessToken"]
return StreamingResponse(
content=agent_service.stream_response(
user_message=request.user_message,
session_id=request.session_id,
user_id=user_id,
workload_access_token=workload_access_token,
),
media_type="text/event-stream",
)
Solicitando o token de acesso OAuth
O servidor usa o decorator require_access_token do AgentCore SDK para recuperar o token OAuth 2.0 (veja como obter um token de acesso OAuth 2.0). A implementação modifica o decorator para aceitar o token de workload como parâmetro explícito, dando controle direto sobre o ciclo de vida do token enquanto preserva a lógica de recuperação e tratamento de erros do SDK:
def requires_access_token(
*,
provider_name: str,
scopes: list[str],
auth_flow: Literal["M2M", "USER_FEDERATION"],
workload_access_token: str | None = None,
session_binding_url: str | None = None,
on_auth_url: Callable[[str], Any] | None = None,
force_authentication: bool = False,
token_poller: TokenPoller | None = None,
custom_state: str | None = None,
custom_parameters: dict[str, str] | None = None,
into: str = "access_token",
region: str | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
client = IdentityClient(region)
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
try:
if not workload_access_token:
raise ValueError("workload_access_token is required")
token = await client.get_token(
provider_name=provider_name,
agent_identity_token=workload_access_token,
scopes=scopes,
auth_flow=auth_flow,
callback_url=session_binding_url,
on_auth_url=on_auth_url,
force_authentication=force_authentication,
token_poller=token_poller,
custom_state=custom_state,
custom_parameters=custom_parameters,
)
kwargs[into] = token
return await func(*args, **kwargs)
except Exception:
logger.exception("Error in requires_access_token decorator")
raise
return wrapper
return decorator
Três decisões de design importantes
A implementação das ferramentas do agente destaca três escolhas de design relevantes:
- Pydantic BaseModel como tipos de retorno: as classes
GitHubUser e GitHubProject são subclasses de BaseModel. O Strands deriva automaticamente descrições de ferramentas a partir do schema e das docstrings, dando ao modelo de linguagem contexto estruturado sobre cada ferramenta.
- Tratamento de erros consistente com o tipo: quando não há token e o AgentCore Identity retorna uma URL de autorização, o callback
on_auth_url lança um AuthorizationRequiredError em vez de retornar uma string — uma ferramenta que declara GitHubUser como tipo de retorno não pode retornar uma URL. A camada de streaming do agente captura a exceção e apresenta a URL ao usuário.
- Escopos por ferramenta: cada ferramenta declara apenas os escopos OAuth que precisa, mantendo o consentimento alinhado ao princípio do menor privilégio.
Completando o session binding
No Session Binding Service, quando o usuário autoriza o acesso no GitHub, o GitHub redireciona para {session_binding_url}?session_id={session_id}, onde session_id corresponde ao sessionURI que o AgentCore Identity incluiu na URL de autorização original:
@router.get("/session-binding", response_class=HTMLResponse)
async def oauth_session_binding(
session_id: str = Query(..., description="Session URI from AgentCore Identity"),
user_id: str = Depends(get_current_user),
settings: Settings = Depends(get_settings),
) -> HTMLResponse:
"""Handle OAuth2 session binding from external providers."""
client = boto3.client("bedrock-agentcore", region_name=settings.identity_region)
try:
client.complete_resource_token_auth(
sessionUri=session_id,
userIdentifier={"userId": user_id},
)
O serviço extrai o ID do usuário do claim sub no header x-amzn-oidc-data, garantindo identidade consistente ao longo de todo o fluxo. Em seguida, chama complete_resource_token_auth com o sessionURI e o ID do usuário, vinculando o token de acesso resultante à sessão correta.
Limpeza dos recursos
Para evitar cobranças desnecessárias, é recomendável excluir os recursos criados pela solução quando não forem mais necessários. As instruções estão disponíveis no guia de limpeza dos recursos.
Próximos passos
O padrão apresentado funciona em diferentes plataformas de computação — ECS, EKS, Lambda ou fora da AWS — e se estende além do GitHub para outros serviços compatíveis com OAuth 2.0, como Jira, Salesforce ou Google Calendar. Para aprofundar:
Fonte
Secure AI agents with Amazon Bedrock AgentCore Identity on Amazon ECS (https://aws.amazon.com/blogs/machine-learning/secure-ai-agents-with-amazon-bedrock-agentcore-identity-on-amazon-ecs/)