Skip to content

Commit 4251229

Browse files
author
Olivier Gintrand
committed
fix(oauth): handle missing expires_in in OAuth token response
Signed-off-by: Olivier Gintrand <olivier.gintrand@forterro.com>
1 parent a02a04b commit 4251229

File tree

2 files changed

+14
-5
lines changed

2 files changed

+14
-5
lines changed

mcpgateway/services/oauth_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,18 @@ async def complete_authorization_code_flow(self, gateway_id: str, code: str, sta
573573

574574
# Store tokens if storage service is available
575575
if self.token_storage:
576+
# Use provider's expires_in if present; None means the provider
577+
# does not specify token expiration (e.g. GitHub OAuth Apps).
578+
raw_expires = token_response.get("expires_in")
579+
expires_in = int(raw_expires) if raw_expires is not None else None
580+
576581
token_record = await self.token_storage.store_tokens(
577582
gateway_id=gateway_id,
578583
user_id=user_id,
579584
app_user_email=app_user_email, # User from state
580585
access_token=token_response["access_token"],
581586
refresh_token=token_response.get("refresh_token"),
582-
expires_in=token_response.get("expires_in", self.settings.oauth_default_timeout),
587+
expires_in=expires_in,
583588
scopes=token_response.get("scope", "").split(),
584589
)
585590

mcpgateway/services/token_storage_service.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def __init__(self, db: Session):
7474
logger.warning("OAuth encryption not available, using plain text storage")
7575
self.encryption = None
7676

77-
async def store_tokens(self, gateway_id: str, user_id: str, app_user_email: str, access_token: str, refresh_token: Optional[str], expires_in: int, scopes: List[str]) -> OAuthToken:
77+
async def store_tokens(self, gateway_id: str, user_id: str, app_user_email: str, access_token: str, refresh_token: Optional[str], expires_in: Optional[int], scopes: List[str]) -> OAuthToken:
7878
"""Store OAuth tokens for a gateway-user combination.
7979
8080
Args:
@@ -83,7 +83,7 @@ async def store_tokens(self, gateway_id: str, user_id: str, app_user_email: str,
8383
app_user_email: ContextForge user email (required)
8484
access_token: Access token from OAuth provider
8585
refresh_token: Refresh token from OAuth provider (optional)
86-
expires_in: Token expiration time in seconds
86+
expires_in: Token expiration time in seconds, or None if the provider does not specify expiration
8787
scopes: List of OAuth scopes granted
8888
8989
Returns:
@@ -102,8 +102,12 @@ async def store_tokens(self, gateway_id: str, user_id: str, app_user_email: str,
102102
if refresh_token:
103103
encrypted_refresh = await self.encryption.encrypt_secret_async(refresh_token)
104104

105-
# Calculate expiration
106-
expires_at = datetime.now(timezone.utc) + timedelta(seconds=int(expires_in))
105+
# Calculate expiration (None if provider does not specify expires_in)
106+
if expires_in is not None:
107+
expires_at = datetime.now(timezone.utc) + timedelta(seconds=int(expires_in))
108+
else:
109+
logger.info(f"No expires_in from OAuth provider for gateway {SecurityValidator.sanitize_log_message(gateway_id)} — token will not auto-expire")
110+
expires_at = None
107111
# Create or update token record - now scoped by app_user_email
108112
token_record = self.db.execute(select(OAuthToken).where(OAuthToken.gateway_id == gateway_id, OAuthToken.app_user_email == app_user_email)).scalar_one_or_none()
109113

0 commit comments

Comments
 (0)