Skip to content

Framework does not handle refreshed tokens correctly #1250

Open

Description

@pematth

Initial Checks

Description

I use python sdk with Keycloak as external authz provider. I allowed my mcp client (in my test scenario it is latest VSC) to get AT and RT (using offline_access scope). I have implemented a TokenVerifier that is doing validation of the MCP keycloak JWT. And then in some MCP tool, i use get_access_token() from the framework to use the JWT. I log both values and i can clearly see that it works fine initially, but after the token was refreshed on client-side, i still get the old accesst token from get_access_token() function. Not sure if you intended to support token refresh in a clean way. But IMHO that is a bug that should be fixed soon.

Example Code

Python & MCP Python SDK

1.12.4

Activity

  1. added
    bugSomething isn't working
    needs reproneeds additional information to be able to reproduce bug
    authIssues and PRs related to Authentication / OAuth
    on Oct 3, 2025
  2. felixweinberger commented on Oct 3, 2025

    @felixweinberger
    Contributor

    Hi @pematth could you provide a code snippet that demonstrates the specific issue you're getting that we can run to debug?

  3. bgaidioz commented on Oct 3, 2025

    @bgaidioz

    I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.

    I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.

    I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.

  4. felixweinberger commented on Oct 3, 2025

    @felixweinberger
    Contributor

    I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.

    I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.

    I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.

    That'd be great thank you!

  5. bgaidioz commented on Oct 6, 2025

    @bgaidioz

    I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
    I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
    I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.

    That'd be great thank you!

    I pushed code to https://github.com/raw-labs/python-sdk-issue-1250: a minimal OAuth implementation that issues 30-second tokens.

    When used with Claude Code:

    1. At connection time, there are multiple calls to refresh the access token. (I don't remember seeing that earlier but I wasn't paying attention maybe),
    2. When invoking the tool, even after refresh, the tool's code still receives an old (possibly expired) token from get_access_token.
  6. added
    P1Significant bug affecting many users, highly requested feature
    ready for workEnough information for someone to start working on
    and removed
    needs reproneeds additional information to be able to reproduce bug
    on Oct 6, 2025
  7. felixweinberger commented on Oct 6, 2025

    @felixweinberger
    Contributor

    I ran into a similar situation when I tried to implement token refresh on our MCP server (after adding load_refresh_token and exchange_refresh_token to our OAuthAuthorizationServerProvider). I could see the new token being passed to auth_context_var.reset(token) in auth_context.py, but get_access_token was still returning the old token when the client issued a new call.
    I’m mentioning this because it seems to confirm that the framework isn’t propagating the refreshed token consistently, which may be the root of what’s being reported here.
    I don’t have a minimal code snippet handy, but I’d be happy to try and put one together when I’m back working on this part of the code.

    That'd be great thank you!

    I pushed code to https://github.com/raw-labs/python-sdk-issue-1250: a minimal OAuth implementation that issues 30-second tokens.

    When used with Claude Code:

    1. At connection time, there are multiple calls to refresh the access token. (I don't remember seeing that earlier but I wasn't paying attention maybe),
    2. When invoking the tool, even after refresh, the tool's code still receives an old (possibly expired) token from get_access_token.

    Thanks for providing the repro!

  8. added
    P0Broken core functionality, security issues, critical missing feature
    and removed
    P1Significant bug affecting many users, highly requested feature
    on Oct 6, 2025
  9. pematth commented on Oct 7, 2025

    @pematth
    Author

    Hi, sorry for not responding, i was on holiday. I guess you are fine with the code provided by Benjamin. Otherwise please let me know.

  10. lucheng-hugh commented on Oct 28, 2025

    @lucheng-hugh

    Hi, do we have any updates on this bug? We also face the same bug where get_access_token() returns the old token. Weirdly, the TokenVerifier will receive the new token, but it won't be accessible during processing the MCP request unless the session is refreshed.

    I can confirm the bug still exists in 1.16.0

  11. 4 remaining items

  12. bgaidioz commented on Dec 3, 2025

    @bgaidioz

    This GitHub issue was referenced in FastMCP because it describes the same problem (PrefectHQ/fastmcp#1863). It’s also related to the implementation that uses a context variable. The fix is here: PrefectHQ/fastmcp#2505

  13. bgaidioz commented on Feb 5, 2026

    @bgaidioz

    Hello.

    Here's a patch that I believe fixes the issue.

    I added a regression test that fails as one would expect without the patch (the old token is seen on the server side). CI failed on coverage for the new branch in _handle_request. I did some efforts for that, added a small, low‑level unit test to cover it, but it’s fairly close to internals and I’m not sure this is the preferred approach.

    This isn't a direct port of the patch to fastmcp. Setting the context var felt reasonable to me.

    That fixes the demo repro and our own MCP server when running against keycloak.

    I hope this helps.

  14. grahonan commented on Feb 12, 2026

    @grahonan

    I also ran into this and would love to see it fixed soon.

  15. added
    P1Significant bug affecting many users, highly requested feature
    and removed
    P0Broken core functionality, security issues, critical missing feature
    on Feb 19, 2026
  16. maxisbey commented on Feb 19, 2026

    @maxisbey
    Contributor

    This may be addressed as part of the broader auth rework in #1743. Keeping as a standalone issue for now in case it can be fixed independently before the rework lands.

  17. Ptraaah commented on Oct 31, 2026

    @Ptraaah

    I dug into this and can confirm the bug on the v1 line. Small repro against a Streamable HTTP server with a plain token verifier: initialize and call a tool presenting token A, then present token B in the same session (same client, like after a refresh) - get_access_token() inside the tool still returns token A.

    One thing worth flagging: on current main (v2) the same scenario already returns the refreshed token, because the new dispatcher propagates each message's sender context into the handler. So this is live on v1.x, which matches the reports here (1.16, 1.19).

    Where I would fix it on v1.x: AuthContextMiddleware sets auth_context_var per HTTP request, but handlers run in the session's long-lived task, so they keep seeing the context of whichever request opened the session. _handle_request in the lowlevel server already re-applies the per-request request_ctx from the message metadata for that exact reason, and the HTTP transports attach the current request to that metadata - its scope carries this request's authenticated user. So I would apply auth_context_var from that scope for the duration of the handler and reset it afterwards, same pattern, no public API change. Both HTTP transports attach the request, so both get it; stdio has no request attached and stays untouched. And since the value is applied per request at dispatch, parallel sessions cannot see each other's tokens.

    I know #1998 took a similar route a while back (thanks @bgaidioz, the repro repo helped pin this down) but it has been sitting in draft, so I will put up a fresh PR against v1.x with regression tests, unless you would rather fold this into the #1743 rework instead.

  18. Ptraaah commented on Oct 31, 2026

    @Ptraaah

    Quick follow-up on this: I opened PR #3794 with the fix, but it was closed automatically within a couple of minutes because I am not assigned to this issue. The change itself is ready and tested locally (new regression tests fail without the fix and pass with it, details are in the PR). Could a maintainer assign me to this issue so I can reopen it? Happy to adjust the approach first if you would rather see it done differently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Significant bug affecting many users, highly requested featureauthIssues and PRs related to Authentication / OAuthbugSomething isn't workingready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions