Conversation
Complete OAuth flow synchronously within redirectToAuthorization() instead
of caching auth codes for SDK's separate token exchange. This addresses the
MCP SDK's design where auth() returns 'REDIRECT' immediately after the call
without re-checking for tokens.
Key changes:
- Exchange auth code for tokens inside redirectToAuthorization()
- Add state parameter validation (CSRF protection)
- Remove clear() from TokenStore, add explicit delete methods to OAuthStore
- Use symbol brand for type-safe OAuthStore detection
- Fix invalidateCredentials('all') to only clear own storeKey, not entire store
- Add authServerUrl option for separate auth/token endpoints
- Atomic file writes via temp file + rename
See ADR-001 through ADR-005 for detailed rationale.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The MCP SDK's
auth()flow creates a timing issue for CLI/desktop apps:provider.tokens()— if valid, returns'AUTHORIZED'redirectToAuthorization(url)'REDIRECT'(without re-checking tokens)For web OAuth, this works: the redirect happens and the SDK never sees a synchronous return. For CLI apps using
browserAuth(), we capture the callback in-process, but the SDK has already decided to return'REDIRECT', causingUnauthorizedError.Solution
Exchange tokens inside
redirectToAuthorization()and save them to storage. While the firstconnect()still throwsUnauthorizedError(SDK constraint), subsequent calls find valid tokens:Changes
OAuth Flow
redirectToAuthorization()refresh_tokengrant type (SDK handles re-auth, see ADR-001)authServerUrloption for separate auth/token endpoint originsStorage API (breaking)
clear()fromTokenStoreinterfacedeleteClient()anddeleteCodeVerifier()toOAuthStoreOAuthStoredetection vs duck typingBug Fixes
invalidateCredentials('all')was clearing the entire store; now only clears the provider's ownstoreKeyexpiresAtAuth Flow Diagram
sequenceDiagram participant App participant SDK as MCP SDK participant Provider as browserAuth participant Browser participant AuthServer as Auth Server App->>SDK: client.connect(transport) SDK->>Provider: tokens() Provider-->>SDK: undefined (no tokens) SDK->>Provider: redirectToAuthorization(url) Provider->>Browser: Open authorization URL Browser->>AuthServer: User authenticates AuthServer->>Provider: Callback with code Note over Provider: Validate state (CSRF) Provider->>AuthServer: Exchange code for tokens AuthServer-->>Provider: Access token Provider->>Provider: saveTokens() Provider-->>SDK: void SDK-->>App: throws UnauthorizedError Note over App: Tokens now in storage App->>SDK: client.connect(newTransport) SDK->>Provider: tokens() Provider-->>SDK: {access_token: "..."} SDK-->>App: Connected ✓ADRs
This PR includes architectural decision records documenting the rationale:
redirectToAuthorization()https://www.linkedin.com/posts/koistya_opensource-typescript-oauth-activity-7421227824482734080-9Tyb