microsoft/semantic-kernel · error · NotImplementedError
Refresh tokens not supported
Error message
Refresh tokens not supported
What it means
The MCP OAuth sample explicitly does not implement refresh-token rotation: load_refresh_token returns None and exchange_refresh_token raises NotImplementedError. Any client that obtains a refresh_token and POSTs to the token endpoint with grant_type=refresh_token hits this. It signals a capability gap of the demo, not a recoverable runtime fault.
Source
Thrown at python/samples/demos/mcp_with_oauth/server/mcp_simple_auth/simple_auth_provider.py:266
# Check if expired
if access_token.expires_at and access_token.expires_at < time.time():
del self.tokens[token]
return None
return access_token
async def load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> RefreshToken | None:
"""Load a refresh token - not supported in this example."""
return None
async def exchange_refresh_token(
self,
client: OAuthClientInformationFull,
refresh_token: RefreshToken,
scopes: list[str],
) -> OAuthToken:
"""Exchange refresh token - not supported in this example."""
raise NotImplementedError("Refresh tokens not supported")
async def revoke_token(self, token: str, token_type_hint: str | None = None) -> None:
"""Revoke a token."""
if token in self.tokens:
del self.tokens[token]
View on GitHub (pinned to c028a0c7dc)
Solutions
- Do not request or rely on refresh tokens against this sample — re-run the full authorization-code flow when the access token expires.
- Configure the client not to send grant_type=refresh_token to this provider.
- If you need refresh support, subclass SimpleAuthProvider and implement load_refresh_token/exchange_refresh_token with a persistent store.
- Use a production-grade OAuth provider instead of this demo for workloads needing refresh.
Example fix
# Disable refresh in the client, or subclass to implement it:
class MyProvider(SimpleAuthProvider):
async def load_refresh_token(self, client, refresh_token):
return self.refresh_tokens.get(refresh_token)
async def exchange_refresh_token(self, client, refresh_token, scopes):
# issue new access token, rotate refresh token
...
Defensive patterns
Strategy: try-catch
Validate before calling
# Detect refresh-token support before calling exchange_refresh_token.
rt = await provider.load_refresh_token(client, refresh_token)
if rt is None:
# refresh not supported -> restart the auth-code flow
raise RuntimeError('refresh not supported; re-authorize') Try / catch
try:
token = await provider.exchange_refresh_token(client, refresh_token, scopes)
except NotImplementedError:
# re-run the full authorization-code flow to get a new access token
... Prevention
- Do not request refresh_token scope against this sample provider.
- Disable auto-refresh in your OAuth client when targeting the demo.
- Re-authorize when the 3600s access token expires.
- If you need refresh, switch to a production provider or subclass and implement it.
When it happens
Trigger: A token request with grant_type=refresh_token reaching exchange_refresh_token; an MCP client configured to auto-refresh expired access tokens against this sample provider.
Common situations: Using a generic OAuth client library that always attempts refresh when an access token expires (3600s expiry); pointing an MCP client expecting refresh support at this demo provider.
Related errors
- Invalid state parameter
- Invalid credentials
- Invalid authorization code
- OAuth error: {self.callback_data['error']}
- Timeout waiting for OAuth callback
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/7e5e4aa93092fb4b.
Report an issue: GitHub.