Skip to content

Conversation

@Skeptomenos
Copy link

Summary

OAuth authentication was failing with "Invalid or expired OAuth state parameter" when the MCP server restarted during an OAuth flow. This happened because OAuth states were stored only in memory (self._oauth_states dict) and lost on restart.

This is a common issue in stdio MCP mode where the server process may restart between when the user clicks the auth link and when they complete the OAuth flow in the browser.

Changes

  • Add file-based persistence for OAuth states to ~/.google_workspace_mcp/oauth_states.json
  • Load persisted states on OAuth21SessionStore initialization
  • Clean up expired states on load
  • States now survive server restarts

Testing

Added 8 test cases in tests/test_oauth_state_persistence.py:

  • test_get_oauth_states_file_path_with_env_var
  • test_store_oauth_state_persists_to_disk
  • test_load_oauth_states_from_disk
  • test_expired_states_cleaned_on_load
  • test_validate_and_consume_persists_deletion
  • test_state_survives_store_recreation (key test - simulates server restart)
  • test_handles_missing_file_gracefully
  • test_handles_corrupted_file_gracefully

All tests pass:

============================= 8 passed in 2.43s ===============================

Backward Compatibility

  • If the state file doesn't exist, the store works exactly as before (in-memory only)
  • Corrupted files are handled gracefully (logged warning, continues with empty state)
  • No changes to the public API

OAuth authentication was failing with 'Invalid or expired OAuth state parameter'
when the MCP server restarted during an OAuth flow. This happened because OAuth
states were stored only in memory and lost on restart.

Changes:
- Add file-based persistence for OAuth states to ~/.google_workspace_mcp/oauth_states.json
- Load persisted states on OAuth21SessionStore initialization
- Clean up expired states on load
- States now survive server restarts

Fixes the common issue where users complete OAuth in browser but get an error
because the server process was restarted (common in stdio MCP mode).
- OAuth callback server now starts on-demand, not at MCP startup
- Uses random port from range 9876-9899 to avoid conflicts
- Server auto-shuts down 2 seconds after successful auth
- Eliminates port conflicts when running multiple opencode sessions
Follow XDG Base Directory Specification by storing credentials in
~/.config/google-workspace-mcp/ instead of ~/.google_workspace_mcp/.

Also set default OAuth port to 9876 for consistency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant