-
Notifications
You must be signed in to change notification settings - Fork 571
Tokens can be cached beyond the lifetime of the (http) transport. #834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
halter73
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add tests for supplying a custom ITokenCache that verifies it gets used?
@localden @DavidParks8 Do you have any thoughts on this?
I added tests that verify a custom token cache is used to look up cached tokens and to store new ones. Since I wanted to test the public API, I had to use What do you think? |
…oken, until we get the TokenCache (modelcontextprotocol/csharp-sdk#834).
|
Until this is approved and merged, I use reflection magic to extract and inject the oauth token like this: var httpClientTransport = new HttpClientTransport(...)
var token = File.Exists("token.json") ? File.ReadAllText("token.json") : null;
httpClientTransport.InjectOAuthToken(token);
await using var mcpClient = await McpClient.CreateAsync(httpClientTransport);
File.WriteAllText("token.json", httpClientTransport.ExtractOAuthToken()); |
src/ModelContextProtocol.Core/Authentication/TokenContainerCacheable.cs
Outdated
Show resolved
Hide resolved
src/ModelContextProtocol.Core/Authentication/TokenContainerConvert.cs
Outdated
Show resolved
Hide resolved
tests/ModelContextProtocol.Tests/Client/CustomTokenCacheTests.cs
Outdated
Show resolved
Hide resolved
|
@halllo I updated this PR to do what I suggested in this comment and add an OAuthTest base. This should make it easier to add tests for the upcoming OAuth SEPs tracked by 2025-11-25 Implementation (view). @stephentoub @eiriktsarpalis @localden It'd be nice if one of you could sign off on this. @halllo Did most of the work, and it looks good to me, but I made some big changes to the tests and updated the refresh token flow slightly, so it works even when we don't know the expiration which aligns more with the TypeScript SDK. This is similar to |
| /// <summary> | ||
| /// A generic implementation of an OAuth authorization provider for MCP. This does not do any advanced token | ||
| /// protection or caching - it acquires a token and server metadata and holds it in memory. | ||
| /// This is suitable for demonstration and development purposes. | ||
| /// </summary> | ||
| internal sealed partial class ClientOAuthProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe these XML docs should be updated, now that tokens aren't necessarily stored in memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just
A generic implementation of an OAuth authorization provider.
?
| // This provider only supports Bearer scheme | ||
| if (!string.Equals(scheme, BearerScheme, StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| throw new InvalidOperationException("This credential provider only supports the Bearer scheme"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Not related directly to this PR, but can't this use the ThrowIfNotBearerScheme() helper?
I added the possibility to plugin a token cache, so that the client can be made to reuse its access token beyond the lifetime of the transport. By default tokens are only cached for the lifetime of the transport.
Fixes #749
Fixes #597
Motivation and Context
When the client acquires an access token and a refresh token, it should be able to utilze them to make authenticated requests to the server even after it was restarted. The client should not need to go through the OAuth dance again and again every time it is initialised (unless the access token is no longer valid and no refresh token was acquired or the refresh token was revoked).
How Has This Been Tested?
I tested it via a proof of concept application without a token cache (using the default
InMemoryTokenCache) and with a custom implementation of a file-based token cache.Breaking Changes
The introduction of the
ITokenCacheis backwards compatible. If no custom token cache is provided, it falls back to anInMemoryTokenCachewith the same lifetime as the (oauth enabled http) transport (like before).Types of changes
Checklist
Additional context
TokenContainerpublic, so that it can be passed into custom token caches.ObtainedAtproperty serialisable, because it needs to remember when it was obtained after serialization to calculate the expiration.These two changes seemed more reasonable than introducing another token type for cache serialization only. I didn't find usages that could be disrupted by this.