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

  1. Do not request or rely on refresh tokens against this sample — re-run the full authorization-code flow when the access token expires.
  2. Configure the client not to send grant_type=refresh_token to this provider.
  3. If you need refresh support, subclass SimpleAuthProvider and implement load_refresh_token/exchange_refresh_token with a persistent store.
  4. 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

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


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/7e5e4aa93092fb4b. Report an issue: GitHub.