Troca de token On-Behalf-Of com ServiceNow via Bedrock AgentCore Gateway

O problema: agentes de IA que perdem a identidade do usuário

Quando um agente de IA chama APIs corporativas em nome de um usuário, existe um risco silencioso: a identidade de quem iniciou a ação pode se perder no caminho. Sem propagação de identidade, as chamadas chegam aos serviços downstream como se fossem da própria aplicação — e não do usuário real. Isso quebra trilhas de auditoria, contorna controles de acesso por usuário e viola o princípio do menor privilégio.

Para resolver esse problema em ambientes que combinam Amazon Bedrock, Microsoft Entra ID e ServiceNow, a AWS publicou um guia técnico detalhado mostrando como configurar o AgentCore Gateway para executar nativamente a troca de token On-Behalf-Of (OBO), sem nenhum código adicional.

Visão geral da solução

A arquitetura proposta conecta três componentes principais através do AgentCore Gateway:

  • Uma aplicação de agente de IA, protegida pelo Microsoft Entra ID via registro de aplicação OAuth.
  • Um servidor MCP (Protocolo de Contexto de Modelo), implementado pelo próprio AgentCore Gateway, posicionado entre o agente e o ServiceNow.
  • A API REST do ServiceNow, configurada com uma integração OAuth de entrada vinculada ao Microsoft Entra ID.
Imagem original — fonte: Aws

O fluxo funciona assim: o usuário obtém um Token Web JSON (JWT) para invocar o agente (passo 1). O agente se autentica no gateway com um JWT com escopo para o próprio registro de aplicação do agente (passo 2). O gateway então troca esse token, usando o fluxo OBO, por um novo token com escopo para o conector da API do ServiceNow, preservando a identidade do usuário original (passo 3). O ServiceNow recebe um token válido cujo claim preferred_username mapeia para um usuário existente, aplicando as permissões daquele usuário à chamada da API (passo 4).

Como funciona a troca OBO nesse contexto

Quando o agente chama o gateway, ele apresenta um JWT com um claim de audiência (audience) que corresponde ao client ID do agente (por exemplo, 3ff88e17-XXXX-XXXX-XXXX-XXXXXXXXXXXX). O ServiceNow, porém, espera um token com audiência correspondente ao client ID do seu próprio conector de API (5d4a7a11-XXXX-XXXX-XXXX-XXXXXXXXXXXX). O fluxo OBO faz essa ponte.

O AgentCore Gateway envia o token recebido ao endpoint de token do Entra junto com as credenciais do aplicativo agente, o escopo alvo e o parâmetro requested_token_use: on_behalf_of. O Entra valida que o usuário autorizou a delegação e emite um novo token para a API do ServiceNow com os claims do usuário original preservados.

Pré-requisitos

Para seguir o guia, são necessários:

  • Uma conta AWS com acesso ao AgentCore Gateway.
  • Um tenant do Microsoft Entra ID com permissões para criar registros de aplicação. A conta precisa ter, no mínimo, a função de Application Developer.
  • Uma instância do ServiceNow com acesso administrativo. É possível criar uma instância de desenvolvedor pessoal (PDI) gratuitamente em https://developer.servicenow.com/.
  • Dois registros de aplicação no Entra: um representando o agente de IA e outro representando o conector da API do ServiceNow.

Configurando o Microsoft Entra ID

O primeiro passo é criar um registro de aplicação para representar o conector da API do ServiceNow. Após criar, anote o Application (client) ID na página de visão geral. Em seguida, no menu de navegação, expanda Manage, selecione Expose an API e adicione um escopo chamado user_impersonation usando o URI de ID de aplicação padrão. Esse escopo é o que o fluxo OBO solicita ao fazer a troca de tokens.

Depois, crie um registro de aplicação para o agente de IA. Anote o Application (client) ID do ServiceNowAgent e crie um client secret nesse registro — o gateway precisará dessas credenciais para realizar a troca OBO. Por fim, configure a aplicação do agente para ter permissões de API que permitam chamar a aplicação do conector do ServiceNow. Não são necessárias mudanças além do que se configuraria para qualquer integração OBO padrão entre duas aplicações no Entra.

Configurando o ServiceNow

No console administrativo do ServiceNow, navegue até System OAuth > Inbound Integrations e crie uma nova integração de entrada do tipo Third party ID token issued by OIDC supporting identity provider. Informe o client ID do registro de aplicação do conector da API do ServiceNow criado no Entra.

Na mesma tela, crie uma nova configuração de provedor OpenID Connect (OIDC) e insira a URL de metadados OIDC do seu tenant Entra. Mapeie o claim do usuário para o campo correspondente — por exemplo, se o tenant Entra coloca o e-mail do usuário no claim preferred_username, mapeie-o para o campo de e-mail no ServiceNow. Desative a verificação JTI caso seu tenant Entra não a inclua nos tokens. O Entra usa um claim interno chamado uti que é equivalente ao JTI — considere mapeá-lo para um claim jti para proteção adicional contra replay de tokens.

Ainda na mesma tela, crie um auth scope para a integração (por exemplo, APIConnector) e limite-o à Table API, ou às APIs do ServiceNow que o agente precisará acessar. Após essa configuração, o ServiceNow passa a aceitar tokens de autorização com audiência correspondente ao client ID do conector e resolve o usuário chamante pelo claim preferred_username.

Configurando o AgentCore Gateway

Passo 1: Criar uma configuração de autenticação de saída

No AgentCore, crie uma nova configuração de autenticação de saída (Build > Identity > Add Outbound Auth > Add OAuth client) e selecione Customized provider. Informe o client ID e o client secret do registro de aplicação do agente, junto com a URL de discovery do Entra. Em seguida, expanda Additional configurations e selecione JWT authorization grant. Isso instrui o gateway a usar um tipo de concessão baseado em JWT para a troca de tokens.

Passo 2: Criar o gateway com a versão MCP 2025-11-25

Crie um novo AgentCore Gateway (Build > Gateways > Create gateway). Ao definir os detalhes do gateway, expanda Additional configurations — optional e selecione a versão MCP 2025-11-25. Essa versão é obrigatória para o suporte nativo ao OBO — gateways criados com versões anteriores do MCP não suportam essa funcionalidade e a seleção não pode ser alterada após a criação.

Configure a identidade de entrada usando o registro de aplicação do agente (selecione Use existing identity provider configurations). Em JWT Authorization Configuration, adicione o Application ID do ServiceNowAgent documentado anteriormente.

Passo 3: Adicionar um target com troca de token

Adicione um target ao gateway apontando para o endpoint REST do ServiceNow e defina o schema inline. Para expor uma ferramenta chamada ListIncidents que chama a Table API do ServiceNow, utilize a seguinte especificação OpenAPI (substitua a URL do servidor pela da sua instância PDI):

{
  "openapi": "3.0.0",
  "info": {
    "title": "ServiceNow Incident API (Minimal)",
    "description": "A subset of the ServiceNow Table API for listing incident records.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://devxxx.service-now.com/api/now/table",
      "variables": {
        "instance": {
          "default": "devxxx",
          "description": "Your ServiceNow instance name."
        }
      }
    }
  ],
  "paths": {
    "/incident": {
      "get": {
        "operationId": "getIncidents",
        "summary": "List incidents",
        "description": "Retrieves a list of incident records from the ServiceNow instance.",
        "parameters": [
          {
            "name": "sysparm_limit",
            "in": "query",
            "description": "Maximum number of records to return.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "sysparm_query",
            "in": "query",
            "description": "An encoded query string to filter results (e.g., active=true).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sysparm_fields",
            "in": "query",
            "description": "A comma-separated list of fields to return.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A successful response containing an array of incidents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Incident"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Invalid credentials."
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {
      "Incident": {
        "type": "object",
        "properties": {
          "sys_id": {
            "type": "string",
            "description": "Unique identifier for the incident."
          },
          "number": {
            "type": "string",
            "description": "Incident ID (e.g., INC0000001)."
          },
          "short_description": {
            "type": "string",
            "description": "A brief summary of the incident."
          },
          "severity": {
            "type": "string",
            "description": "The severity level of the incident."
          },
          "state": {
            "type": "string",
            "description": "The current state of the incident (e.g., New, In Progress, Closed)."
          },
          "sys_created_on": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp when the incident was created."
          }
        }
      }
    }
  }
}

Na configuração de autenticação de saída do target, selecione OAuth como provedor de identidade e escolha a configuração criada no Passo 1. Expanda Additional configurations, selecione Token exchange grant e configure os seguintes valores:

  • Scope: api://5d4a7a11-xxxx-xxxx-xxxx-xxxxxxxxxxxx/user_impersonation — este é o escopo exposto no registro de aplicação do conector da API do ServiceNow no Entra.
  • Custom parameter (par chave-valor): requested_token_use:on_behalf_of — este parâmetro é crítico. Sem ele, o Entra não executa a troca OBO e a chamada ao ServiceNow falha com erro de autenticação.

Passo 4: Salvar e testar

Após salvar a configuração do target, o AgentCore Gateway passa a trocar automaticamente os tokens de entrada por tokens válidos do ServiceNow, preservando a identidade do usuário — sem nenhum código customizado. O fluxo completo acontece de forma transparente a cada requisição:

  • O gateway recebe o JWT do agente (audiência: client ID do agente).
  • O gateway envia o token ao endpoint de token do Entra com o tipo de concessão OBO.
  • O Entra emite um novo token (audiência: client ID do conector do ServiceNow) com os claims do usuário original.
  • O gateway encaminha a requisição ao ServiceNow com o token trocado.
  • O ServiceNow resolve o usuário a partir do token e aplica suas permissões.

O trecho de código abaixo mostra um agente de chat mínimo que interage com o ServiceNow através do servidor MCP:

import sys
import strands
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from mcp.client.streamable_http import streamablehttp_client

token = sys.argv[1] if len(sys.argv) > 1 else sys.exit("Usage: python snow_agent.py <TOKEN>")

MCP_URL = "<GATEWAY_URL>"

mcp_client = MCPClient(lambda: streamablehttp_client(url=MCP_URL, headers={"Authorization": f"Bearer {token}"}))

agent = strands.Agent(
    model=BedrockModel(model_id="global.anthropic.claude-sonnet-4-6", streaming=False),
    system_prompt=(
        "You have a tool called 'target-snow___getIncidents'. "
        "When asked about incidents, call it immediately with no parameters. "
        "Never ask the user for credentials or instance details --- the tool is pre-configured."
    ),
    tools=[mcp_client],
)

while (user_input := input("\nYou: ")).lower() not in ("quit", "exit", "q"):
    print(f"\nAgent: {agent(user_input)}")

Resolução de problemas

Se forem recebidos erros de autenticação do ServiceNow, verifique se:

  • O claim preferred_username no token trocado corresponde a um e-mail de usuário válido no ServiceNow.
  • O auth scope na integração de entrada do ServiceNow permite acesso aos endpoints de API que o agente está chamando.
  • A aplicação do agente possui permissões de API para a aplicação do conector do ServiceNow no Entra.

Limpeza dos recursos

Para evitar cobranças recorrentes, remova os seguintes recursos criados para testes: o AgentCore Gateway e seus targets; a configuração de autenticação de saída; os registros de aplicação no Entra (se criados especificamente para este guia); e a integração de entrada e a configuração do provedor OIDC no ServiceNow.

Conclusão

O suporte nativo ao OBO no AgentCore Gateway elimina a necessidade de interceptores customizados no AWS Lambda para realizar trocas de token. A troca é configurada diretamente no target do gateway, e o gateway a executa de forma transparente em cada requisição. A abordagem mantém a identidade do usuário ao longo de toda a cadeia agente-API, suporta os controles de acesso por usuário do ServiceNow e elimina código customizado que precisaria ser mantido separadamente.

Para começar com o AgentCore Gateway, consulte a documentação do Amazon Bedrock AgentCore. Para saber mais sobre o fluxo OBO do Microsoft Entra, acesse a documentação da plataforma de identidade Microsoft.

Fonte

ServiceNow on-behalf-of token exchange with Bedrock AgentCore Gateway (https://aws.amazon.com/blogs/machine-learning/servicenow-on-behalf-of-token-exchange-with-bedrock-agentcore-gateway/)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *