Skip to content

[BUG]: OAuth token exchange fails when provider omits expires_in from token response #3989

@ecthelion77

Description

@ecthelion77

🐞 Bug Summary

When an OAuth2 provider returns a token response without the expires_in field, the gateway crashes with a TypeError during token storage. The expires_in parameter is RECOMMENDED (not REQUIRED) per RFC 6749 Section 5.1, so providers may legitimately omit it.


🧩 Affected Component

  • Federation or Transports
  • mcpgateway - API

🔁 Steps to Reproduce

  1. Configure a gateway with OAuth2 pointing to a provider that does not return expires_in in the token response (e.g., some Azure AD configurations, custom OAuth servers)
  2. Attempt to connect to the gateway — OAuth flow triggers token exchange
  3. Token response is received successfully but lacks expires_in
  4. Gateway crashes when trying to compute expires_at = now + expires_in

🤔 Expected Behavior

The gateway should handle missing expires_in gracefully:

  • Extract expires_in from the token response, defaulting to None when absent
  • Store the token without an expires_at timestamp when no expiry is provided
  • The token should remain usable until explicitly revoked or refreshed

📓 Logs / Error Output

TypeError: unsupported operand type(s) for +: 'datetime' and 'NoneType'

Occurs in token_storage_service.py when expires_in is None and the code tries to compute datetime.now() + timedelta(seconds=expires_in).


🧠 Environment Info

Key Value
Version or commit 1.0.0-RC2
Runtime Python 3.12, Gunicorn
Platform / OS Linux (Kubernetes)
Container Docker

🧩 Additional Context

RFC 6749 Section 5.1 states:

expires_in — RECOMMENDED. The lifetime in seconds of the access token.

Since it is RECOMMENDED and not REQUIRED, the gateway must handle its absence gracefully. The fix involves two changes:

  1. oauth_manager.py: Extract expires_in with .get() and pass None when absent
  2. token_storage_service.py: Accept Optional[int] for expires_in and skip expires_at computation when None

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions