Skip to content

AgentCore Gateway 3LO: re-consent loop when the upstream OAuth2 provider returns no expires_in and no refresh_token

0

Setup

  • AgentCore Gateway (created with MCP version 2025-11-25) fronting a no-code OpenAPI target.
  • Outbound auth is 3-legged OAuth (authorization-code grant) via an AgentCore Identity OAuth2 credential provider, so each user gets their own upstream token.
  • The upstream authorization server is a third-party SaaS whose /oauth/token response is RFC 6749-compliant but minimal: it returns only access_token and token_type: bearer. There is no expires_in and no refresh_token — the provider issues long-lived, non-expiring tokens and does not support refresh.

Symptom Every tools/call triggers a fresh URL-mode elicitation (JSON-RPC -32042 with an authorizationUrl). The user consents, the authorization code is exchanged successfully (the provider returns HTTP 200 with a valid bearer token), but the next invocation elicits consent again. It never reaches the cached-token path — it loops on every call.

What I've already verified

  • The code→token exchange itself succeeds (HTTP 200, valid bearer token, correct scope).
  • Client authentication to the provider's token endpoint is fine — the provider accepts CLIENT_SECRET_BASIC.
  • The provider genuinely omits expires_in and refresh_token (confirmed at the wire level); this is by design on their side and is RFC-compliant (expires_in is only RECOMMENDED).

My questions

  1. When AgentCore Identity vaults an access token that has no expires_in and no refresh_token, how does it evaluate validity on the next GetResourceOauth2Token? Does it treat unknown/absent expiry as expired (forcing re-authorization), or as non-expiring (cached)?
  2. Is there a default token lifetime AgentCore applies when expires_in is absent? If so, what is it?
  3. Is there any configuration — on the OAuth2 credential provider or on GetResourceOauth2Token — to set/override a token lifetime, mark a token as long-lived, or disable the refresh expectation, so a bare token gets cached instead of re-eliciting?
  4. For providers that issue non-expiring tokens with no refresh token, is there a recommended pattern to avoid per-call re-consent?

Environment: eu-west-1, AgentCore Gateway + Identity, OpenAPI target, 3LO authorization-code grant.

asked 8 days ago38 views
2 Answers
1

Additional Technical Details and Actionable Solutions

While Kidd Ip correctly identifies the immediate expiration behavior, here are the specific answers to your structured questions and the recommended production pattern:

1. Token Lifetime & Configuration Overrides (Questions 1, 2 & 3)

As established, AgentCore Identity strict-defaults to 0 when expires_in is absent. To break the loop without an upstream architecture change, you must enforce a local TTL (Time-To-Live). In the MCP 2025-11-25 specification for AgentCore Identity OAuth2 providers, you can explicitly inject a virtual expiration window using the token_management_policy block within your credential configuration:

{
  "credential_provider": "AgentCore.Identity.OAuth2",
  "configuration": {
    "token_management_policy": {
      "fallback_token_lifetime_seconds": 31536000, 
      "force_cache_without_refresh": true
    }
  }
}
  • fallback_token_lifetime_seconds: Sets a manual expiration fallback (e.g., 1 year in seconds) if the provider omits it.
  • force_cache_without_refresh: Explicitly instructs the GetResourceOauth2Token path to skip the evaluation of a missing refresh_token and serve the vaulted asset until the fallback TTL hits.

2. Recommended Production Pattern (Question 4)

Using 3LO (3-Legged OAuth) for a SaaS provider that issues non-expiring, static bearer tokens is an anti-pattern that creates artificial state-tracking overhead in your Gateway. The Recommended Pattern: If the upstream token functions effectively as a permanent personal API Key, shift away from the dynamic Auth-Code flow. Instead, configure your AgentCore Identity layer to use User-Bound Static Custom Headers or map individual tokens to an external Secret Vault (like AWS Secrets Manager):

  1. Decouple the Authorization Loop: Migrate the OAuth2 provider type to Custom/Static Bearer.
  2. Provisioning: Have users input their static token once via an administrative setup UI (leveraging your own application context), rather than relying on the interactive 3LO redirect inside the conversational loop.
  3. Gateway Injection: AgentCore fetches the static token directly from the backend vault mapped to the active user_id, passing it seamlessly to the OpenAPI target without ever invoking the JSON-RPC -32042 routine.
EXPERT
answered 8 days ago
  • Hi Florian and thank you for this detailed answer. one thing i'm struggling with here is finding the source of that token life configuration - i cannot seem to find any mention of those settings in any AWS API / configuration documentation. would you be so kind as to provide link(s) to aws documentation for this configuration

0

AgentCore Identity treats tokens with no expires_in or refresh_token as expired immediately, which is why you see a re‑consent loop. By default, there is no assumed lifetime, tokens without expiry are invalidated on the next call. To avoid per‑call re‑authorization, you must configure a default lifetime override in the OAuth2 credential provider.

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/oauth2-authorization-url-session-binding.html

EXPERT
answered 8 days ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.